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');
407 * Hide a component - adds 'hidden' class
411 if(!this.getVisibilityEl()){
415 this.getVisibilityEl().addClass('hidden');
428 * @class Roo.bootstrap.Body
429 * @extends Roo.bootstrap.Component
430 * Bootstrap Body class
434 * @param {Object} config The config object
437 Roo.bootstrap.Body = function(config){
439 config = config || {};
441 Roo.bootstrap.Body.superclass.constructor.call(this, config);
442 this.el = Roo.get(config.el ? config.el : document.body );
443 if (this.cls && this.cls.length) {
444 Roo.get(document.body).addClass(this.cls);
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
450 is_body : true,// just to make sure it's constructed?
455 onRender : function(ct, position)
457 /* Roo.log("Roo.bootstrap.Body - onRender");
458 if (this.cls && this.cls.length) {
459 Roo.get(document.body).addClass(this.cls);
478 * @class Roo.bootstrap.ButtonGroup
479 * @extends Roo.bootstrap.Component
480 * Bootstrap ButtonGroup class
481 * @cfg {String} size lg | sm | xs (default empty normal)
482 * @cfg {String} align vertical | justified (default none)
483 * @cfg {String} direction up | down (default down)
484 * @cfg {Boolean} toolbar false | true
485 * @cfg {Boolean} btn true | false
490 * @param {Object} config The config object
493 Roo.bootstrap.ButtonGroup = function(config){
494 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
505 getAutoCreate : function(){
511 cfg.html = this.html || cfg.html;
522 if (['vertical','justified'].indexOf(this.align)!==-1) {
523 cfg.cls = 'btn-group-' + this.align;
525 if (this.align == 'justified') {
526 console.log(this.items);
530 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531 cfg.cls += ' btn-group-' + this.size;
534 if (this.direction == 'up') {
535 cfg.cls += ' dropup' ;
551 * @class Roo.bootstrap.Button
552 * @extends Roo.bootstrap.Component
553 * Bootstrap Button class
554 * @cfg {String} html The button content
555 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
556 * @cfg {String} size ( lg | sm | xs)
557 * @cfg {String} tag ( a | input | submit)
558 * @cfg {String} href empty or href
559 * @cfg {Boolean} disabled default false;
560 * @cfg {Boolean} isClose default false;
561 * @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)
562 * @cfg {String} badge text for badge
563 * @cfg {String} theme (default|glow)
564 * @cfg {Boolean} inverse dark themed version
565 * @cfg {Boolean} toggle is it a slidy toggle button
566 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567 * @cfg {String} ontext text for on slidy toggle state
568 * @cfg {String} offtext text for off slidy toggle state
569 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
570 * @cfg {Boolean} removeClass remove the standard class..
571 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
574 * Create a new button
575 * @param {Object} config The config object
579 Roo.bootstrap.Button = function(config){
580 Roo.bootstrap.Button.superclass.constructor.call(this, config);
581 this.weightClass = ["btn-default",
593 * When a butotn is pressed
594 * @param {Roo.bootstrap.Button} btn
595 * @param {Roo.EventObject} e
600 * After the button has been toggles
601 * @param {Roo.bootstrap.Button} btn
602 * @param {Roo.EventObject} e
603 * @param {boolean} pressed (also available as button.pressed)
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
627 preventDefault: true,
635 getAutoCreate : function(){
643 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
651 if (this.toggle == true) {
654 cls: 'slider-frame roo-button',
659 'data-off-text':'OFF',
660 cls: 'slider-button',
666 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667 cfg.cls += ' '+this.weight;
676 cfg["aria-hidden"] = true;
678 cfg.html = "×";
684 if (this.theme==='default') {
685 cfg.cls = 'btn roo-button';
687 //if (this.parentType != 'Navbar') {
688 this.weight = this.weight.length ? this.weight : 'default';
690 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
692 cfg.cls += ' btn-' + this.weight;
694 } else if (this.theme==='glow') {
697 cfg.cls = 'btn-glow roo-button';
699 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' ' + this.weight;
707 this.cls += ' inverse';
711 if (this.active || this.pressed === true) {
712 cfg.cls += ' active';
716 cfg.disabled = 'disabled';
720 Roo.log('changing to ul' );
722 this.glyphicon = 'caret';
725 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
727 //gsRoo.log(this.parentType);
728 if (this.parentType === 'Navbar' && !this.parent().bar) {
729 Roo.log('changing to li?');
738 href : this.href || '#'
741 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
742 cfg.cls += ' dropdown';
749 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
751 if (this.glyphicon) {
752 cfg.html = ' ' + cfg.html;
757 cls: 'glyphicon glyphicon-' + this.glyphicon
767 // cfg.cls='btn roo-button';
771 var value = cfg.html;
776 cls: 'glyphicon glyphicon-' + this.glyphicon,
795 cfg.cls += ' dropdown';
796 cfg.html = typeof(cfg.html) != 'undefined' ?
797 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
800 if (cfg.tag !== 'a' && this.href !== '') {
801 throw "Tag must be a to set href.";
802 } else if (this.href.length > 0) {
803 cfg.href = this.href;
806 if(this.removeClass){
811 cfg.target = this.target;
816 initEvents: function() {
817 // Roo.log('init events?');
818 // Roo.log(this.el.dom);
821 if (typeof (this.menu) != 'undefined') {
822 this.menu.parentType = this.xtype;
823 this.menu.triggerEl = this.el;
824 this.addxtype(Roo.apply({}, this.menu));
828 if (this.el.hasClass('roo-button')) {
829 this.el.on('click', this.onClick, this);
831 this.el.select('.roo-button').on('click', this.onClick, this);
834 if(this.removeClass){
835 this.el.on('click', this.onClick, this);
838 this.el.enableDisplayMode();
841 onClick : function(e)
847 Roo.log('button on click ');
848 if(this.preventDefault){
852 if (this.pressed === true || this.pressed === false) {
853 this.toggleActive(e);
857 this.fireEvent('click', this, e);
861 * Enables this button
865 this.disabled = false;
866 this.el.removeClass('disabled');
870 * Disable this button
874 this.disabled = true;
875 this.el.addClass('disabled');
878 * sets the active state on/off,
879 * @param {Boolean} state (optional) Force a particular state
881 setActive : function(v) {
883 this.el[v ? 'addClass' : 'removeClass']('active');
887 * toggles the current active state
889 toggleActive : function(e)
891 this.setActive(!this.pressed);
892 this.fireEvent('toggle', this, e, !this.pressed);
895 * get the current active state
896 * @return {boolean} true if it's active
898 isActive : function()
900 return this.el.hasClass('active');
903 * set the text of the first selected button
905 setText : function(str)
907 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
910 * get the text of the first selected button
914 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
925 setWeight : function(str)
927 this.el.removeClass(this.weightClass);
928 this.el.addClass('btn-' + str);
942 * @class Roo.bootstrap.Column
943 * @extends Roo.bootstrap.Component
944 * Bootstrap Column class
945 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
955 * @cfg {Boolean} hidden (true|false) hide the element
956 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957 * @cfg {String} fa (ban|check|...) font awesome icon
958 * @cfg {Number} fasize (1|2|....) font awsome size
960 * @cfg {String} icon (info-sign|check|...) glyphicon name
962 * @cfg {String} html content of column.
965 * Create a new Column
966 * @param {Object} config The config object
969 Roo.bootstrap.Column = function(config){
970 Roo.bootstrap.Column.superclass.constructor.call(this, config);
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
991 getAutoCreate : function(){
992 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1000 ['xs','sm','md','lg'].map(function(size){
1001 //Roo.log( size + ':' + settings[size]);
1003 if (settings[size+'off'] !== false) {
1004 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1007 if (settings[size] === false) {
1011 if (!settings[size]) { // 0 = hidden
1012 cfg.cls += ' hidden-' + size;
1015 cfg.cls += ' col-' + size + '-' + settings[size];
1020 cfg.cls += ' hidden';
1023 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024 cfg.cls +=' alert alert-' + this.alert;
1028 if (this.html.length) {
1029 cfg.html = this.html;
1033 if (this.fasize > 1) {
1034 fasize = ' fa-' + this.fasize + 'x';
1036 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1060 * @class Roo.bootstrap.Container
1061 * @extends Roo.bootstrap.Component
1062 * Bootstrap Container class
1063 * @cfg {Boolean} jumbotron is it a jumbotron element
1064 * @cfg {String} html content of element
1065 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1067 * @cfg {String} header content of header (for panel)
1068 * @cfg {String} footer content of footer (for panel)
1069 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070 * @cfg {String} tag (header|aside|section) type of HTML tag.
1071 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072 * @cfg {String} fa font awesome icon
1073 * @cfg {String} icon (info-sign|check|...) glyphicon name
1074 * @cfg {Boolean} hidden (true|false) hide the element
1075 * @cfg {Boolean} expandable (true|false) default false
1076 * @cfg {Boolean} expanded (true|false) default true
1077 * @cfg {String} rheader contet on the right of header
1078 * @cfg {Boolean} clickable (true|false) default false
1082 * Create a new Container
1083 * @param {Object} config The config object
1086 Roo.bootstrap.Container = function(config){
1087 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1093 * After the panel has been expand
1095 * @param {Roo.bootstrap.Container} this
1100 * After the panel has been collapsed
1102 * @param {Roo.bootstrap.Container} this
1107 * When a element is chick
1108 * @param {Roo.bootstrap.Container} this
1109 * @param {Roo.EventObject} e
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1133 getChildContainer : function() {
1139 if (this.panel.length) {
1140 return this.el.select('.panel-body',true).first();
1147 getAutoCreate : function(){
1150 tag : this.tag || 'div',
1154 if (this.jumbotron) {
1155 cfg.cls = 'jumbotron';
1160 // - this is applied by the parent..
1162 // cfg.cls = this.cls + '';
1165 if (this.sticky.length) {
1167 var bd = Roo.get(document.body);
1168 if (!bd.hasClass('bootstrap-sticky')) {
1169 bd.addClass('bootstrap-sticky');
1170 Roo.select('html',true).setStyle('height', '100%');
1173 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1177 if (this.well.length) {
1178 switch (this.well) {
1181 cfg.cls +=' well well-' +this.well;
1190 cfg.cls += ' hidden';
1194 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195 cfg.cls +=' alert alert-' + this.alert;
1200 if (this.panel.length) {
1201 cfg.cls += ' panel panel-' + this.panel;
1203 if (this.header.length) {
1207 if(this.expandable){
1209 cfg.cls = cfg.cls + ' expandable';
1213 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1221 cls : 'panel-title',
1222 html : (this.expandable ? ' ' : '') + this.header
1226 cls: 'panel-header-right',
1232 cls : 'panel-heading',
1233 style : this.expandable ? 'cursor: pointer' : '',
1241 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246 if (this.footer.length) {
1248 cls : 'panel-footer',
1257 body.html = this.html || cfg.html;
1258 // prefix with the icons..
1260 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1263 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269 cfg.cls = 'container';
1275 initEvents: function()
1277 if(this.expandable){
1278 var headerEl = this.headerEl();
1281 headerEl.on('click', this.onToggleClick, this);
1286 this.el.on('click', this.onClick, this);
1291 onToggleClick : function()
1293 var headerEl = this.headerEl();
1309 if(this.fireEvent('expand', this)) {
1311 this.expanded = true;
1313 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1315 this.el.select('.panel-body',true).first().removeClass('hide');
1317 var toggleEl = this.toggleEl();
1323 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328 collapse : function()
1330 if(this.fireEvent('collapse', this)) {
1332 this.expanded = false;
1334 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335 this.el.select('.panel-body',true).first().addClass('hide');
1337 var toggleEl = this.toggleEl();
1343 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1347 toggleEl : function()
1349 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1353 return this.el.select('.panel-heading .fa',true).first();
1356 headerEl : function()
1358 if(!this.el || !this.panel.length || !this.header.length){
1362 return this.el.select('.panel-heading',true).first()
1367 if(!this.el || !this.panel.length){
1371 return this.el.select('.panel-body',true).first()
1374 titleEl : function()
1376 if(!this.el || !this.panel.length || !this.header.length){
1380 return this.el.select('.panel-title',true).first();
1383 setTitle : function(v)
1385 var titleEl = this.titleEl();
1391 titleEl.dom.innerHTML = v;
1394 getTitle : function()
1397 var titleEl = this.titleEl();
1403 return titleEl.dom.innerHTML;
1406 setRightTitle : function(v)
1408 var t = this.el.select('.panel-header-right',true).first();
1414 t.dom.innerHTML = v;
1417 onClick : function(e)
1421 this.fireEvent('click', this, e);
1434 * @class Roo.bootstrap.Img
1435 * @extends Roo.bootstrap.Component
1436 * Bootstrap Img class
1437 * @cfg {Boolean} imgResponsive false | true
1438 * @cfg {String} border rounded | circle | thumbnail
1439 * @cfg {String} src image source
1440 * @cfg {String} alt image alternative text
1441 * @cfg {String} href a tag href
1442 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443 * @cfg {String} xsUrl xs image source
1444 * @cfg {String} smUrl sm image source
1445 * @cfg {String} mdUrl md image source
1446 * @cfg {String} lgUrl lg image source
1449 * Create a new Input
1450 * @param {Object} config The config object
1453 Roo.bootstrap.Img = function(config){
1454 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1460 * The img click event for the img.
1461 * @param {Roo.EventObject} e
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1469 imgResponsive: true,
1479 getAutoCreate : function()
1481 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482 return this.createSingleImg();
1487 cls: 'roo-image-responsive-group',
1492 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1494 if(!_this[size + 'Url']){
1500 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501 html: _this.html || cfg.html,
1502 src: _this[size + 'Url']
1505 img.cls += ' roo-image-responsive-' + size;
1507 var s = ['xs', 'sm', 'md', 'lg'];
1509 s.splice(s.indexOf(size), 1);
1511 Roo.each(s, function(ss){
1512 img.cls += ' hidden-' + ss;
1515 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516 cfg.cls += ' img-' + _this.border;
1520 cfg.alt = _this.alt;
1533 a.target = _this.target;
1537 cfg.cn.push((_this.href) ? a : img);
1544 createSingleImg : function()
1548 cls: (this.imgResponsive) ? 'img-responsive' : '',
1550 src : 'about:blank' // just incase src get's set to undefined?!?
1553 cfg.html = this.html || cfg.html;
1555 cfg.src = this.src || cfg.src;
1557 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558 cfg.cls += ' img-' + this.border;
1575 a.target = this.target;
1580 return (this.href) ? a : cfg;
1583 initEvents: function()
1586 this.el.on('click', this.onClick, this);
1591 onClick : function(e)
1593 Roo.log('img onclick');
1594 this.fireEvent('click', this, e);
1597 * Sets the url of the image - used to update it
1598 * @param {String} url the url of the image
1601 setSrc : function(url)
1605 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606 this.el.dom.src = url;
1610 this.el.select('img', true).first().dom.src = url;
1626 * @class Roo.bootstrap.Link
1627 * @extends Roo.bootstrap.Component
1628 * Bootstrap Link Class
1629 * @cfg {String} alt image alternative text
1630 * @cfg {String} href a tag href
1631 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632 * @cfg {String} html the content of the link.
1633 * @cfg {String} anchor name for the anchor link
1634 * @cfg {String} fa - favicon
1636 * @cfg {Boolean} preventDefault (true | false) default false
1640 * Create a new Input
1641 * @param {Object} config The config object
1644 Roo.bootstrap.Link = function(config){
1645 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1651 * The img click event for the img.
1652 * @param {Roo.EventObject} e
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1662 preventDefault: false,
1668 getAutoCreate : function()
1670 var html = this.html || '';
1672 if (this.fa !== false) {
1673 html = '<i class="fa fa-' + this.fa + '"></i>';
1678 // anchor's do not require html/href...
1679 if (this.anchor === false) {
1681 cfg.href = this.href || '#';
1683 cfg.name = this.anchor;
1684 if (this.html !== false || this.fa !== false) {
1687 if (this.href !== false) {
1688 cfg.href = this.href;
1692 if(this.alt !== false){
1697 if(this.target !== false) {
1698 cfg.target = this.target;
1704 initEvents: function() {
1706 if(!this.href || this.preventDefault){
1707 this.el.on('click', this.onClick, this);
1711 onClick : function(e)
1713 if(this.preventDefault){
1716 //Roo.log('img onclick');
1717 this.fireEvent('click', this, e);
1730 * @class Roo.bootstrap.Header
1731 * @extends Roo.bootstrap.Component
1732 * Bootstrap Header class
1733 * @cfg {String} html content of header
1734 * @cfg {Number} level (1|2|3|4|5|6) default 1
1737 * Create a new Header
1738 * @param {Object} config The config object
1742 Roo.bootstrap.Header = function(config){
1743 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1754 getAutoCreate : function(){
1759 tag: 'h' + (1 *this.level),
1760 html: this.html || ''
1772 * Ext JS Library 1.1.1
1773 * Copyright(c) 2006-2007, Ext JS, LLC.
1775 * Originally Released Under LGPL - original licence link has changed is not relivant.
1778 * <script type="text/javascript">
1782 * @class Roo.bootstrap.MenuMgr
1783 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1786 Roo.bootstrap.MenuMgr = function(){
1787 var menus, active, groups = {}, attached = false, lastShow = new Date();
1789 // private - called when first menu is created
1792 active = new Roo.util.MixedCollection();
1793 Roo.get(document).addKeyListener(27, function(){
1794 if(active.length > 0){
1802 if(active && active.length > 0){
1803 var c = active.clone();
1813 if(active.length < 1){
1814 Roo.get(document).un("mouseup", onMouseDown);
1822 var last = active.last();
1823 lastShow = new Date();
1826 Roo.get(document).on("mouseup", onMouseDown);
1831 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832 m.parentMenu.activeChild = m;
1833 }else if(last && last.isVisible()){
1834 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839 function onBeforeHide(m){
1841 m.activeChild.hide();
1843 if(m.autoHideTimer){
1844 clearTimeout(m.autoHideTimer);
1845 delete m.autoHideTimer;
1850 function onBeforeShow(m){
1851 var pm = m.parentMenu;
1852 if(!pm && !m.allowOtherMenus){
1854 }else if(pm && pm.activeChild && active != m){
1855 pm.activeChild.hide();
1859 // private this should really trigger on mouseup..
1860 function onMouseDown(e){
1861 Roo.log("on Mouse Up");
1863 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864 Roo.log("MenuManager hideAll");
1873 function onBeforeCheck(mi, state){
1875 var g = groups[mi.group];
1876 for(var i = 0, l = g.length; i < l; i++){
1878 g[i].setChecked(false);
1887 * Hides all menus that are currently visible
1889 hideAll : function(){
1894 register : function(menu){
1898 menus[menu.id] = menu;
1899 menu.on("beforehide", onBeforeHide);
1900 menu.on("hide", onHide);
1901 menu.on("beforeshow", onBeforeShow);
1902 menu.on("show", onShow);
1904 if(g && menu.events["checkchange"]){
1908 groups[g].push(menu);
1909 menu.on("checkchange", onCheck);
1914 * Returns a {@link Roo.menu.Menu} object
1915 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916 * be used to generate and return a new Menu instance.
1918 get : function(menu){
1919 if(typeof menu == "string"){ // menu id
1921 }else if(menu.events){ // menu instance
1924 /*else if(typeof menu.length == 'number'){ // array of menu items?
1925 return new Roo.bootstrap.Menu({items:menu});
1926 }else{ // otherwise, must be a config
1927 return new Roo.bootstrap.Menu(menu);
1934 unregister : function(menu){
1935 delete menus[menu.id];
1936 menu.un("beforehide", onBeforeHide);
1937 menu.un("hide", onHide);
1938 menu.un("beforeshow", onBeforeShow);
1939 menu.un("show", onShow);
1941 if(g && menu.events["checkchange"]){
1942 groups[g].remove(menu);
1943 menu.un("checkchange", onCheck);
1948 registerCheckable : function(menuItem){
1949 var g = menuItem.group;
1954 groups[g].push(menuItem);
1955 menuItem.on("beforecheckchange", onBeforeCheck);
1960 unregisterCheckable : function(menuItem){
1961 var g = menuItem.group;
1963 groups[g].remove(menuItem);
1964 menuItem.un("beforecheckchange", onBeforeCheck);
1976 * @class Roo.bootstrap.Menu
1977 * @extends Roo.bootstrap.Component
1978 * Bootstrap Menu class - container for MenuItems
1979 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980 * @cfg {bool} hidden if the menu should be hidden when rendered.
1981 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1982 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1986 * @param {Object} config The config object
1990 Roo.bootstrap.Menu = function(config){
1991 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992 if (this.registerMenu && this.type != 'treeview') {
1993 Roo.bootstrap.MenuMgr.register(this);
1998 * Fires before this menu is displayed
1999 * @param {Roo.menu.Menu} this
2004 * Fires before this menu is hidden
2005 * @param {Roo.menu.Menu} this
2010 * Fires after this menu is displayed
2011 * @param {Roo.menu.Menu} this
2016 * Fires after this menu is hidden
2017 * @param {Roo.menu.Menu} this
2022 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023 * @param {Roo.menu.Menu} this
2024 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025 * @param {Roo.EventObject} e
2030 * Fires when the mouse is hovering over this menu
2031 * @param {Roo.menu.Menu} this
2032 * @param {Roo.EventObject} e
2033 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038 * Fires when the mouse exits this menu
2039 * @param {Roo.menu.Menu} this
2040 * @param {Roo.EventObject} e
2041 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046 * Fires when a menu item contained in this menu is clicked
2047 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048 * @param {Roo.EventObject} e
2052 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2059 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2062 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2064 registerMenu : true,
2066 menuItems :false, // stores the menu items..
2076 getChildContainer : function() {
2080 getAutoCreate : function(){
2082 //if (['right'].indexOf(this.align)!==-1) {
2083 // cfg.cn[1].cls += ' pull-right'
2089 cls : 'dropdown-menu' ,
2090 style : 'z-index:1000'
2094 if (this.type === 'submenu') {
2095 cfg.cls = 'submenu active';
2097 if (this.type === 'treeview') {
2098 cfg.cls = 'treeview-menu';
2103 initEvents : function() {
2105 // Roo.log("ADD event");
2106 // Roo.log(this.triggerEl.dom);
2108 this.triggerEl.on('click', this.onTriggerClick, this);
2110 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2112 this.triggerEl.addClass('dropdown-toggle');
2115 this.el.on('touchstart' , this.onTouch, this);
2117 this.el.on('click' , this.onClick, this);
2119 this.el.on("mouseover", this.onMouseOver, this);
2120 this.el.on("mouseout", this.onMouseOut, this);
2124 findTargetItem : function(e)
2126 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2130 //Roo.log(t); Roo.log(t.id);
2132 //Roo.log(this.menuitems);
2133 return this.menuitems.get(t.id);
2135 //return this.items.get(t.menuItemId);
2141 onTouch : function(e)
2143 Roo.log("menu.onTouch");
2144 //e.stopEvent(); this make the user popdown broken
2148 onClick : function(e)
2150 Roo.log("menu.onClick");
2152 var t = this.findTargetItem(e);
2153 if(!t || t.isContainer){
2158 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2159 if(t == this.activeItem && t.shouldDeactivate(e)){
2160 this.activeItem.deactivate();
2161 delete this.activeItem;
2165 this.setActiveItem(t, true);
2173 Roo.log('pass click event');
2177 this.fireEvent("click", this, t, e);
2181 if(!t.href.length || t.href == '#'){
2182 (function() { _this.hide(); }).defer(100);
2187 onMouseOver : function(e){
2188 var t = this.findTargetItem(e);
2191 // if(t.canActivate && !t.disabled){
2192 // this.setActiveItem(t, true);
2196 this.fireEvent("mouseover", this, e, t);
2198 isVisible : function(){
2199 return !this.hidden;
2201 onMouseOut : function(e){
2202 var t = this.findTargetItem(e);
2205 // if(t == this.activeItem && t.shouldDeactivate(e)){
2206 // this.activeItem.deactivate();
2207 // delete this.activeItem;
2210 this.fireEvent("mouseout", this, e, t);
2215 * Displays this menu relative to another element
2216 * @param {String/HTMLElement/Roo.Element} element The element to align to
2217 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218 * the element (defaults to this.defaultAlign)
2219 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2221 show : function(el, pos, parentMenu){
2222 this.parentMenu = parentMenu;
2226 this.fireEvent("beforeshow", this);
2227 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2230 * Displays this menu at a specific xy position
2231 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2232 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2234 showAt : function(xy, parentMenu, /* private: */_e){
2235 this.parentMenu = parentMenu;
2240 this.fireEvent("beforeshow", this);
2241 //xy = this.el.adjustForConstraints(xy);
2245 this.hideMenuItems();
2246 this.hidden = false;
2247 this.triggerEl.addClass('open');
2249 // reassign x when hitting right
2250 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2254 // reassign y when hitting bottom
2255 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2259 // but the list may align on trigger left or trigger top... should it be a properity?
2261 Roo.log(this.el.getWidth());
2263 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2267 Roo.log(this.el.getWidth());
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
2584 * Create a new Modal Dialog
2585 * @param {Object} config The config object
2588 Roo.bootstrap.Modal = function(config){
2589 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2594 * The raw btnclick event for the button
2595 * @param {Roo.EventObject} e
2600 * Fire when dialog resize
2601 * @param {Roo.bootstrap.Modal} this
2602 * @param {Roo.EventObject} e
2606 this.buttons = this.buttons || [];
2609 this.tmpl = Roo.factory(this.tmpl);
2614 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2616 title : 'test dialog',
2626 specificTitle: false,
2628 buttonPosition: 'right',
2647 onRender : function(ct, position)
2649 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2652 var cfg = Roo.apply({}, this.getAutoCreate());
2655 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2657 //if (!cfg.name.length) {
2661 cfg.cls += ' ' + this.cls;
2664 cfg.style = this.style;
2666 this.el = Roo.get(document.body).createChild(cfg, position);
2668 //var type = this.el.dom.type;
2671 if(this.tabIndex !== undefined){
2672 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2675 this.dialogEl = this.el.select('.modal-dialog',true).first();
2676 this.bodyEl = this.el.select('.modal-body',true).first();
2677 this.closeEl = this.el.select('.modal-header .close', true).first();
2678 this.headerEl = this.el.select('.modal-header',true).first();
2679 this.titleEl = this.el.select('.modal-title',true).first();
2680 this.footerEl = this.el.select('.modal-footer',true).first();
2682 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2684 //this.el.addClass("x-dlg-modal");
2686 if (this.buttons.length) {
2687 Roo.each(this.buttons, function(bb) {
2688 var b = Roo.apply({}, bb);
2689 b.xns = b.xns || Roo.bootstrap;
2690 b.xtype = b.xtype || 'Button';
2691 if (typeof(b.listeners) == 'undefined') {
2692 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2695 var btn = Roo.factory(b);
2697 btn.render(this.el.select('.modal-footer div').first());
2701 // render the children.
2704 if(typeof(this.items) != 'undefined'){
2705 var items = this.items;
2708 for(var i =0;i < items.length;i++) {
2709 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2713 this.items = nitems;
2715 // where are these used - they used to be body/close/footer
2719 //this.el.addClass([this.fieldClass, this.cls]);
2723 getAutoCreate : function(){
2728 html : this.html || ''
2733 cls : 'modal-title',
2737 if(this.specificTitle){
2743 if (this.allow_close) {
2755 if(this.size.length){
2756 size = 'modal-' + this.size;
2763 cls: "modal-dialog " + size,
2766 cls : "modal-content",
2769 cls : 'modal-header',
2774 cls : 'modal-footer',
2778 cls: 'btn-' + this.buttonPosition
2795 modal.cls += ' fade';
2801 getChildContainer : function() {
2806 getButtonContainer : function() {
2807 return this.el.select('.modal-footer div',true).first();
2810 initEvents : function()
2812 if (this.allow_close) {
2813 this.closeEl.on('click', this.hide, this);
2815 Roo.EventManager.onWindowResize(this.resize, this, true);
2822 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2823 if (this.fitwindow) {
2824 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2825 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2830 setSize : function(w,h)
2840 if (!this.rendered) {
2844 //this.el.setStyle('display', 'block');
2845 this.el.removeClass('hideing');
2846 this.el.addClass('show');
2848 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2851 this.el.addClass('in');
2854 this.el.addClass('in');
2858 // not sure how we can show data in here..
2860 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2863 Roo.get(document.body).addClass("x-body-masked");
2865 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2866 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2867 this.maskEl.addClass('show');
2871 this.fireEvent('show', this);
2873 // set zindex here - otherwise it appears to be ignored...
2874 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2877 this.items.forEach( function(e) {
2878 e.layout ? e.layout() : false;
2886 if(this.fireEvent("beforehide", this) !== false){
2887 this.maskEl.removeClass('show');
2888 Roo.get(document.body).removeClass("x-body-masked");
2889 this.el.removeClass('in');
2890 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2892 if(this.animate){ // why
2893 this.el.addClass('hideing');
2895 if (!this.el.hasClass('hideing')) {
2896 return; // it's been shown again...
2898 this.el.removeClass('show');
2899 this.el.removeClass('hideing');
2903 this.el.removeClass('show');
2905 this.fireEvent('hide', this);
2908 isVisible : function()
2911 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2915 addButton : function(str, cb)
2919 var b = Roo.apply({}, { html : str } );
2920 b.xns = b.xns || Roo.bootstrap;
2921 b.xtype = b.xtype || 'Button';
2922 if (typeof(b.listeners) == 'undefined') {
2923 b.listeners = { click : cb.createDelegate(this) };
2926 var btn = Roo.factory(b);
2928 btn.render(this.el.select('.modal-footer div').first());
2934 setDefaultButton : function(btn)
2936 //this.el.select('.modal-footer').()
2940 resizeTo: function(w,h)
2944 this.dialogEl.setWidth(w);
2945 if (this.diff === false) {
2946 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2949 this.bodyEl.setHeight(h-this.diff);
2951 this.fireEvent('resize', this);
2954 setContentSize : function(w, h)
2958 onButtonClick: function(btn,e)
2961 this.fireEvent('btnclick', btn.name, e);
2964 * Set the title of the Dialog
2965 * @param {String} str new Title
2967 setTitle: function(str) {
2968 this.titleEl.dom.innerHTML = str;
2971 * Set the body of the Dialog
2972 * @param {String} str new Title
2974 setBody: function(str) {
2975 this.bodyEl.dom.innerHTML = str;
2978 * Set the body of the Dialog using the template
2979 * @param {Obj} data - apply this data to the template and replace the body contents.
2981 applyBody: function(obj)
2984 Roo.log("Error - using apply Body without a template");
2987 this.tmpl.overwrite(this.bodyEl, obj);
2993 Roo.apply(Roo.bootstrap.Modal, {
2995 * Button config that displays a single OK button
3004 * Button config that displays Yes and No buttons
3020 * Button config that displays OK and Cancel buttons
3035 * Button config that displays Yes, No and Cancel buttons
3059 * messagebox - can be used as a replace
3063 * @class Roo.MessageBox
3064 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3068 Roo.Msg.alert('Status', 'Changes saved successfully.');
3070 // Prompt for user data:
3071 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3073 // process text value...
3077 // Show a dialog using config options:
3079 title:'Save Changes?',
3080 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3081 buttons: Roo.Msg.YESNOCANCEL,
3088 Roo.bootstrap.MessageBox = function(){
3089 var dlg, opt, mask, waitTimer;
3090 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3091 var buttons, activeTextEl, bwidth;
3095 var handleButton = function(button){
3097 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3101 var handleHide = function(){
3103 dlg.el.removeClass(opt.cls);
3106 // Roo.TaskMgr.stop(waitTimer);
3107 // waitTimer = null;
3112 var updateButtons = function(b){
3115 buttons["ok"].hide();
3116 buttons["cancel"].hide();
3117 buttons["yes"].hide();
3118 buttons["no"].hide();
3119 //dlg.footer.dom.style.display = 'none';
3122 dlg.footerEl.dom.style.display = '';
3123 for(var k in buttons){
3124 if(typeof buttons[k] != "function"){
3127 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3128 width += buttons[k].el.getWidth()+15;
3138 var handleEsc = function(d, k, e){
3139 if(opt && opt.closable !== false){
3149 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3150 * @return {Roo.BasicDialog} The BasicDialog element
3152 getDialog : function(){
3154 dlg = new Roo.bootstrap.Modal( {
3157 //constraintoviewport:false,
3159 //collapsible : false,
3164 //buttonAlign:"center",
3165 closeClick : function(){
3166 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3169 handleButton("cancel");
3174 dlg.on("hide", handleHide);
3176 //dlg.addKeyListener(27, handleEsc);
3178 this.buttons = buttons;
3179 var bt = this.buttonText;
3180 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3181 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3182 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3183 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3185 bodyEl = dlg.bodyEl.createChild({
3187 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3188 '<textarea class="roo-mb-textarea"></textarea>' +
3189 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3191 msgEl = bodyEl.dom.firstChild;
3192 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3193 textboxEl.enableDisplayMode();
3194 textboxEl.addKeyListener([10,13], function(){
3195 if(dlg.isVisible() && opt && opt.buttons){
3198 }else if(opt.buttons.yes){
3199 handleButton("yes");
3203 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3204 textareaEl.enableDisplayMode();
3205 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3206 progressEl.enableDisplayMode();
3208 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3209 var pf = progressEl.dom.firstChild;
3211 pp = Roo.get(pf.firstChild);
3212 pp.setHeight(pf.offsetHeight);
3220 * Updates the message box body text
3221 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3222 * the XHTML-compliant non-breaking space character '&#160;')
3223 * @return {Roo.MessageBox} This message box
3225 updateText : function(text)
3227 if(!dlg.isVisible() && !opt.width){
3228 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3229 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3231 msgEl.innerHTML = text || ' ';
3233 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3234 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3236 Math.min(opt.width || cw , this.maxWidth),
3237 Math.max(opt.minWidth || this.minWidth, bwidth)
3240 activeTextEl.setWidth(w);
3242 if(dlg.isVisible()){
3243 dlg.fixedcenter = false;
3245 // to big, make it scroll. = But as usual stupid IE does not support
3248 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3249 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3250 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3252 bodyEl.dom.style.height = '';
3253 bodyEl.dom.style.overflowY = '';
3256 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3258 bodyEl.dom.style.overflowX = '';
3261 dlg.setContentSize(w, bodyEl.getHeight());
3262 if(dlg.isVisible()){
3263 dlg.fixedcenter = true;
3269 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3270 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3271 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3272 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3273 * @return {Roo.MessageBox} This message box
3275 updateProgress : function(value, text){
3277 this.updateText(text);
3280 if (pp) { // weird bug on my firefox - for some reason this is not defined
3281 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3282 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3288 * Returns true if the message box is currently displayed
3289 * @return {Boolean} True if the message box is visible, else false
3291 isVisible : function(){
3292 return dlg && dlg.isVisible();
3296 * Hides the message box if it is displayed
3299 if(this.isVisible()){
3305 * Displays a new message box, or reinitializes an existing message box, based on the config options
3306 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3307 * The following config object properties are supported:
3309 Property Type Description
3310 ---------- --------------- ------------------------------------------------------------------------------------
3311 animEl String/Element An id or Element from which the message box should animate as it opens and
3312 closes (defaults to undefined)
3313 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3314 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3315 closable Boolean False to hide the top-right close button (defaults to true). Note that
3316 progress and wait dialogs will ignore this property and always hide the
3317 close button as they can only be closed programmatically.
3318 cls String A custom CSS class to apply to the message box element
3319 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3320 displayed (defaults to 75)
3321 fn Function A callback function to execute after closing the dialog. The arguments to the
3322 function will be btn (the name of the button that was clicked, if applicable,
3323 e.g. "ok"), and text (the value of the active text field, if applicable).
3324 Progress and wait dialogs will ignore this option since they do not respond to
3325 user actions and can only be closed programmatically, so any required function
3326 should be called by the same code after it closes the dialog.
3327 icon String A CSS class that provides a background image to be used as an icon for
3328 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3329 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3330 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3331 modal Boolean False to allow user interaction with the page while the message box is
3332 displayed (defaults to true)
3333 msg String A string that will replace the existing message box body text (defaults
3334 to the XHTML-compliant non-breaking space character ' ')
3335 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3336 progress Boolean True to display a progress bar (defaults to false)
3337 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3338 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3339 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3340 title String The title text
3341 value String The string value to set into the active textbox element if displayed
3342 wait Boolean True to display a progress bar (defaults to false)
3343 width Number The width of the dialog in pixels
3350 msg: 'Please enter your address:',
3352 buttons: Roo.MessageBox.OKCANCEL,
3355 animEl: 'addAddressBtn'
3358 * @param {Object} config Configuration options
3359 * @return {Roo.MessageBox} This message box
3361 show : function(options)
3364 // this causes nightmares if you show one dialog after another
3365 // especially on callbacks..
3367 if(this.isVisible()){
3370 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3371 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3372 Roo.log("New Dialog Message:" + options.msg )
3373 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3374 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3377 var d = this.getDialog();
3379 d.setTitle(opt.title || " ");
3380 d.closeEl.setDisplayed(opt.closable !== false);
3381 activeTextEl = textboxEl;
3382 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3387 textareaEl.setHeight(typeof opt.multiline == "number" ?
3388 opt.multiline : this.defaultTextHeight);
3389 activeTextEl = textareaEl;
3398 progressEl.setDisplayed(opt.progress === true);
3399 this.updateProgress(0);
3400 activeTextEl.dom.value = opt.value || "";
3402 dlg.setDefaultButton(activeTextEl);
3404 var bs = opt.buttons;
3408 }else if(bs && bs.yes){
3409 db = buttons["yes"];
3411 dlg.setDefaultButton(db);
3413 bwidth = updateButtons(opt.buttons);
3414 this.updateText(opt.msg);
3416 d.el.addClass(opt.cls);
3418 d.proxyDrag = opt.proxyDrag === true;
3419 d.modal = opt.modal !== false;
3420 d.mask = opt.modal !== false ? mask : false;
3422 // force it to the end of the z-index stack so it gets a cursor in FF
3423 document.body.appendChild(dlg.el.dom);
3424 d.animateTarget = null;
3425 d.show(options.animEl);
3431 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3432 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3433 * and closing the message box when the process is complete.
3434 * @param {String} title The title bar text
3435 * @param {String} msg The message box body text
3436 * @return {Roo.MessageBox} This message box
3438 progress : function(title, msg){
3445 minWidth: this.minProgressWidth,
3452 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3453 * If a callback function is passed it will be called after the user clicks the button, and the
3454 * id of the button that was clicked will be passed as the only parameter to the callback
3455 * (could also be the top-right close button).
3456 * @param {String} title The title bar text
3457 * @param {String} msg The message box body text
3458 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3459 * @param {Object} scope (optional) The scope of the callback function
3460 * @return {Roo.MessageBox} This message box
3462 alert : function(title, msg, fn, scope)
3477 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3478 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3479 * You are responsible for closing the message box when the process is complete.
3480 * @param {String} msg The message box body text
3481 * @param {String} title (optional) The title bar text
3482 * @return {Roo.MessageBox} This message box
3484 wait : function(msg, title){
3495 waitTimer = Roo.TaskMgr.start({
3497 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3505 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3506 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3507 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3508 * @param {String} title The title bar text
3509 * @param {String} msg The message box body text
3510 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3511 * @param {Object} scope (optional) The scope of the callback function
3512 * @return {Roo.MessageBox} This message box
3514 confirm : function(title, msg, fn, scope){
3518 buttons: this.YESNO,
3527 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3528 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3529 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3530 * (could also be the top-right close button) and the text that was entered will be passed as the two
3531 * parameters to the callback.
3532 * @param {String} title The title bar text
3533 * @param {String} msg The message box body text
3534 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3535 * @param {Object} scope (optional) The scope of the callback function
3536 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3537 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3538 * @return {Roo.MessageBox} This message box
3540 prompt : function(title, msg, fn, scope, multiline){
3544 buttons: this.OKCANCEL,
3549 multiline: multiline,
3556 * Button config that displays a single OK button
3561 * Button config that displays Yes and No buttons
3564 YESNO : {yes:true, no:true},
3566 * Button config that displays OK and Cancel buttons
3569 OKCANCEL : {ok:true, cancel:true},
3571 * Button config that displays Yes, No and Cancel buttons
3574 YESNOCANCEL : {yes:true, no:true, cancel:true},
3577 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3580 defaultTextHeight : 75,
3582 * The maximum width in pixels of the message box (defaults to 600)
3587 * The minimum width in pixels of the message box (defaults to 100)
3592 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3593 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3596 minProgressWidth : 250,
3598 * An object containing the default button text strings that can be overriden for localized language support.
3599 * Supported properties are: ok, cancel, yes and no.
3600 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3613 * Shorthand for {@link Roo.MessageBox}
3615 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3616 Roo.Msg = Roo.Msg || Roo.MessageBox;
3625 * @class Roo.bootstrap.Navbar
3626 * @extends Roo.bootstrap.Component
3627 * Bootstrap Navbar class
3630 * Create a new Navbar
3631 * @param {Object} config The config object
3635 Roo.bootstrap.Navbar = function(config){
3636 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3640 * @event beforetoggle
3641 * Fire before toggle the menu
3642 * @param {Roo.EventObject} e
3644 "beforetoggle" : true
3648 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3657 getAutoCreate : function(){
3660 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3664 initEvents :function ()
3666 //Roo.log(this.el.select('.navbar-toggle',true));
3667 this.el.select('.navbar-toggle',true).on('click', function() {
3668 if(this.fireEvent('beforetoggle', this) !== false){
3669 this.el.select('.navbar-collapse',true).toggleClass('in');
3679 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3681 var size = this.el.getSize();
3682 this.maskEl.setSize(size.width, size.height);
3683 this.maskEl.enableDisplayMode("block");
3692 getChildContainer : function()
3694 if (this.el.select('.collapse').getCount()) {
3695 return this.el.select('.collapse',true).first();
3728 * @class Roo.bootstrap.NavSimplebar
3729 * @extends Roo.bootstrap.Navbar
3730 * Bootstrap Sidebar class
3732 * @cfg {Boolean} inverse is inverted color
3734 * @cfg {String} type (nav | pills | tabs)
3735 * @cfg {Boolean} arrangement stacked | justified
3736 * @cfg {String} align (left | right) alignment
3738 * @cfg {Boolean} main (true|false) main nav bar? default false
3739 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3741 * @cfg {String} tag (header|footer|nav|div) default is nav
3747 * Create a new Sidebar
3748 * @param {Object} config The config object
3752 Roo.bootstrap.NavSimplebar = function(config){
3753 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3756 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3772 getAutoCreate : function(){
3776 tag : this.tag || 'div',
3789 this.type = this.type || 'nav';
3790 if (['tabs','pills'].indexOf(this.type)!==-1) {
3791 cfg.cn[0].cls += ' nav-' + this.type
3795 if (this.type!=='nav') {
3796 Roo.log('nav type must be nav/tabs/pills')
3798 cfg.cn[0].cls += ' navbar-nav'
3804 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3805 cfg.cn[0].cls += ' nav-' + this.arrangement;
3809 if (this.align === 'right') {
3810 cfg.cn[0].cls += ' navbar-right';
3814 cfg.cls += ' navbar-inverse';
3841 * @class Roo.bootstrap.NavHeaderbar
3842 * @extends Roo.bootstrap.NavSimplebar
3843 * Bootstrap Sidebar class
3845 * @cfg {String} brand what is brand
3846 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3847 * @cfg {String} brand_href href of the brand
3848 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3849 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3850 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3851 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3854 * Create a new Sidebar
3855 * @param {Object} config The config object
3859 Roo.bootstrap.NavHeaderbar = function(config){
3860 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3864 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3871 desktopCenter : false,
3874 getAutoCreate : function(){
3877 tag: this.nav || 'nav',
3884 if (this.desktopCenter) {
3885 cn.push({cls : 'container', cn : []});
3892 cls: 'navbar-header',
3897 cls: 'navbar-toggle',
3898 'data-toggle': 'collapse',
3903 html: 'Toggle navigation'
3925 cls: 'collapse navbar-collapse',
3929 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3931 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3932 cfg.cls += ' navbar-' + this.position;
3934 // tag can override this..
3936 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3939 if (this.brand !== '') {
3942 href: this.brand_href ? this.brand_href : '#',
3943 cls: 'navbar-brand',
3951 cfg.cls += ' main-nav';
3959 getHeaderChildContainer : function()
3961 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3962 return this.el.select('.navbar-header',true).first();
3965 return this.getChildContainer();
3969 initEvents : function()
3971 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3973 if (this.autohide) {
3978 Roo.get(document).on('scroll',function(e) {
3979 var ns = Roo.get(document).getScroll().top;
3980 var os = prevScroll;
3984 ft.removeClass('slideDown');
3985 ft.addClass('slideUp');
3988 ft.removeClass('slideUp');
3989 ft.addClass('slideDown');
4010 * @class Roo.bootstrap.NavSidebar
4011 * @extends Roo.bootstrap.Navbar
4012 * Bootstrap Sidebar class
4015 * Create a new Sidebar
4016 * @param {Object} config The config object
4020 Roo.bootstrap.NavSidebar = function(config){
4021 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4024 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4026 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4028 getAutoCreate : function(){
4033 cls: 'sidebar sidebar-nav'
4055 * @class Roo.bootstrap.NavGroup
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap NavGroup class
4058 * @cfg {String} align (left|right)
4059 * @cfg {Boolean} inverse
4060 * @cfg {String} type (nav|pills|tab) default nav
4061 * @cfg {String} navId - reference Id for navbar.
4065 * Create a new nav group
4066 * @param {Object} config The config object
4069 Roo.bootstrap.NavGroup = function(config){
4070 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4073 Roo.bootstrap.NavGroup.register(this);
4077 * Fires when the active item changes
4078 * @param {Roo.bootstrap.NavGroup} this
4079 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4080 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4087 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4098 getAutoCreate : function()
4100 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4107 if (['tabs','pills'].indexOf(this.type)!==-1) {
4108 cfg.cls += ' nav-' + this.type
4110 if (this.type!=='nav') {
4111 Roo.log('nav type must be nav/tabs/pills')
4113 cfg.cls += ' navbar-nav'
4116 if (this.parent() && this.parent().sidebar) {
4119 cls: 'dashboard-menu sidebar-menu'
4125 if (this.form === true) {
4131 if (this.align === 'right') {
4132 cfg.cls += ' navbar-right';
4134 cfg.cls += ' navbar-left';
4138 if (this.align === 'right') {
4139 cfg.cls += ' navbar-right';
4143 cfg.cls += ' navbar-inverse';
4151 * sets the active Navigation item
4152 * @param {Roo.bootstrap.NavItem} the new current navitem
4154 setActiveItem : function(item)
4157 Roo.each(this.navItems, function(v){
4162 v.setActive(false, true);
4169 item.setActive(true, true);
4170 this.fireEvent('changed', this, item, prev);
4175 * gets the active Navigation item
4176 * @return {Roo.bootstrap.NavItem} the current navitem
4178 getActive : function()
4182 Roo.each(this.navItems, function(v){
4193 indexOfNav : function()
4197 Roo.each(this.navItems, function(v,i){
4208 * adds a Navigation item
4209 * @param {Roo.bootstrap.NavItem} the navitem to add
4211 addItem : function(cfg)
4213 var cn = new Roo.bootstrap.NavItem(cfg);
4215 cn.parentId = this.id;
4216 cn.onRender(this.el, null);
4220 * register a Navigation item
4221 * @param {Roo.bootstrap.NavItem} the navitem to add
4223 register : function(item)
4225 this.navItems.push( item);
4226 item.navId = this.navId;
4231 * clear all the Navigation item
4234 clearAll : function()
4237 this.el.dom.innerHTML = '';
4240 getNavItem: function(tabId)
4243 Roo.each(this.navItems, function(e) {
4244 if (e.tabId == tabId) {
4254 setActiveNext : function()
4256 var i = this.indexOfNav(this.getActive());
4257 if (i > this.navItems.length) {
4260 this.setActiveItem(this.navItems[i+1]);
4262 setActivePrev : function()
4264 var i = this.indexOfNav(this.getActive());
4268 this.setActiveItem(this.navItems[i-1]);
4270 clearWasActive : function(except) {
4271 Roo.each(this.navItems, function(e) {
4272 if (e.tabId != except.tabId && e.was_active) {
4273 e.was_active = false;
4280 getWasActive : function ()
4283 Roo.each(this.navItems, function(e) {
4298 Roo.apply(Roo.bootstrap.NavGroup, {
4302 * register a Navigation Group
4303 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4305 register : function(navgrp)
4307 this.groups[navgrp.navId] = navgrp;
4311 * fetch a Navigation Group based on the navigation ID
4312 * @param {string} the navgroup to add
4313 * @returns {Roo.bootstrap.NavGroup} the navgroup
4315 get: function(navId) {
4316 if (typeof(this.groups[navId]) == 'undefined') {
4318 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4320 return this.groups[navId] ;
4335 * @class Roo.bootstrap.NavItem
4336 * @extends Roo.bootstrap.Component
4337 * Bootstrap Navbar.NavItem class
4338 * @cfg {String} href link to
4339 * @cfg {String} html content of button
4340 * @cfg {String} badge text inside badge
4341 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4342 * @cfg {String} glyphicon name of glyphicon
4343 * @cfg {String} icon name of font awesome icon
4344 * @cfg {Boolean} active Is item active
4345 * @cfg {Boolean} disabled Is item disabled
4347 * @cfg {Boolean} preventDefault (true | false) default false
4348 * @cfg {String} tabId the tab that this item activates.
4349 * @cfg {String} tagtype (a|span) render as a href or span?
4350 * @cfg {Boolean} animateRef (true|false) link to element default false
4353 * Create a new Navbar Item
4354 * @param {Object} config The config object
4356 Roo.bootstrap.NavItem = function(config){
4357 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4362 * The raw click event for the entire grid.
4363 * @param {Roo.EventObject} e
4368 * Fires when the active item active state changes
4369 * @param {Roo.bootstrap.NavItem} this
4370 * @param {boolean} state the new state
4376 * Fires when scroll to element
4377 * @param {Roo.bootstrap.NavItem} this
4378 * @param {Object} options
4379 * @param {Roo.EventObject} e
4387 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4395 preventDefault : false,
4402 getAutoCreate : function(){
4411 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4413 if (this.disabled) {
4414 cfg.cls += ' disabled';
4417 if (this.href || this.html || this.glyphicon || this.icon) {
4421 href : this.href || "#",
4422 html: this.html || ''
4427 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4430 if(this.glyphicon) {
4431 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4436 cfg.cn[0].html += " <span class='caret'></span>";
4440 if (this.badge !== '') {
4442 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4450 initEvents: function()
4452 if (typeof (this.menu) != 'undefined') {
4453 this.menu.parentType = this.xtype;
4454 this.menu.triggerEl = this.el;
4455 this.menu = this.addxtype(Roo.apply({}, this.menu));
4458 this.el.select('a',true).on('click', this.onClick, this);
4460 if(this.tagtype == 'span'){
4461 this.el.select('span',true).on('click', this.onClick, this);
4464 // at this point parent should be available..
4465 this.parent().register(this);
4468 onClick : function(e)
4470 if (e.getTarget('.dropdown-menu-item')) {
4471 // did you click on a menu itemm.... - then don't trigger onclick..
4476 this.preventDefault ||
4479 Roo.log("NavItem - prevent Default?");
4483 if (this.disabled) {
4487 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4488 if (tg && tg.transition) {
4489 Roo.log("waiting for the transitionend");
4495 //Roo.log("fire event clicked");
4496 if(this.fireEvent('click', this, e) === false){
4500 if(this.tagtype == 'span'){
4504 //Roo.log(this.href);
4505 var ael = this.el.select('a',true).first();
4508 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4509 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4510 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4511 return; // ignore... - it's a 'hash' to another page.
4513 Roo.log("NavItem - prevent Default?");
4515 this.scrollToElement(e);
4519 var p = this.parent();
4521 if (['tabs','pills'].indexOf(p.type)!==-1) {
4522 if (typeof(p.setActiveItem) !== 'undefined') {
4523 p.setActiveItem(this);
4527 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4528 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4529 // remove the collapsed menu expand...
4530 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4534 isActive: function () {
4537 setActive : function(state, fire, is_was_active)
4539 if (this.active && !state && this.navId) {
4540 this.was_active = true;
4541 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4543 nv.clearWasActive(this);
4547 this.active = state;
4550 this.el.removeClass('active');
4551 } else if (!this.el.hasClass('active')) {
4552 this.el.addClass('active');
4555 this.fireEvent('changed', this, state);
4558 // show a panel if it's registered and related..
4560 if (!this.navId || !this.tabId || !state || is_was_active) {
4564 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4568 var pan = tg.getPanelByName(this.tabId);
4572 // if we can not flip to new panel - go back to old nav highlight..
4573 if (false == tg.showPanel(pan)) {
4574 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4576 var onav = nv.getWasActive();
4578 onav.setActive(true, false, true);
4587 // this should not be here...
4588 setDisabled : function(state)
4590 this.disabled = state;
4592 this.el.removeClass('disabled');
4593 } else if (!this.el.hasClass('disabled')) {
4594 this.el.addClass('disabled');
4600 * Fetch the element to display the tooltip on.
4601 * @return {Roo.Element} defaults to this.el
4603 tooltipEl : function()
4605 return this.el.select('' + this.tagtype + '', true).first();
4608 scrollToElement : function(e)
4610 var c = document.body;
4613 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4615 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4616 c = document.documentElement;
4619 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4625 var o = target.calcOffsetsTo(c);
4632 this.fireEvent('scrollto', this, options, e);
4634 Roo.get(c).scrollTo('top', options.value, true);
4647 * <span> icon </span>
4648 * <span> text </span>
4649 * <span>badge </span>
4653 * @class Roo.bootstrap.NavSidebarItem
4654 * @extends Roo.bootstrap.NavItem
4655 * Bootstrap Navbar.NavSidebarItem class
4656 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4657 * {Boolean} open is the menu open
4658 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4659 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4660 * {String} buttonSize (sm|md|lg)the extra classes for the button
4661 * {Boolean} showArrow show arrow next to the text (default true)
4663 * Create a new Navbar Button
4664 * @param {Object} config The config object
4666 Roo.bootstrap.NavSidebarItem = function(config){
4667 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4672 * The raw click event for the entire grid.
4673 * @param {Roo.EventObject} e
4678 * Fires when the active item active state changes
4679 * @param {Roo.bootstrap.NavSidebarItem} this
4680 * @param {boolean} state the new state
4688 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4690 badgeWeight : 'default',
4696 buttonWeight : 'default',
4702 getAutoCreate : function(){
4707 href : this.href || '#',
4713 if(this.buttonView){
4716 href : this.href || '#',
4717 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4730 cfg.cls += ' active';
4733 if (this.disabled) {
4734 cfg.cls += ' disabled';
4737 cfg.cls += ' open x-open';
4740 if (this.glyphicon || this.icon) {
4741 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4742 a.cn.push({ tag : 'i', cls : c }) ;
4745 if(!this.buttonView){
4748 html : this.html || ''
4755 if (this.badge !== '') {
4756 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4762 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4765 a.cls += ' dropdown-toggle treeview' ;
4771 initEvents : function()
4773 if (typeof (this.menu) != 'undefined') {
4774 this.menu.parentType = this.xtype;
4775 this.menu.triggerEl = this.el;
4776 this.menu = this.addxtype(Roo.apply({}, this.menu));
4779 this.el.on('click', this.onClick, this);
4781 if(this.badge !== ''){
4782 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4787 onClick : function(e)
4794 if(this.preventDefault){
4798 this.fireEvent('click', this);
4801 disable : function()
4803 this.setDisabled(true);
4808 this.setDisabled(false);
4811 setDisabled : function(state)
4813 if(this.disabled == state){
4817 this.disabled = state;
4820 this.el.addClass('disabled');
4824 this.el.removeClass('disabled');
4829 setActive : function(state)
4831 if(this.active == state){
4835 this.active = state;
4838 this.el.addClass('active');
4842 this.el.removeClass('active');
4847 isActive: function ()
4852 setBadge : function(str)
4858 this.badgeEl.dom.innerHTML = str;
4875 * @class Roo.bootstrap.Row
4876 * @extends Roo.bootstrap.Component
4877 * Bootstrap Row class (contains columns...)
4881 * @param {Object} config The config object
4884 Roo.bootstrap.Row = function(config){
4885 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4888 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4890 getAutoCreate : function(){
4909 * @class Roo.bootstrap.Element
4910 * @extends Roo.bootstrap.Component
4911 * Bootstrap Element class
4912 * @cfg {String} html contents of the element
4913 * @cfg {String} tag tag of the element
4914 * @cfg {String} cls class of the element
4915 * @cfg {Boolean} preventDefault (true|false) default false
4916 * @cfg {Boolean} clickable (true|false) default false
4919 * Create a new Element
4920 * @param {Object} config The config object
4923 Roo.bootstrap.Element = function(config){
4924 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4930 * When a element is chick
4931 * @param {Roo.bootstrap.Element} this
4932 * @param {Roo.EventObject} e
4938 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4943 preventDefault: false,
4946 getAutoCreate : function(){
4950 // cls: this.cls, double assign in parent class Component.js :: onRender
4957 initEvents: function()
4959 Roo.bootstrap.Element.superclass.initEvents.call(this);
4962 this.el.on('click', this.onClick, this);
4967 onClick : function(e)
4969 if(this.preventDefault){
4973 this.fireEvent('click', this, e);
4976 getValue : function()
4978 return this.el.dom.innerHTML;
4981 setValue : function(value)
4983 this.el.dom.innerHTML = value;
4998 * @class Roo.bootstrap.Pagination
4999 * @extends Roo.bootstrap.Component
5000 * Bootstrap Pagination class
5001 * @cfg {String} size xs | sm | md | lg
5002 * @cfg {Boolean} inverse false | true
5005 * Create a new Pagination
5006 * @param {Object} config The config object
5009 Roo.bootstrap.Pagination = function(config){
5010 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5013 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5019 getAutoCreate : function(){
5025 cfg.cls += ' inverse';
5031 cfg.cls += " " + this.cls;
5049 * @class Roo.bootstrap.PaginationItem
5050 * @extends Roo.bootstrap.Component
5051 * Bootstrap PaginationItem class
5052 * @cfg {String} html text
5053 * @cfg {String} href the link
5054 * @cfg {Boolean} preventDefault (true | false) default true
5055 * @cfg {Boolean} active (true | false) default false
5056 * @cfg {Boolean} disabled default false
5060 * Create a new PaginationItem
5061 * @param {Object} config The config object
5065 Roo.bootstrap.PaginationItem = function(config){
5066 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5071 * The raw click event for the entire grid.
5072 * @param {Roo.EventObject} e
5078 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5082 preventDefault: true,
5087 getAutoCreate : function(){
5093 href : this.href ? this.href : '#',
5094 html : this.html ? this.html : ''
5104 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5108 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5114 initEvents: function() {
5116 this.el.on('click', this.onClick, this);
5119 onClick : function(e)
5121 Roo.log('PaginationItem on click ');
5122 if(this.preventDefault){
5130 this.fireEvent('click', this, e);
5146 * @class Roo.bootstrap.Slider
5147 * @extends Roo.bootstrap.Component
5148 * Bootstrap Slider class
5151 * Create a new Slider
5152 * @param {Object} config The config object
5155 Roo.bootstrap.Slider = function(config){
5156 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5159 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5161 getAutoCreate : function(){
5165 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5169 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5181 * Ext JS Library 1.1.1
5182 * Copyright(c) 2006-2007, Ext JS, LLC.
5184 * Originally Released Under LGPL - original licence link has changed is not relivant.
5187 * <script type="text/javascript">
5192 * @class Roo.grid.ColumnModel
5193 * @extends Roo.util.Observable
5194 * This is the default implementation of a ColumnModel used by the Grid. It defines
5195 * the columns in the grid.
5198 var colModel = new Roo.grid.ColumnModel([
5199 {header: "Ticker", width: 60, sortable: true, locked: true},
5200 {header: "Company Name", width: 150, sortable: true},
5201 {header: "Market Cap.", width: 100, sortable: true},
5202 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5203 {header: "Employees", width: 100, sortable: true, resizable: false}
5208 * The config options listed for this class are options which may appear in each
5209 * individual column definition.
5210 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5212 * @param {Object} config An Array of column config objects. See this class's
5213 * config objects for details.
5215 Roo.grid.ColumnModel = function(config){
5217 * The config passed into the constructor
5219 this.config = config;
5222 // if no id, create one
5223 // if the column does not have a dataIndex mapping,
5224 // map it to the order it is in the config
5225 for(var i = 0, len = config.length; i < len; i++){
5227 if(typeof c.dataIndex == "undefined"){
5230 if(typeof c.renderer == "string"){
5231 c.renderer = Roo.util.Format[c.renderer];
5233 if(typeof c.id == "undefined"){
5236 if(c.editor && c.editor.xtype){
5237 c.editor = Roo.factory(c.editor, Roo.grid);
5239 if(c.editor && c.editor.isFormField){
5240 c.editor = new Roo.grid.GridEditor(c.editor);
5242 this.lookup[c.id] = c;
5246 * The width of columns which have no width specified (defaults to 100)
5249 this.defaultWidth = 100;
5252 * Default sortable of columns which have no sortable specified (defaults to false)
5255 this.defaultSortable = false;
5259 * @event widthchange
5260 * Fires when the width of a column changes.
5261 * @param {ColumnModel} this
5262 * @param {Number} columnIndex The column index
5263 * @param {Number} newWidth The new width
5265 "widthchange": true,
5267 * @event headerchange
5268 * Fires when the text of a header changes.
5269 * @param {ColumnModel} this
5270 * @param {Number} columnIndex The column index
5271 * @param {Number} newText The new header text
5273 "headerchange": true,
5275 * @event hiddenchange
5276 * Fires when a column is hidden or "unhidden".
5277 * @param {ColumnModel} this
5278 * @param {Number} columnIndex The column index
5279 * @param {Boolean} hidden true if hidden, false otherwise
5281 "hiddenchange": true,
5283 * @event columnmoved
5284 * Fires when a column is moved.
5285 * @param {ColumnModel} this
5286 * @param {Number} oldIndex
5287 * @param {Number} newIndex
5289 "columnmoved" : true,
5291 * @event columlockchange
5292 * Fires when a column's locked state is changed
5293 * @param {ColumnModel} this
5294 * @param {Number} colIndex
5295 * @param {Boolean} locked true if locked
5297 "columnlockchange" : true
5299 Roo.grid.ColumnModel.superclass.constructor.call(this);
5301 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5303 * @cfg {String} header The header text to display in the Grid view.
5306 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5307 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5308 * specified, the column's index is used as an index into the Record's data Array.
5311 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5312 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5315 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5316 * Defaults to the value of the {@link #defaultSortable} property.
5317 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5320 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5323 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5326 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5329 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5332 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5333 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5334 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5335 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5338 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5341 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5344 * @cfg {String} cursor (Optional)
5347 * @cfg {String} tooltip (Optional)
5350 * @cfg {Number} xs (Optional)
5353 * @cfg {Number} sm (Optional)
5356 * @cfg {Number} md (Optional)
5359 * @cfg {Number} lg (Optional)
5362 * Returns the id of the column at the specified index.
5363 * @param {Number} index The column index
5364 * @return {String} the id
5366 getColumnId : function(index){
5367 return this.config[index].id;
5371 * Returns the column for a specified id.
5372 * @param {String} id The column id
5373 * @return {Object} the column
5375 getColumnById : function(id){
5376 return this.lookup[id];
5381 * Returns the column for a specified dataIndex.
5382 * @param {String} dataIndex The column dataIndex
5383 * @return {Object|Boolean} the column or false if not found
5385 getColumnByDataIndex: function(dataIndex){
5386 var index = this.findColumnIndex(dataIndex);
5387 return index > -1 ? this.config[index] : false;
5391 * Returns the index for a specified column id.
5392 * @param {String} id The column id
5393 * @return {Number} the index, or -1 if not found
5395 getIndexById : function(id){
5396 for(var i = 0, len = this.config.length; i < len; i++){
5397 if(this.config[i].id == id){
5405 * Returns the index for a specified column dataIndex.
5406 * @param {String} dataIndex The column dataIndex
5407 * @return {Number} the index, or -1 if not found
5410 findColumnIndex : function(dataIndex){
5411 for(var i = 0, len = this.config.length; i < len; i++){
5412 if(this.config[i].dataIndex == dataIndex){
5420 moveColumn : function(oldIndex, newIndex){
5421 var c = this.config[oldIndex];
5422 this.config.splice(oldIndex, 1);
5423 this.config.splice(newIndex, 0, c);
5424 this.dataMap = null;
5425 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5428 isLocked : function(colIndex){
5429 return this.config[colIndex].locked === true;
5432 setLocked : function(colIndex, value, suppressEvent){
5433 if(this.isLocked(colIndex) == value){
5436 this.config[colIndex].locked = value;
5438 this.fireEvent("columnlockchange", this, colIndex, value);
5442 getTotalLockedWidth : function(){
5444 for(var i = 0; i < this.config.length; i++){
5445 if(this.isLocked(i) && !this.isHidden(i)){
5446 this.totalWidth += this.getColumnWidth(i);
5452 getLockedCount : function(){
5453 for(var i = 0, len = this.config.length; i < len; i++){
5454 if(!this.isLocked(i)){
5459 return this.config.length;
5463 * Returns the number of columns.
5466 getColumnCount : function(visibleOnly){
5467 if(visibleOnly === true){
5469 for(var i = 0, len = this.config.length; i < len; i++){
5470 if(!this.isHidden(i)){
5476 return this.config.length;
5480 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5481 * @param {Function} fn
5482 * @param {Object} scope (optional)
5483 * @return {Array} result
5485 getColumnsBy : function(fn, scope){
5487 for(var i = 0, len = this.config.length; i < len; i++){
5488 var c = this.config[i];
5489 if(fn.call(scope||this, c, i) === true){
5497 * Returns true if the specified column is sortable.
5498 * @param {Number} col The column index
5501 isSortable : function(col){
5502 if(typeof this.config[col].sortable == "undefined"){
5503 return this.defaultSortable;
5505 return this.config[col].sortable;
5509 * Returns the rendering (formatting) function defined for the column.
5510 * @param {Number} col The column index.
5511 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5513 getRenderer : function(col){
5514 if(!this.config[col].renderer){
5515 return Roo.grid.ColumnModel.defaultRenderer;
5517 return this.config[col].renderer;
5521 * Sets the rendering (formatting) function for a column.
5522 * @param {Number} col The column index
5523 * @param {Function} fn The function to use to process the cell's raw data
5524 * to return HTML markup for the grid view. The render function is called with
5525 * the following parameters:<ul>
5526 * <li>Data value.</li>
5527 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5528 * <li>css A CSS style string to apply to the table cell.</li>
5529 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5530 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5531 * <li>Row index</li>
5532 * <li>Column index</li>
5533 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5535 setRenderer : function(col, fn){
5536 this.config[col].renderer = fn;
5540 * Returns the width for the specified column.
5541 * @param {Number} col The column index
5544 getColumnWidth : function(col){
5545 return this.config[col].width * 1 || this.defaultWidth;
5549 * Sets the width for a column.
5550 * @param {Number} col The column index
5551 * @param {Number} width The new width
5553 setColumnWidth : function(col, width, suppressEvent){
5554 this.config[col].width = width;
5555 this.totalWidth = null;
5557 this.fireEvent("widthchange", this, col, width);
5562 * Returns the total width of all columns.
5563 * @param {Boolean} includeHidden True to include hidden column widths
5566 getTotalWidth : function(includeHidden){
5567 if(!this.totalWidth){
5568 this.totalWidth = 0;
5569 for(var i = 0, len = this.config.length; i < len; i++){
5570 if(includeHidden || !this.isHidden(i)){
5571 this.totalWidth += this.getColumnWidth(i);
5575 return this.totalWidth;
5579 * Returns the header for the specified column.
5580 * @param {Number} col The column index
5583 getColumnHeader : function(col){
5584 return this.config[col].header;
5588 * Sets the header for a column.
5589 * @param {Number} col The column index
5590 * @param {String} header The new header
5592 setColumnHeader : function(col, header){
5593 this.config[col].header = header;
5594 this.fireEvent("headerchange", this, col, header);
5598 * Returns the tooltip for the specified column.
5599 * @param {Number} col The column index
5602 getColumnTooltip : function(col){
5603 return this.config[col].tooltip;
5606 * Sets the tooltip for a column.
5607 * @param {Number} col The column index
5608 * @param {String} tooltip The new tooltip
5610 setColumnTooltip : function(col, tooltip){
5611 this.config[col].tooltip = tooltip;
5615 * Returns the dataIndex for the specified column.
5616 * @param {Number} col The column index
5619 getDataIndex : function(col){
5620 return this.config[col].dataIndex;
5624 * Sets the dataIndex for a column.
5625 * @param {Number} col The column index
5626 * @param {Number} dataIndex The new dataIndex
5628 setDataIndex : function(col, dataIndex){
5629 this.config[col].dataIndex = dataIndex;
5635 * Returns true if the cell is editable.
5636 * @param {Number} colIndex The column index
5637 * @param {Number} rowIndex The row index - this is nto actually used..?
5640 isCellEditable : function(colIndex, rowIndex){
5641 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5645 * Returns the editor defined for the cell/column.
5646 * return false or null to disable editing.
5647 * @param {Number} colIndex The column index
5648 * @param {Number} rowIndex The row index
5651 getCellEditor : function(colIndex, rowIndex){
5652 return this.config[colIndex].editor;
5656 * Sets if a column is editable.
5657 * @param {Number} col The column index
5658 * @param {Boolean} editable True if the column is editable
5660 setEditable : function(col, editable){
5661 this.config[col].editable = editable;
5666 * Returns true if the column is hidden.
5667 * @param {Number} colIndex The column index
5670 isHidden : function(colIndex){
5671 return this.config[colIndex].hidden;
5676 * Returns true if the column width cannot be changed
5678 isFixed : function(colIndex){
5679 return this.config[colIndex].fixed;
5683 * Returns true if the column can be resized
5686 isResizable : function(colIndex){
5687 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5690 * Sets if a column is hidden.
5691 * @param {Number} colIndex The column index
5692 * @param {Boolean} hidden True if the column is hidden
5694 setHidden : function(colIndex, hidden){
5695 this.config[colIndex].hidden = hidden;
5696 this.totalWidth = null;
5697 this.fireEvent("hiddenchange", this, colIndex, hidden);
5701 * Sets the editor for a column.
5702 * @param {Number} col The column index
5703 * @param {Object} editor The editor object
5705 setEditor : function(col, editor){
5706 this.config[col].editor = editor;
5710 Roo.grid.ColumnModel.defaultRenderer = function(value)
5712 if(typeof value == "object") {
5715 if(typeof value == "string" && value.length < 1){
5719 return String.format("{0}", value);
5722 // Alias for backwards compatibility
5723 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5726 * Ext JS Library 1.1.1
5727 * Copyright(c) 2006-2007, Ext JS, LLC.
5729 * Originally Released Under LGPL - original licence link has changed is not relivant.
5732 * <script type="text/javascript">
5736 * @class Roo.LoadMask
5737 * A simple utility class for generically masking elements while loading data. If the element being masked has
5738 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5739 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5740 * element's UpdateManager load indicator and will be destroyed after the initial load.
5742 * Create a new LoadMask
5743 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5744 * @param {Object} config The config object
5746 Roo.LoadMask = function(el, config){
5747 this.el = Roo.get(el);
5748 Roo.apply(this, config);
5750 this.store.on('beforeload', this.onBeforeLoad, this);
5751 this.store.on('load', this.onLoad, this);
5752 this.store.on('loadexception', this.onLoadException, this);
5753 this.removeMask = false;
5755 var um = this.el.getUpdateManager();
5756 um.showLoadIndicator = false; // disable the default indicator
5757 um.on('beforeupdate', this.onBeforeLoad, this);
5758 um.on('update', this.onLoad, this);
5759 um.on('failure', this.onLoad, this);
5760 this.removeMask = true;
5764 Roo.LoadMask.prototype = {
5766 * @cfg {Boolean} removeMask
5767 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5768 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5772 * The text to display in a centered loading message box (defaults to 'Loading...')
5776 * @cfg {String} msgCls
5777 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5779 msgCls : 'x-mask-loading',
5782 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5788 * Disables the mask to prevent it from being displayed
5790 disable : function(){
5791 this.disabled = true;
5795 * Enables the mask so that it can be displayed
5797 enable : function(){
5798 this.disabled = false;
5801 onLoadException : function()
5805 if (typeof(arguments[3]) != 'undefined') {
5806 Roo.MessageBox.alert("Error loading",arguments[3]);
5810 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5811 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5818 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5823 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5827 onBeforeLoad : function(){
5829 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5834 destroy : function(){
5836 this.store.un('beforeload', this.onBeforeLoad, this);
5837 this.store.un('load', this.onLoad, this);
5838 this.store.un('loadexception', this.onLoadException, this);
5840 var um = this.el.getUpdateManager();
5841 um.un('beforeupdate', this.onBeforeLoad, this);
5842 um.un('update', this.onLoad, this);
5843 um.un('failure', this.onLoad, this);
5854 * @class Roo.bootstrap.Table
5855 * @extends Roo.bootstrap.Component
5856 * Bootstrap Table class
5857 * @cfg {String} cls table class
5858 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5859 * @cfg {String} bgcolor Specifies the background color for a table
5860 * @cfg {Number} border Specifies whether the table cells should have borders or not
5861 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5862 * @cfg {Number} cellspacing Specifies the space between cells
5863 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5864 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5865 * @cfg {String} sortable Specifies that the table should be sortable
5866 * @cfg {String} summary Specifies a summary of the content of a table
5867 * @cfg {Number} width Specifies the width of a table
5868 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5870 * @cfg {boolean} striped Should the rows be alternative striped
5871 * @cfg {boolean} bordered Add borders to the table
5872 * @cfg {boolean} hover Add hover highlighting
5873 * @cfg {boolean} condensed Format condensed
5874 * @cfg {boolean} responsive Format condensed
5875 * @cfg {Boolean} loadMask (true|false) default false
5876 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5877 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5878 * @cfg {Boolean} rowSelection (true|false) default false
5879 * @cfg {Boolean} cellSelection (true|false) default false
5880 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5881 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5882 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5886 * Create a new Table
5887 * @param {Object} config The config object
5890 Roo.bootstrap.Table = function(config){
5891 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5896 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5897 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5898 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5899 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5901 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5903 this.sm.grid = this;
5904 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5905 this.sm = this.selModel;
5906 this.sm.xmodule = this.xmodule || false;
5909 if (this.cm && typeof(this.cm.config) == 'undefined') {
5910 this.colModel = new Roo.grid.ColumnModel(this.cm);
5911 this.cm = this.colModel;
5912 this.cm.xmodule = this.xmodule || false;
5915 this.store= Roo.factory(this.store, Roo.data);
5916 this.ds = this.store;
5917 this.ds.xmodule = this.xmodule || false;
5920 if (this.footer && this.store) {
5921 this.footer.dataSource = this.ds;
5922 this.footer = Roo.factory(this.footer);
5929 * Fires when a cell is clicked
5930 * @param {Roo.bootstrap.Table} this
5931 * @param {Roo.Element} el
5932 * @param {Number} rowIndex
5933 * @param {Number} columnIndex
5934 * @param {Roo.EventObject} e
5938 * @event celldblclick
5939 * Fires when a cell is double clicked
5940 * @param {Roo.bootstrap.Table} this
5941 * @param {Roo.Element} el
5942 * @param {Number} rowIndex
5943 * @param {Number} columnIndex
5944 * @param {Roo.EventObject} e
5946 "celldblclick" : true,
5949 * Fires when a row is clicked
5950 * @param {Roo.bootstrap.Table} this
5951 * @param {Roo.Element} el
5952 * @param {Number} rowIndex
5953 * @param {Roo.EventObject} e
5957 * @event rowdblclick
5958 * Fires when a row is double clicked
5959 * @param {Roo.bootstrap.Table} this
5960 * @param {Roo.Element} el
5961 * @param {Number} rowIndex
5962 * @param {Roo.EventObject} e
5964 "rowdblclick" : true,
5967 * Fires when a mouseover occur
5968 * @param {Roo.bootstrap.Table} this
5969 * @param {Roo.Element} el
5970 * @param {Number} rowIndex
5971 * @param {Number} columnIndex
5972 * @param {Roo.EventObject} e
5977 * Fires when a mouseout occur
5978 * @param {Roo.bootstrap.Table} this
5979 * @param {Roo.Element} el
5980 * @param {Number} rowIndex
5981 * @param {Number} columnIndex
5982 * @param {Roo.EventObject} e
5987 * Fires when a row is rendered, so you can change add a style to it.
5988 * @param {Roo.bootstrap.Table} this
5989 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5993 * @event rowsrendered
5994 * Fires when all the rows have been rendered
5995 * @param {Roo.bootstrap.Table} this
5997 'rowsrendered' : true,
5999 * @event contextmenu
6000 * The raw contextmenu event for the entire grid.
6001 * @param {Roo.EventObject} e
6003 "contextmenu" : true,
6005 * @event rowcontextmenu
6006 * Fires when a row is right clicked
6007 * @param {Roo.bootstrap.Table} this
6008 * @param {Number} rowIndex
6009 * @param {Roo.EventObject} e
6011 "rowcontextmenu" : true,
6013 * @event cellcontextmenu
6014 * Fires when a cell is right clicked
6015 * @param {Roo.bootstrap.Table} this
6016 * @param {Number} rowIndex
6017 * @param {Number} cellIndex
6018 * @param {Roo.EventObject} e
6020 "cellcontextmenu" : true,
6022 * @event headercontextmenu
6023 * Fires when a header is right clicked
6024 * @param {Roo.bootstrap.Table} this
6025 * @param {Number} columnIndex
6026 * @param {Roo.EventObject} e
6028 "headercontextmenu" : true
6032 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6058 rowSelection : false,
6059 cellSelection : false,
6062 // Roo.Element - the tbody
6064 // Roo.Element - thead element
6067 container: false, // used by gridpanel...
6073 getAutoCreate : function()
6075 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6082 if (this.scrollBody) {
6083 cfg.cls += ' table-body-fixed';
6086 cfg.cls += ' table-striped';
6090 cfg.cls += ' table-hover';
6092 if (this.bordered) {
6093 cfg.cls += ' table-bordered';
6095 if (this.condensed) {
6096 cfg.cls += ' table-condensed';
6098 if (this.responsive) {
6099 cfg.cls += ' table-responsive';
6103 cfg.cls+= ' ' +this.cls;
6106 // this lot should be simplifed...
6109 cfg.align=this.align;
6112 cfg.bgcolor=this.bgcolor;
6115 cfg.border=this.border;
6117 if (this.cellpadding) {
6118 cfg.cellpadding=this.cellpadding;
6120 if (this.cellspacing) {
6121 cfg.cellspacing=this.cellspacing;
6124 cfg.frame=this.frame;
6127 cfg.rules=this.rules;
6129 if (this.sortable) {
6130 cfg.sortable=this.sortable;
6133 cfg.summary=this.summary;
6136 cfg.width=this.width;
6139 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6142 if(this.store || this.cm){
6143 if(this.headerShow){
6144 cfg.cn.push(this.renderHeader());
6147 cfg.cn.push(this.renderBody());
6149 if(this.footerShow){
6150 cfg.cn.push(this.renderFooter());
6152 // where does this come from?
6153 //cfg.cls+= ' TableGrid';
6156 return { cn : [ cfg ] };
6159 initEvents : function()
6161 if(!this.store || !this.cm){
6164 if (this.selModel) {
6165 this.selModel.initEvents();
6169 //Roo.log('initEvents with ds!!!!');
6171 this.mainBody = this.el.select('tbody', true).first();
6172 this.mainHead = this.el.select('thead', true).first();
6179 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6180 e.on('click', _this.sort, _this);
6183 this.mainBody.on("click", this.onClick, this);
6184 this.mainBody.on("dblclick", this.onDblClick, this);
6186 // why is this done????? = it breaks dialogs??
6187 //this.parent().el.setStyle('position', 'relative');
6191 this.footer.parentId = this.id;
6192 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6195 this.el.select('tfoot tr td').first().addClass('hide');
6199 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6201 this.store.on('load', this.onLoad, this);
6202 this.store.on('beforeload', this.onBeforeLoad, this);
6203 this.store.on('update', this.onUpdate, this);
6204 this.store.on('add', this.onAdd, this);
6205 this.store.on("clear", this.clear, this);
6207 this.el.on("contextmenu", this.onContextMenu, this);
6209 this.mainBody.on('scroll', this.onBodyScroll, this);
6211 this.cm.on("headerchange", this.onHeaderChange, this);
6213 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6217 onContextMenu : function(e, t)
6219 this.processEvent("contextmenu", e);
6222 processEvent : function(name, e)
6224 if (name != 'touchstart' ) {
6225 this.fireEvent(name, e);
6228 var t = e.getTarget();
6230 var cell = Roo.get(t);
6236 if(cell.findParent('tfoot', false, true)){
6240 if(cell.findParent('thead', false, true)){
6242 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6243 cell = Roo.get(t).findParent('th', false, true);
6245 Roo.log("failed to find th in thead?");
6246 Roo.log(e.getTarget());
6251 var cellIndex = cell.dom.cellIndex;
6253 var ename = name == 'touchstart' ? 'click' : name;
6254 this.fireEvent("header" + ename, this, cellIndex, e);
6259 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6260 cell = Roo.get(t).findParent('td', false, true);
6262 Roo.log("failed to find th in tbody?");
6263 Roo.log(e.getTarget());
6268 var row = cell.findParent('tr', false, true);
6269 var cellIndex = cell.dom.cellIndex;
6270 var rowIndex = row.dom.rowIndex - 1;
6274 this.fireEvent("row" + name, this, rowIndex, e);
6278 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6284 onMouseover : function(e, el)
6286 var cell = Roo.get(el);
6292 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6293 cell = cell.findParent('td', false, true);
6296 var row = cell.findParent('tr', false, true);
6297 var cellIndex = cell.dom.cellIndex;
6298 var rowIndex = row.dom.rowIndex - 1; // start from 0
6300 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6304 onMouseout : function(e, el)
6306 var cell = Roo.get(el);
6312 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6313 cell = cell.findParent('td', false, true);
6316 var row = cell.findParent('tr', false, true);
6317 var cellIndex = cell.dom.cellIndex;
6318 var rowIndex = row.dom.rowIndex - 1; // start from 0
6320 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6324 onClick : function(e, el)
6326 var cell = Roo.get(el);
6328 if(!cell || (!this.cellSelection && !this.rowSelection)){
6332 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6333 cell = cell.findParent('td', false, true);
6336 if(!cell || typeof(cell) == 'undefined'){
6340 var row = cell.findParent('tr', false, true);
6342 if(!row || typeof(row) == 'undefined'){
6346 var cellIndex = cell.dom.cellIndex;
6347 var rowIndex = this.getRowIndex(row);
6349 // why??? - should these not be based on SelectionModel?
6350 if(this.cellSelection){
6351 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6354 if(this.rowSelection){
6355 this.fireEvent('rowclick', this, row, rowIndex, e);
6361 onDblClick : function(e,el)
6363 var cell = Roo.get(el);
6365 if(!cell || (!this.cellSelection && !this.rowSelection)){
6369 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6370 cell = cell.findParent('td', false, true);
6373 if(!cell || typeof(cell) == 'undefined'){
6377 var row = cell.findParent('tr', false, true);
6379 if(!row || typeof(row) == 'undefined'){
6383 var cellIndex = cell.dom.cellIndex;
6384 var rowIndex = this.getRowIndex(row);
6386 if(this.cellSelection){
6387 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6390 if(this.rowSelection){
6391 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6395 sort : function(e,el)
6397 var col = Roo.get(el);
6399 if(!col.hasClass('sortable')){
6403 var sort = col.attr('sort');
6406 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6410 this.store.sortInfo = {field : sort, direction : dir};
6413 Roo.log("calling footer first");
6414 this.footer.onClick('first');
6417 this.store.load({ params : { start : 0 } });
6421 renderHeader : function()
6429 this.totalWidth = 0;
6431 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6433 var config = cm.config[i];
6437 cls : 'x-hcol-' + i,
6439 html: cm.getColumnHeader(i)
6444 if(typeof(config.sortable) != 'undefined' && config.sortable){
6446 c.html = '<i class="glyphicon"></i>' + c.html;
6449 if(typeof(config.lgHeader) != 'undefined'){
6450 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6453 if(typeof(config.mdHeader) != 'undefined'){
6454 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6457 if(typeof(config.smHeader) != 'undefined'){
6458 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6461 if(typeof(config.xsHeader) != 'undefined'){
6462 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6469 if(typeof(config.tooltip) != 'undefined'){
6470 c.tooltip = config.tooltip;
6473 if(typeof(config.colspan) != 'undefined'){
6474 c.colspan = config.colspan;
6477 if(typeof(config.hidden) != 'undefined' && config.hidden){
6478 c.style += ' display:none;';
6481 if(typeof(config.dataIndex) != 'undefined'){
6482 c.sort = config.dataIndex;
6487 if(typeof(config.align) != 'undefined' && config.align.length){
6488 c.style += ' text-align:' + config.align + ';';
6491 if(typeof(config.width) != 'undefined'){
6492 c.style += ' width:' + config.width + 'px;';
6493 this.totalWidth += config.width;
6495 this.totalWidth += 100; // assume minimum of 100 per column?
6498 if(typeof(config.cls) != 'undefined'){
6499 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6502 ['xs','sm','md','lg'].map(function(size){
6504 if(typeof(config[size]) == 'undefined'){
6508 if (!config[size]) { // 0 = hidden
6509 c.cls += ' hidden-' + size;
6513 c.cls += ' col-' + size + '-' + config[size];
6523 renderBody : function()
6533 colspan : this.cm.getColumnCount()
6543 renderFooter : function()
6553 colspan : this.cm.getColumnCount()
6567 // Roo.log('ds onload');
6572 var ds = this.store;
6574 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6575 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6576 if (_this.store.sortInfo) {
6578 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6579 e.select('i', true).addClass(['glyphicon-arrow-up']);
6582 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6583 e.select('i', true).addClass(['glyphicon-arrow-down']);
6588 var tbody = this.mainBody;
6590 if(ds.getCount() > 0){
6591 ds.data.each(function(d,rowIndex){
6592 var row = this.renderRow(cm, ds, rowIndex);
6594 tbody.createChild(row);
6598 if(row.cellObjects.length){
6599 Roo.each(row.cellObjects, function(r){
6600 _this.renderCellObject(r);
6607 Roo.each(this.el.select('tbody td', true).elements, function(e){
6608 e.on('mouseover', _this.onMouseover, _this);
6611 Roo.each(this.el.select('tbody td', true).elements, function(e){
6612 e.on('mouseout', _this.onMouseout, _this);
6614 this.fireEvent('rowsrendered', this);
6615 //if(this.loadMask){
6616 // this.maskEl.hide();
6623 onUpdate : function(ds,record)
6625 this.refreshRow(record);
6629 onRemove : function(ds, record, index, isUpdate){
6630 if(isUpdate !== true){
6631 this.fireEvent("beforerowremoved", this, index, record);
6633 var bt = this.mainBody.dom;
6635 var rows = this.el.select('tbody > tr', true).elements;
6637 if(typeof(rows[index]) != 'undefined'){
6638 bt.removeChild(rows[index].dom);
6641 // if(bt.rows[index]){
6642 // bt.removeChild(bt.rows[index]);
6645 if(isUpdate !== true){
6646 //this.stripeRows(index);
6647 //this.syncRowHeights(index, index);
6649 this.fireEvent("rowremoved", this, index, record);
6653 onAdd : function(ds, records, rowIndex)
6655 //Roo.log('on Add called');
6656 // - note this does not handle multiple adding very well..
6657 var bt = this.mainBody.dom;
6658 for (var i =0 ; i < records.length;i++) {
6659 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6660 //Roo.log(records[i]);
6661 //Roo.log(this.store.getAt(rowIndex+i));
6662 this.insertRow(this.store, rowIndex + i, false);
6669 refreshRow : function(record){
6670 var ds = this.store, index;
6671 if(typeof record == 'number'){
6673 record = ds.getAt(index);
6675 index = ds.indexOf(record);
6677 this.insertRow(ds, index, true);
6679 this.onRemove(ds, record, index+1, true);
6681 //this.syncRowHeights(index, index);
6683 this.fireEvent("rowupdated", this, index, record);
6686 insertRow : function(dm, rowIndex, isUpdate){
6689 this.fireEvent("beforerowsinserted", this, rowIndex);
6691 //var s = this.getScrollState();
6692 var row = this.renderRow(this.cm, this.store, rowIndex);
6693 // insert before rowIndex..
6694 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6698 if(row.cellObjects.length){
6699 Roo.each(row.cellObjects, function(r){
6700 _this.renderCellObject(r);
6705 this.fireEvent("rowsinserted", this, rowIndex);
6706 //this.syncRowHeights(firstRow, lastRow);
6707 //this.stripeRows(firstRow);
6714 getRowDom : function(rowIndex)
6716 var rows = this.el.select('tbody > tr', true).elements;
6718 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6721 // returns the object tree for a tr..
6724 renderRow : function(cm, ds, rowIndex)
6726 var d = ds.getAt(rowIndex);
6730 cls : 'x-row-' + rowIndex,
6734 var cellObjects = [];
6736 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6737 var config = cm.config[i];
6739 var renderer = cm.getRenderer(i);
6743 if(typeof(renderer) !== 'undefined'){
6744 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6746 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6747 // and are rendered into the cells after the row is rendered - using the id for the element.
6749 if(typeof(value) === 'object'){
6759 rowIndex : rowIndex,
6764 this.fireEvent('rowclass', this, rowcfg);
6768 cls : rowcfg.rowClass + ' x-col-' + i,
6770 html: (typeof(value) === 'object') ? '' : value
6777 if(typeof(config.colspan) != 'undefined'){
6778 td.colspan = config.colspan;
6781 if(typeof(config.hidden) != 'undefined' && config.hidden){
6782 td.style += ' display:none;';
6785 if(typeof(config.align) != 'undefined' && config.align.length){
6786 td.style += ' text-align:' + config.align + ';';
6789 if(typeof(config.width) != 'undefined'){
6790 td.style += ' width:' + config.width + 'px;';
6793 if(typeof(config.cursor) != 'undefined'){
6794 td.style += ' cursor:' + config.cursor + ';';
6797 if(typeof(config.cls) != 'undefined'){
6798 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6801 ['xs','sm','md','lg'].map(function(size){
6803 if(typeof(config[size]) == 'undefined'){
6807 if (!config[size]) { // 0 = hidden
6808 td.cls += ' hidden-' + size;
6812 td.cls += ' col-' + size + '-' + config[size];
6820 row.cellObjects = cellObjects;
6828 onBeforeLoad : function()
6830 //Roo.log('ds onBeforeLoad');
6834 //if(this.loadMask){
6835 // this.maskEl.show();
6843 this.el.select('tbody', true).first().dom.innerHTML = '';
6846 * Show or hide a row.
6847 * @param {Number} rowIndex to show or hide
6848 * @param {Boolean} state hide
6850 setRowVisibility : function(rowIndex, state)
6852 var bt = this.mainBody.dom;
6854 var rows = this.el.select('tbody > tr', true).elements;
6856 if(typeof(rows[rowIndex]) == 'undefined'){
6859 rows[rowIndex].dom.style.display = state ? '' : 'none';
6863 getSelectionModel : function(){
6865 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6867 return this.selModel;
6870 * Render the Roo.bootstrap object from renderder
6872 renderCellObject : function(r)
6876 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6878 var t = r.cfg.render(r.container);
6881 Roo.each(r.cfg.cn, function(c){
6883 container: t.getChildContainer(),
6886 _this.renderCellObject(child);
6891 getRowIndex : function(row)
6895 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6906 * Returns the grid's underlying element = used by panel.Grid
6907 * @return {Element} The element
6909 getGridEl : function(){
6913 * Forces a resize - used by panel.Grid
6914 * @return {Element} The element
6916 autoSize : function()
6918 //var ctr = Roo.get(this.container.dom.parentElement);
6919 var ctr = Roo.get(this.el.dom);
6921 var thd = this.getGridEl().select('thead',true).first();
6922 var tbd = this.getGridEl().select('tbody', true).first();
6923 var tfd = this.getGridEl().select('tfoot', true).first();
6925 var cw = ctr.getWidth();
6929 tbd.setSize(ctr.getWidth(),
6930 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6932 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6935 cw = Math.max(cw, this.totalWidth);
6936 this.getGridEl().select('tr',true).setWidth(cw);
6937 // resize 'expandable coloumn?
6939 return; // we doe not have a view in this design..
6942 onBodyScroll: function()
6944 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6946 this.mainHead.setStyle({
6947 'position' : 'relative',
6948 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6954 var scrollHeight = this.mainBody.dom.scrollHeight;
6956 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6958 var height = this.mainBody.getHeight();
6960 if(scrollHeight - height == scrollTop) {
6962 var total = this.ds.getTotalCount();
6964 if(this.footer.cursor + this.footer.pageSize < total){
6966 this.footer.ds.load({
6968 start : this.footer.cursor + this.footer.pageSize,
6969 limit : this.footer.pageSize
6979 onHeaderChange : function()
6981 var header = this.renderHeader();
6982 var table = this.el.select('table', true).first();
6984 this.mainHead.remove();
6985 this.mainHead = table.createChild(header, this.mainBody, false);
6988 onHiddenChange : function(colModel, colIndex, hidden)
6990 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6991 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6993 this.CSS.updateRule(thSelector, "display", "");
6994 this.CSS.updateRule(tdSelector, "display", "");
6997 this.CSS.updateRule(thSelector, "display", "none");
6998 this.CSS.updateRule(tdSelector, "display", "none");
7001 this.onHeaderChange();
7018 * @class Roo.bootstrap.TableCell
7019 * @extends Roo.bootstrap.Component
7020 * Bootstrap TableCell class
7021 * @cfg {String} html cell contain text
7022 * @cfg {String} cls cell class
7023 * @cfg {String} tag cell tag (td|th) default td
7024 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7025 * @cfg {String} align Aligns the content in a cell
7026 * @cfg {String} axis Categorizes cells
7027 * @cfg {String} bgcolor Specifies the background color of a cell
7028 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7029 * @cfg {Number} colspan Specifies the number of columns a cell should span
7030 * @cfg {String} headers Specifies one or more header cells a cell is related to
7031 * @cfg {Number} height Sets the height of a cell
7032 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7033 * @cfg {Number} rowspan Sets the number of rows a cell should span
7034 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7035 * @cfg {String} valign Vertical aligns the content in a cell
7036 * @cfg {Number} width Specifies the width of a cell
7039 * Create a new TableCell
7040 * @param {Object} config The config object
7043 Roo.bootstrap.TableCell = function(config){
7044 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7047 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7067 getAutoCreate : function(){
7068 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7088 cfg.align=this.align
7094 cfg.bgcolor=this.bgcolor
7097 cfg.charoff=this.charoff
7100 cfg.colspan=this.colspan
7103 cfg.headers=this.headers
7106 cfg.height=this.height
7109 cfg.nowrap=this.nowrap
7112 cfg.rowspan=this.rowspan
7115 cfg.scope=this.scope
7118 cfg.valign=this.valign
7121 cfg.width=this.width
7140 * @class Roo.bootstrap.TableRow
7141 * @extends Roo.bootstrap.Component
7142 * Bootstrap TableRow class
7143 * @cfg {String} cls row class
7144 * @cfg {String} align Aligns the content in a table row
7145 * @cfg {String} bgcolor Specifies a background color for a table row
7146 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7147 * @cfg {String} valign Vertical aligns the content in a table row
7150 * Create a new TableRow
7151 * @param {Object} config The config object
7154 Roo.bootstrap.TableRow = function(config){
7155 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7158 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7166 getAutoCreate : function(){
7167 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7177 cfg.align = this.align;
7180 cfg.bgcolor = this.bgcolor;
7183 cfg.charoff = this.charoff;
7186 cfg.valign = this.valign;
7204 * @class Roo.bootstrap.TableBody
7205 * @extends Roo.bootstrap.Component
7206 * Bootstrap TableBody class
7207 * @cfg {String} cls element class
7208 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7209 * @cfg {String} align Aligns the content inside the element
7210 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7211 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7214 * Create a new TableBody
7215 * @param {Object} config The config object
7218 Roo.bootstrap.TableBody = function(config){
7219 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7222 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7230 getAutoCreate : function(){
7231 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7245 cfg.align = this.align;
7248 cfg.charoff = this.charoff;
7251 cfg.valign = this.valign;
7258 // initEvents : function()
7265 // this.store = Roo.factory(this.store, Roo.data);
7266 // this.store.on('load', this.onLoad, this);
7268 // this.store.load();
7272 // onLoad: function ()
7274 // this.fireEvent('load', this);
7284 * Ext JS Library 1.1.1
7285 * Copyright(c) 2006-2007, Ext JS, LLC.
7287 * Originally Released Under LGPL - original licence link has changed is not relivant.
7290 * <script type="text/javascript">
7293 // as we use this in bootstrap.
7294 Roo.namespace('Roo.form');
7296 * @class Roo.form.Action
7297 * Internal Class used to handle form actions
7299 * @param {Roo.form.BasicForm} el The form element or its id
7300 * @param {Object} config Configuration options
7305 // define the action interface
7306 Roo.form.Action = function(form, options){
7308 this.options = options || {};
7311 * Client Validation Failed
7314 Roo.form.Action.CLIENT_INVALID = 'client';
7316 * Server Validation Failed
7319 Roo.form.Action.SERVER_INVALID = 'server';
7321 * Connect to Server Failed
7324 Roo.form.Action.CONNECT_FAILURE = 'connect';
7326 * Reading Data from Server Failed
7329 Roo.form.Action.LOAD_FAILURE = 'load';
7331 Roo.form.Action.prototype = {
7333 failureType : undefined,
7334 response : undefined,
7338 run : function(options){
7343 success : function(response){
7348 handleResponse : function(response){
7352 // default connection failure
7353 failure : function(response){
7355 this.response = response;
7356 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7357 this.form.afterAction(this, false);
7360 processResponse : function(response){
7361 this.response = response;
7362 if(!response.responseText){
7365 this.result = this.handleResponse(response);
7369 // utility functions used internally
7370 getUrl : function(appendParams){
7371 var url = this.options.url || this.form.url || this.form.el.dom.action;
7373 var p = this.getParams();
7375 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7381 getMethod : function(){
7382 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7385 getParams : function(){
7386 var bp = this.form.baseParams;
7387 var p = this.options.params;
7389 if(typeof p == "object"){
7390 p = Roo.urlEncode(Roo.applyIf(p, bp));
7391 }else if(typeof p == 'string' && bp){
7392 p += '&' + Roo.urlEncode(bp);
7395 p = Roo.urlEncode(bp);
7400 createCallback : function(){
7402 success: this.success,
7403 failure: this.failure,
7405 timeout: (this.form.timeout*1000),
7406 upload: this.form.fileUpload ? this.success : undefined
7411 Roo.form.Action.Submit = function(form, options){
7412 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7415 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7418 haveProgress : false,
7419 uploadComplete : false,
7421 // uploadProgress indicator.
7422 uploadProgress : function()
7424 if (!this.form.progressUrl) {
7428 if (!this.haveProgress) {
7429 Roo.MessageBox.progress("Uploading", "Uploading");
7431 if (this.uploadComplete) {
7432 Roo.MessageBox.hide();
7436 this.haveProgress = true;
7438 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7440 var c = new Roo.data.Connection();
7442 url : this.form.progressUrl,
7447 success : function(req){
7448 //console.log(data);
7452 rdata = Roo.decode(req.responseText)
7454 Roo.log("Invalid data from server..");
7458 if (!rdata || !rdata.success) {
7460 Roo.MessageBox.alert(Roo.encode(rdata));
7463 var data = rdata.data;
7465 if (this.uploadComplete) {
7466 Roo.MessageBox.hide();
7471 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7472 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7475 this.uploadProgress.defer(2000,this);
7478 failure: function(data) {
7479 Roo.log('progress url failed ');
7490 // run get Values on the form, so it syncs any secondary forms.
7491 this.form.getValues();
7493 var o = this.options;
7494 var method = this.getMethod();
7495 var isPost = method == 'POST';
7496 if(o.clientValidation === false || this.form.isValid()){
7498 if (this.form.progressUrl) {
7499 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7500 (new Date() * 1) + '' + Math.random());
7505 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7506 form:this.form.el.dom,
7507 url:this.getUrl(!isPost),
7509 params:isPost ? this.getParams() : null,
7510 isUpload: this.form.fileUpload
7513 this.uploadProgress();
7515 }else if (o.clientValidation !== false){ // client validation failed
7516 this.failureType = Roo.form.Action.CLIENT_INVALID;
7517 this.form.afterAction(this, false);
7521 success : function(response)
7523 this.uploadComplete= true;
7524 if (this.haveProgress) {
7525 Roo.MessageBox.hide();
7529 var result = this.processResponse(response);
7530 if(result === true || result.success){
7531 this.form.afterAction(this, true);
7535 this.form.markInvalid(result.errors);
7536 this.failureType = Roo.form.Action.SERVER_INVALID;
7538 this.form.afterAction(this, false);
7540 failure : function(response)
7542 this.uploadComplete= true;
7543 if (this.haveProgress) {
7544 Roo.MessageBox.hide();
7547 this.response = response;
7548 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7549 this.form.afterAction(this, false);
7552 handleResponse : function(response){
7553 if(this.form.errorReader){
7554 var rs = this.form.errorReader.read(response);
7557 for(var i = 0, len = rs.records.length; i < len; i++) {
7558 var r = rs.records[i];
7562 if(errors.length < 1){
7566 success : rs.success,
7572 ret = Roo.decode(response.responseText);
7576 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7586 Roo.form.Action.Load = function(form, options){
7587 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7588 this.reader = this.form.reader;
7591 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7596 Roo.Ajax.request(Roo.apply(
7597 this.createCallback(), {
7598 method:this.getMethod(),
7599 url:this.getUrl(false),
7600 params:this.getParams()
7604 success : function(response){
7606 var result = this.processResponse(response);
7607 if(result === true || !result.success || !result.data){
7608 this.failureType = Roo.form.Action.LOAD_FAILURE;
7609 this.form.afterAction(this, false);
7612 this.form.clearInvalid();
7613 this.form.setValues(result.data);
7614 this.form.afterAction(this, true);
7617 handleResponse : function(response){
7618 if(this.form.reader){
7619 var rs = this.form.reader.read(response);
7620 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7622 success : rs.success,
7626 return Roo.decode(response.responseText);
7630 Roo.form.Action.ACTION_TYPES = {
7631 'load' : Roo.form.Action.Load,
7632 'submit' : Roo.form.Action.Submit
7641 * @class Roo.bootstrap.Form
7642 * @extends Roo.bootstrap.Component
7643 * Bootstrap Form class
7644 * @cfg {String} method GET | POST (default POST)
7645 * @cfg {String} labelAlign top | left (default top)
7646 * @cfg {String} align left | right - for navbars
7647 * @cfg {Boolean} loadMask load mask when submit (default true)
7652 * @param {Object} config The config object
7656 Roo.bootstrap.Form = function(config){
7658 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7660 Roo.bootstrap.Form.popover.apply();
7664 * @event clientvalidation
7665 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7666 * @param {Form} this
7667 * @param {Boolean} valid true if the form has passed client-side validation
7669 clientvalidation: true,
7671 * @event beforeaction
7672 * Fires before any action is performed. Return false to cancel the action.
7673 * @param {Form} this
7674 * @param {Action} action The action to be performed
7678 * @event actionfailed
7679 * Fires when an action fails.
7680 * @param {Form} this
7681 * @param {Action} action The action that failed
7683 actionfailed : true,
7685 * @event actioncomplete
7686 * Fires when an action is completed.
7687 * @param {Form} this
7688 * @param {Action} action The action that completed
7690 actioncomplete : true
7694 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7697 * @cfg {String} method
7698 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7703 * The URL to use for form actions if one isn't supplied in the action options.
7706 * @cfg {Boolean} fileUpload
7707 * Set to true if this form is a file upload.
7711 * @cfg {Object} baseParams
7712 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7716 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7720 * @cfg {Sting} align (left|right) for navbar forms
7725 activeAction : null,
7728 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7729 * element by passing it or its id or mask the form itself by passing in true.
7732 waitMsgTarget : false,
7737 * @cfg {Boolean} errorMask (true|false) default false
7742 * @cfg {Number} maskOffset Default 100
7747 * @cfg {Boolean} maskBody
7751 getAutoCreate : function(){
7755 method : this.method || 'POST',
7756 id : this.id || Roo.id(),
7759 if (this.parent().xtype.match(/^Nav/)) {
7760 cfg.cls = 'navbar-form navbar-' + this.align;
7764 if (this.labelAlign == 'left' ) {
7765 cfg.cls += ' form-horizontal';
7771 initEvents : function()
7773 this.el.on('submit', this.onSubmit, this);
7774 // this was added as random key presses on the form where triggering form submit.
7775 this.el.on('keypress', function(e) {
7776 if (e.getCharCode() != 13) {
7779 // we might need to allow it for textareas.. and some other items.
7780 // check e.getTarget().
7782 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7786 Roo.log("keypress blocked");
7794 onSubmit : function(e){
7799 * Returns true if client-side validation on the form is successful.
7802 isValid : function(){
7803 var items = this.getItems();
7807 items.each(function(f){
7814 if(!target && f.el.isVisible(true)){
7820 if(this.errorMask && !valid){
7821 Roo.bootstrap.Form.popover.mask(this, target);
7828 * Returns true if any fields in this form have changed since their original load.
7831 isDirty : function(){
7833 var items = this.getItems();
7834 items.each(function(f){
7844 * Performs a predefined action (submit or load) or custom actions you define on this form.
7845 * @param {String} actionName The name of the action type
7846 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7847 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7848 * accept other config options):
7850 Property Type Description
7851 ---------------- --------------- ----------------------------------------------------------------------------------
7852 url String The url for the action (defaults to the form's url)
7853 method String The form method to use (defaults to the form's method, or POST if not defined)
7854 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7855 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7856 validate the form on the client (defaults to false)
7858 * @return {BasicForm} this
7860 doAction : function(action, options){
7861 if(typeof action == 'string'){
7862 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7864 if(this.fireEvent('beforeaction', this, action) !== false){
7865 this.beforeAction(action);
7866 action.run.defer(100, action);
7872 beforeAction : function(action){
7873 var o = action.options;
7878 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7880 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7883 // not really supported yet.. ??
7885 //if(this.waitMsgTarget === true){
7886 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7887 //}else if(this.waitMsgTarget){
7888 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7889 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7891 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7897 afterAction : function(action, success){
7898 this.activeAction = null;
7899 var o = action.options;
7904 Roo.get(document.body).unmask();
7910 //if(this.waitMsgTarget === true){
7911 // this.el.unmask();
7912 //}else if(this.waitMsgTarget){
7913 // this.waitMsgTarget.unmask();
7915 // Roo.MessageBox.updateProgress(1);
7916 // Roo.MessageBox.hide();
7923 Roo.callback(o.success, o.scope, [this, action]);
7924 this.fireEvent('actioncomplete', this, action);
7928 // failure condition..
7929 // we have a scenario where updates need confirming.
7930 // eg. if a locking scenario exists..
7931 // we look for { errors : { needs_confirm : true }} in the response.
7933 (typeof(action.result) != 'undefined') &&
7934 (typeof(action.result.errors) != 'undefined') &&
7935 (typeof(action.result.errors.needs_confirm) != 'undefined')
7938 Roo.log("not supported yet");
7941 Roo.MessageBox.confirm(
7942 "Change requires confirmation",
7943 action.result.errorMsg,
7948 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7958 Roo.callback(o.failure, o.scope, [this, action]);
7959 // show an error message if no failed handler is set..
7960 if (!this.hasListener('actionfailed')) {
7961 Roo.log("need to add dialog support");
7963 Roo.MessageBox.alert("Error",
7964 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7965 action.result.errorMsg :
7966 "Saving Failed, please check your entries or try again"
7971 this.fireEvent('actionfailed', this, action);
7976 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7977 * @param {String} id The value to search for
7980 findField : function(id){
7981 var items = this.getItems();
7982 var field = items.get(id);
7984 items.each(function(f){
7985 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7992 return field || null;
7995 * Mark fields in this form invalid in bulk.
7996 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7997 * @return {BasicForm} this
7999 markInvalid : function(errors){
8000 if(errors instanceof Array){
8001 for(var i = 0, len = errors.length; i < len; i++){
8002 var fieldError = errors[i];
8003 var f = this.findField(fieldError.id);
8005 f.markInvalid(fieldError.msg);
8011 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8012 field.markInvalid(errors[id]);
8016 //Roo.each(this.childForms || [], function (f) {
8017 // f.markInvalid(errors);
8024 * Set values for fields in this form in bulk.
8025 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8026 * @return {BasicForm} this
8028 setValues : function(values){
8029 if(values instanceof Array){ // array of objects
8030 for(var i = 0, len = values.length; i < len; i++){
8032 var f = this.findField(v.id);
8034 f.setValue(v.value);
8035 if(this.trackResetOnLoad){
8036 f.originalValue = f.getValue();
8040 }else{ // object hash
8043 if(typeof values[id] != 'function' && (field = this.findField(id))){
8045 if (field.setFromData &&
8047 field.displayField &&
8048 // combos' with local stores can
8049 // be queried via setValue()
8050 // to set their value..
8051 (field.store && !field.store.isLocal)
8055 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8056 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8057 field.setFromData(sd);
8059 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8061 field.setFromData(values);
8064 field.setValue(values[id]);
8068 if(this.trackResetOnLoad){
8069 field.originalValue = field.getValue();
8075 //Roo.each(this.childForms || [], function (f) {
8076 // f.setValues(values);
8083 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8084 * they are returned as an array.
8085 * @param {Boolean} asString
8088 getValues : function(asString){
8089 //if (this.childForms) {
8090 // copy values from the child forms
8091 // Roo.each(this.childForms, function (f) {
8092 // this.setValues(f.getValues());
8098 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8099 if(asString === true){
8102 return Roo.urlDecode(fs);
8106 * Returns the fields in this form as an object with key/value pairs.
8107 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8110 getFieldValues : function(with_hidden)
8112 var items = this.getItems();
8114 items.each(function(f){
8120 var v = f.getValue();
8122 if (f.inputType =='radio') {
8123 if (typeof(ret[f.getName()]) == 'undefined') {
8124 ret[f.getName()] = ''; // empty..
8127 if (!f.el.dom.checked) {
8135 if(f.xtype == 'MoneyField'){
8136 ret[f.currencyName] = f.getCurrency();
8139 // not sure if this supported any more..
8140 if ((typeof(v) == 'object') && f.getRawValue) {
8141 v = f.getRawValue() ; // dates..
8143 // combo boxes where name != hiddenName...
8144 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8145 ret[f.name] = f.getRawValue();
8147 ret[f.getName()] = v;
8154 * Clears all invalid messages in this form.
8155 * @return {BasicForm} this
8157 clearInvalid : function(){
8158 var items = this.getItems();
8160 items.each(function(f){
8169 * @return {BasicForm} this
8172 var items = this.getItems();
8173 items.each(function(f){
8177 Roo.each(this.childForms || [], function (f) {
8185 getItems : function()
8187 var r=new Roo.util.MixedCollection(false, function(o){
8188 return o.id || (o.id = Roo.id());
8190 var iter = function(el) {
8197 Roo.each(el.items,function(e) {
8206 hideFields : function(items)
8208 Roo.each(items, function(i){
8210 var f = this.findField(i);
8216 if(f.xtype == 'DateField'){
8217 f.setVisible(false);
8226 showFields : function(items)
8228 Roo.each(items, function(i){
8230 var f = this.findField(i);
8236 if(f.xtype == 'DateField'){
8248 Roo.apply(Roo.bootstrap.Form, {
8275 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8276 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8277 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8278 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8281 this.maskEl.top.enableDisplayMode("block");
8282 this.maskEl.left.enableDisplayMode("block");
8283 this.maskEl.bottom.enableDisplayMode("block");
8284 this.maskEl.right.enableDisplayMode("block");
8286 this.toolTip = new Roo.bootstrap.Tooltip({
8287 cls : 'roo-form-error-popover',
8289 'left' : ['r-l', [-2,0], 'right'],
8290 'right' : ['l-r', [2,0], 'left'],
8291 'bottom' : ['tl-bl', [0,2], 'top'],
8292 'top' : [ 'bl-tl', [0,-2], 'bottom']
8296 this.toolTip.render(Roo.get(document.body));
8298 this.toolTip.el.enableDisplayMode("block");
8300 Roo.get(document.body).on('click', function(){
8304 Roo.get(document.body).on('touchstart', function(){
8308 this.isApplied = true
8311 mask : function(form, target)
8315 this.target = target;
8317 if(!this.form.errorMask || !target.el){
8321 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8323 Roo.log(scrollable);
8325 var ot = this.target.el.calcOffsetsTo(scrollable);
8327 var scrollTo = ot[1] - this.form.maskOffset;
8329 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8331 scrollable.scrollTo('top', scrollTo);
8333 var box = this.target.el.getBox();
8335 var zIndex = Roo.bootstrap.Modal.zIndex++;
8338 this.maskEl.top.setStyle('position', 'absolute');
8339 this.maskEl.top.setStyle('z-index', zIndex);
8340 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8341 this.maskEl.top.setLeft(0);
8342 this.maskEl.top.setTop(0);
8343 this.maskEl.top.show();
8345 this.maskEl.left.setStyle('position', 'absolute');
8346 this.maskEl.left.setStyle('z-index', zIndex);
8347 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8348 this.maskEl.left.setLeft(0);
8349 this.maskEl.left.setTop(box.y - this.padding);
8350 this.maskEl.left.show();
8352 this.maskEl.bottom.setStyle('position', 'absolute');
8353 this.maskEl.bottom.setStyle('z-index', zIndex);
8354 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8355 this.maskEl.bottom.setLeft(0);
8356 this.maskEl.bottom.setTop(box.bottom + this.padding);
8357 this.maskEl.bottom.show();
8359 this.maskEl.right.setStyle('position', 'absolute');
8360 this.maskEl.right.setStyle('z-index', zIndex);
8361 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8362 this.maskEl.right.setLeft(box.right + this.padding);
8363 this.maskEl.right.setTop(box.y - this.padding);
8364 this.maskEl.right.show();
8366 this.toolTip.bindEl = this.target.el;
8368 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8370 var tip = this.target.blankText;
8372 if(this.target.getValue() !== '' ) {
8374 if (this.target.invalidText.length) {
8375 tip = this.target.invalidText;
8376 } else if (this.target.regexText.length){
8377 tip = this.target.regexText;
8381 this.toolTip.show(tip);
8383 this.intervalID = window.setInterval(function() {
8384 Roo.bootstrap.Form.popover.unmask();
8387 window.onwheel = function(){ return false;};
8389 (function(){ this.isMasked = true; }).defer(500, this);
8395 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8399 this.maskEl.top.setStyle('position', 'absolute');
8400 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8401 this.maskEl.top.hide();
8403 this.maskEl.left.setStyle('position', 'absolute');
8404 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8405 this.maskEl.left.hide();
8407 this.maskEl.bottom.setStyle('position', 'absolute');
8408 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8409 this.maskEl.bottom.hide();
8411 this.maskEl.right.setStyle('position', 'absolute');
8412 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8413 this.maskEl.right.hide();
8415 this.toolTip.hide();
8417 this.toolTip.el.hide();
8419 window.onwheel = function(){ return true;};
8421 if(this.intervalID){
8422 window.clearInterval(this.intervalID);
8423 this.intervalID = false;
8426 this.isMasked = false;
8436 * Ext JS Library 1.1.1
8437 * Copyright(c) 2006-2007, Ext JS, LLC.
8439 * Originally Released Under LGPL - original licence link has changed is not relivant.
8442 * <script type="text/javascript">
8445 * @class Roo.form.VTypes
8446 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8449 Roo.form.VTypes = function(){
8450 // closure these in so they are only created once.
8451 var alpha = /^[a-zA-Z_]+$/;
8452 var alphanum = /^[a-zA-Z0-9_]+$/;
8453 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8454 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8456 // All these messages and functions are configurable
8459 * The function used to validate email addresses
8460 * @param {String} value The email address
8462 'email' : function(v){
8463 return email.test(v);
8466 * The error text to display when the email validation function returns false
8469 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8471 * The keystroke filter mask to be applied on email input
8474 'emailMask' : /[a-z0-9_\.\-@]/i,
8477 * The function used to validate URLs
8478 * @param {String} value The URL
8480 'url' : function(v){
8484 * The error text to display when the url validation function returns false
8487 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8490 * The function used to validate alpha values
8491 * @param {String} value The value
8493 'alpha' : function(v){
8494 return alpha.test(v);
8497 * The error text to display when the alpha validation function returns false
8500 'alphaText' : 'This field should only contain letters and _',
8502 * The keystroke filter mask to be applied on alpha input
8505 'alphaMask' : /[a-z_]/i,
8508 * The function used to validate alphanumeric values
8509 * @param {String} value The value
8511 'alphanum' : function(v){
8512 return alphanum.test(v);
8515 * The error text to display when the alphanumeric validation function returns false
8518 'alphanumText' : 'This field should only contain letters, numbers and _',
8520 * The keystroke filter mask to be applied on alphanumeric input
8523 'alphanumMask' : /[a-z0-9_]/i
8533 * @class Roo.bootstrap.Input
8534 * @extends Roo.bootstrap.Component
8535 * Bootstrap Input class
8536 * @cfg {Boolean} disabled is it disabled
8537 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8538 * @cfg {String} name name of the input
8539 * @cfg {string} fieldLabel - the label associated
8540 * @cfg {string} placeholder - placeholder to put in text.
8541 * @cfg {string} before - input group add on before
8542 * @cfg {string} after - input group add on after
8543 * @cfg {string} size - (lg|sm) or leave empty..
8544 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8545 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8546 * @cfg {Number} md colspan out of 12 for computer-sized screens
8547 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8548 * @cfg {string} value default value of the input
8549 * @cfg {Number} labelWidth set the width of label
8550 * @cfg {Number} labellg set the width of label (1-12)
8551 * @cfg {Number} labelmd set the width of label (1-12)
8552 * @cfg {Number} labelsm set the width of label (1-12)
8553 * @cfg {Number} labelxs set the width of label (1-12)
8554 * @cfg {String} labelAlign (top|left)
8555 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8556 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8557 * @cfg {String} indicatorpos (left|right) default left
8558 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8559 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8561 * @cfg {String} align (left|center|right) Default left
8562 * @cfg {Boolean} forceFeedback (true|false) Default false
8565 * Create a new Input
8566 * @param {Object} config The config object
8569 Roo.bootstrap.Input = function(config){
8571 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8576 * Fires when this field receives input focus.
8577 * @param {Roo.form.Field} this
8582 * Fires when this field loses input focus.
8583 * @param {Roo.form.Field} this
8588 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8589 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8590 * @param {Roo.form.Field} this
8591 * @param {Roo.EventObject} e The event object
8596 * Fires just before the field blurs if the field value has changed.
8597 * @param {Roo.form.Field} this
8598 * @param {Mixed} newValue The new value
8599 * @param {Mixed} oldValue The original value
8604 * Fires after the field has been marked as invalid.
8605 * @param {Roo.form.Field} this
8606 * @param {String} msg The validation message
8611 * Fires after the field has been validated with no errors.
8612 * @param {Roo.form.Field} this
8617 * Fires after the key up
8618 * @param {Roo.form.Field} this
8619 * @param {Roo.EventObject} e The event Object
8625 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8627 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8628 automatic validation (defaults to "keyup").
8630 validationEvent : "keyup",
8632 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8634 validateOnBlur : true,
8636 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8638 validationDelay : 250,
8640 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8642 focusClass : "x-form-focus", // not needed???
8646 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8648 invalidClass : "has-warning",
8651 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8653 validClass : "has-success",
8656 * @cfg {Boolean} hasFeedback (true|false) default true
8661 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8663 invalidFeedbackClass : "glyphicon-warning-sign",
8666 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8668 validFeedbackClass : "glyphicon-ok",
8671 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8673 selectOnFocus : false,
8676 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8680 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8685 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8687 disableKeyFilter : false,
8690 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8694 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8698 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8700 blankText : "Please complete this mandatory field",
8703 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8707 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8709 maxLength : Number.MAX_VALUE,
8711 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8713 minLengthText : "The minimum length for this field is {0}",
8715 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8717 maxLengthText : "The maximum length for this field is {0}",
8721 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8722 * If available, this function will be called only after the basic validators all return true, and will be passed the
8723 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8727 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8728 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8729 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8733 * @cfg {String} regexText -- Depricated - use Invalid Text
8738 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8744 autocomplete: false,
8763 formatedValue : false,
8764 forceFeedback : false,
8766 indicatorpos : 'left',
8776 parentLabelAlign : function()
8779 while (parent.parent()) {
8780 parent = parent.parent();
8781 if (typeof(parent.labelAlign) !='undefined') {
8782 return parent.labelAlign;
8789 getAutoCreate : function()
8791 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8797 if(this.inputType != 'hidden'){
8798 cfg.cls = 'form-group' //input-group
8804 type : this.inputType,
8806 cls : 'form-control',
8807 placeholder : this.placeholder || '',
8808 autocomplete : this.autocomplete || 'new-password'
8811 if(this.capture.length){
8812 input.capture = this.capture;
8815 if(this.accept.length){
8816 input.accept = this.accept + "/*";
8820 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8823 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8824 input.maxLength = this.maxLength;
8827 if (this.disabled) {
8828 input.disabled=true;
8831 if (this.readOnly) {
8832 input.readonly=true;
8836 input.name = this.name;
8840 input.cls += ' input-' + this.size;
8844 ['xs','sm','md','lg'].map(function(size){
8845 if (settings[size]) {
8846 cfg.cls += ' col-' + size + '-' + settings[size];
8850 var inputblock = input;
8854 cls: 'glyphicon form-control-feedback'
8857 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8860 cls : 'has-feedback',
8868 if (this.before || this.after) {
8871 cls : 'input-group',
8875 if (this.before && typeof(this.before) == 'string') {
8877 inputblock.cn.push({
8879 cls : 'roo-input-before input-group-addon',
8883 if (this.before && typeof(this.before) == 'object') {
8884 this.before = Roo.factory(this.before);
8886 inputblock.cn.push({
8888 cls : 'roo-input-before input-group-' +
8889 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8893 inputblock.cn.push(input);
8895 if (this.after && typeof(this.after) == 'string') {
8896 inputblock.cn.push({
8898 cls : 'roo-input-after input-group-addon',
8902 if (this.after && typeof(this.after) == 'object') {
8903 this.after = Roo.factory(this.after);
8905 inputblock.cn.push({
8907 cls : 'roo-input-after input-group-' +
8908 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8912 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8913 inputblock.cls += ' has-feedback';
8914 inputblock.cn.push(feedback);
8918 if (align ==='left' && this.fieldLabel.length) {
8920 cfg.cls += ' roo-form-group-label-left';
8925 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8926 tooltip : 'This field is required'
8931 cls : 'control-label',
8932 html : this.fieldLabel
8943 var labelCfg = cfg.cn[1];
8944 var contentCfg = cfg.cn[2];
8946 if(this.indicatorpos == 'right'){
8951 cls : 'control-label',
8955 html : this.fieldLabel
8959 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8960 tooltip : 'This field is required'
8973 labelCfg = cfg.cn[0];
8974 contentCfg = cfg.cn[1];
8978 if(this.labelWidth > 12){
8979 labelCfg.style = "width: " + this.labelWidth + 'px';
8982 if(this.labelWidth < 13 && this.labelmd == 0){
8983 this.labelmd = this.labelWidth;
8986 if(this.labellg > 0){
8987 labelCfg.cls += ' col-lg-' + this.labellg;
8988 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8991 if(this.labelmd > 0){
8992 labelCfg.cls += ' col-md-' + this.labelmd;
8993 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8996 if(this.labelsm > 0){
8997 labelCfg.cls += ' col-sm-' + this.labelsm;
8998 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9001 if(this.labelxs > 0){
9002 labelCfg.cls += ' col-xs-' + this.labelxs;
9003 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9007 } else if ( this.fieldLabel.length) {
9012 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9013 tooltip : 'This field is required'
9017 //cls : 'input-group-addon',
9018 html : this.fieldLabel
9026 if(this.indicatorpos == 'right'){
9031 //cls : 'input-group-addon',
9032 html : this.fieldLabel
9037 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9038 tooltip : 'This field is required'
9058 if (this.parentType === 'Navbar' && this.parent().bar) {
9059 cfg.cls += ' navbar-form';
9062 if (this.parentType === 'NavGroup') {
9063 cfg.cls += ' navbar-form';
9071 * return the real input element.
9073 inputEl: function ()
9075 return this.el.select('input.form-control',true).first();
9078 tooltipEl : function()
9080 return this.inputEl();
9083 indicatorEl : function()
9085 var indicator = this.el.select('i.roo-required-indicator',true).first();
9095 setDisabled : function(v)
9097 var i = this.inputEl().dom;
9099 i.removeAttribute('disabled');
9103 i.setAttribute('disabled','true');
9105 initEvents : function()
9108 this.inputEl().on("keydown" , this.fireKey, this);
9109 this.inputEl().on("focus", this.onFocus, this);
9110 this.inputEl().on("blur", this.onBlur, this);
9112 this.inputEl().relayEvent('keyup', this);
9114 this.indicator = this.indicatorEl();
9117 this.indicator.addClass('invisible');
9120 // reference to original value for reset
9121 this.originalValue = this.getValue();
9122 //Roo.form.TextField.superclass.initEvents.call(this);
9123 if(this.validationEvent == 'keyup'){
9124 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9125 this.inputEl().on('keyup', this.filterValidation, this);
9127 else if(this.validationEvent !== false){
9128 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9131 if(this.selectOnFocus){
9132 this.on("focus", this.preFocus, this);
9135 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9136 this.inputEl().on("keypress", this.filterKeys, this);
9138 this.inputEl().relayEvent('keypress', this);
9141 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9142 this.el.on("click", this.autoSize, this);
9145 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9146 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9149 if (typeof(this.before) == 'object') {
9150 this.before.render(this.el.select('.roo-input-before',true).first());
9152 if (typeof(this.after) == 'object') {
9153 this.after.render(this.el.select('.roo-input-after',true).first());
9156 this.inputEl().on('change', this.onChange, this);
9159 filterValidation : function(e){
9160 if(!e.isNavKeyPress()){
9161 this.validationTask.delay(this.validationDelay);
9165 * Validates the field value
9166 * @return {Boolean} True if the value is valid, else false
9168 validate : function(){
9169 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9170 if(this.disabled || this.validateValue(this.getRawValue())){
9181 * Validates a value according to the field's validation rules and marks the field as invalid
9182 * if the validation fails
9183 * @param {Mixed} value The value to validate
9184 * @return {Boolean} True if the value is valid, else false
9186 validateValue : function(value)
9188 if(this.getVisibilityEl().hasClass('hidden')){
9192 if(value.length < 1) { // if it's blank
9193 if(this.allowBlank){
9199 if(value.length < this.minLength){
9202 if(value.length > this.maxLength){
9206 var vt = Roo.form.VTypes;
9207 if(!vt[this.vtype](value, this)){
9211 if(typeof this.validator == "function"){
9212 var msg = this.validator(value);
9216 if (typeof(msg) == 'string') {
9217 this.invalidText = msg;
9221 if(this.regex && !this.regex.test(value)){
9229 fireKey : function(e){
9230 //Roo.log('field ' + e.getKey());
9231 if(e.isNavKeyPress()){
9232 this.fireEvent("specialkey", this, e);
9235 focus : function (selectText){
9237 this.inputEl().focus();
9238 if(selectText === true){
9239 this.inputEl().dom.select();
9245 onFocus : function(){
9246 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9247 // this.el.addClass(this.focusClass);
9250 this.hasFocus = true;
9251 this.startValue = this.getValue();
9252 this.fireEvent("focus", this);
9256 beforeBlur : Roo.emptyFn,
9260 onBlur : function(){
9262 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9263 //this.el.removeClass(this.focusClass);
9265 this.hasFocus = false;
9266 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9269 var v = this.getValue();
9270 if(String(v) !== String(this.startValue)){
9271 this.fireEvent('change', this, v, this.startValue);
9273 this.fireEvent("blur", this);
9276 onChange : function(e)
9278 var v = this.getValue();
9279 if(String(v) !== String(this.startValue)){
9280 this.fireEvent('change', this, v, this.startValue);
9286 * Resets the current field value to the originally loaded value and clears any validation messages
9289 this.setValue(this.originalValue);
9293 * Returns the name of the field
9294 * @return {Mixed} name The name field
9296 getName: function(){
9300 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9301 * @return {Mixed} value The field value
9303 getValue : function(){
9305 var v = this.inputEl().getValue();
9310 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9311 * @return {Mixed} value The field value
9313 getRawValue : function(){
9314 var v = this.inputEl().getValue();
9320 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9321 * @param {Mixed} value The value to set
9323 setRawValue : function(v){
9324 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9327 selectText : function(start, end){
9328 var v = this.getRawValue();
9330 start = start === undefined ? 0 : start;
9331 end = end === undefined ? v.length : end;
9332 var d = this.inputEl().dom;
9333 if(d.setSelectionRange){
9334 d.setSelectionRange(start, end);
9335 }else if(d.createTextRange){
9336 var range = d.createTextRange();
9337 range.moveStart("character", start);
9338 range.moveEnd("character", v.length-end);
9345 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9346 * @param {Mixed} value The value to set
9348 setValue : function(v){
9351 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9357 processValue : function(value){
9358 if(this.stripCharsRe){
9359 var newValue = value.replace(this.stripCharsRe, '');
9360 if(newValue !== value){
9361 this.setRawValue(newValue);
9368 preFocus : function(){
9370 if(this.selectOnFocus){
9371 this.inputEl().dom.select();
9374 filterKeys : function(e){
9376 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9379 var c = e.getCharCode(), cc = String.fromCharCode(c);
9380 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9383 if(!this.maskRe.test(cc)){
9388 * Clear any invalid styles/messages for this field
9390 clearInvalid : function(){
9392 if(!this.el || this.preventMark){ // not rendered
9397 this.el.removeClass(this.invalidClass);
9399 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9401 var feedback = this.el.select('.form-control-feedback', true).first();
9404 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9409 this.fireEvent('valid', this);
9413 * Mark this field as valid
9415 markValid : function()
9417 if(!this.el || this.preventMark){ // not rendered...
9421 this.el.removeClass([this.invalidClass, this.validClass]);
9423 var feedback = this.el.select('.form-control-feedback', true).first();
9426 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9430 this.indicator.removeClass('visible');
9431 this.indicator.addClass('invisible');
9438 if(this.allowBlank && !this.getRawValue().length){
9442 this.el.addClass(this.validClass);
9444 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9446 var feedback = this.el.select('.form-control-feedback', true).first();
9449 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9450 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9455 this.fireEvent('valid', this);
9459 * Mark this field as invalid
9460 * @param {String} msg The validation message
9462 markInvalid : function(msg)
9464 if(!this.el || this.preventMark){ // not rendered
9468 this.el.removeClass([this.invalidClass, this.validClass]);
9470 var feedback = this.el.select('.form-control-feedback', true).first();
9473 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9480 if(this.allowBlank && !this.getRawValue().length){
9485 this.indicator.removeClass('invisible');
9486 this.indicator.addClass('visible');
9489 this.el.addClass(this.invalidClass);
9491 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9493 var feedback = this.el.select('.form-control-feedback', true).first();
9496 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9498 if(this.getValue().length || this.forceFeedback){
9499 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9506 this.fireEvent('invalid', this, msg);
9509 SafariOnKeyDown : function(event)
9511 // this is a workaround for a password hang bug on chrome/ webkit.
9512 if (this.inputEl().dom.type != 'password') {
9516 var isSelectAll = false;
9518 if(this.inputEl().dom.selectionEnd > 0){
9519 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9521 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9522 event.preventDefault();
9527 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9529 event.preventDefault();
9530 // this is very hacky as keydown always get's upper case.
9532 var cc = String.fromCharCode(event.getCharCode());
9533 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9537 adjustWidth : function(tag, w){
9538 tag = tag.toLowerCase();
9539 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9540 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9544 if(tag == 'textarea'){
9547 }else if(Roo.isOpera){
9551 if(tag == 'textarea'){
9559 setFieldLabel : function(v)
9566 var ar = this.el.select('label > span',true);
9568 if (ar.elements.length) {
9569 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9570 this.fieldLabel = v;
9574 var br = this.el.select('label',true);
9576 if(br.elements.length) {
9577 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9578 this.fieldLabel = v;
9582 Roo.log('Cannot Found any of label > span || label in input');
9586 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9587 this.fieldLabel = v;
9602 * @class Roo.bootstrap.TextArea
9603 * @extends Roo.bootstrap.Input
9604 * Bootstrap TextArea class
9605 * @cfg {Number} cols Specifies the visible width of a text area
9606 * @cfg {Number} rows Specifies the visible number of lines in a text area
9607 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9608 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9609 * @cfg {string} html text
9612 * Create a new TextArea
9613 * @param {Object} config The config object
9616 Roo.bootstrap.TextArea = function(config){
9617 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9621 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9631 getAutoCreate : function(){
9633 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9639 if(this.inputType != 'hidden'){
9640 cfg.cls = 'form-group' //input-group
9648 value : this.value || '',
9649 html: this.html || '',
9650 cls : 'form-control',
9651 placeholder : this.placeholder || ''
9655 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9656 input.maxLength = this.maxLength;
9660 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9664 input.cols = this.cols;
9667 if (this.readOnly) {
9668 input.readonly = true;
9672 input.name = this.name;
9676 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9680 ['xs','sm','md','lg'].map(function(size){
9681 if (settings[size]) {
9682 cfg.cls += ' col-' + size + '-' + settings[size];
9686 var inputblock = input;
9688 if(this.hasFeedback && !this.allowBlank){
9692 cls: 'glyphicon form-control-feedback'
9696 cls : 'has-feedback',
9705 if (this.before || this.after) {
9708 cls : 'input-group',
9712 inputblock.cn.push({
9714 cls : 'input-group-addon',
9719 inputblock.cn.push(input);
9721 if(this.hasFeedback && !this.allowBlank){
9722 inputblock.cls += ' has-feedback';
9723 inputblock.cn.push(feedback);
9727 inputblock.cn.push({
9729 cls : 'input-group-addon',
9736 if (align ==='left' && this.fieldLabel.length) {
9741 cls : 'control-label',
9742 html : this.fieldLabel
9753 if(this.labelWidth > 12){
9754 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9757 if(this.labelWidth < 13 && this.labelmd == 0){
9758 this.labelmd = this.labelWidth;
9761 if(this.labellg > 0){
9762 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9763 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9766 if(this.labelmd > 0){
9767 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9768 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9771 if(this.labelsm > 0){
9772 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9773 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9776 if(this.labelxs > 0){
9777 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9778 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9781 } else if ( this.fieldLabel.length) {
9786 //cls : 'input-group-addon',
9787 html : this.fieldLabel
9805 if (this.disabled) {
9806 input.disabled=true;
9813 * return the real textarea element.
9815 inputEl: function ()
9817 return this.el.select('textarea.form-control',true).first();
9821 * Clear any invalid styles/messages for this field
9823 clearInvalid : function()
9826 if(!this.el || this.preventMark){ // not rendered
9830 var label = this.el.select('label', true).first();
9831 var icon = this.el.select('i.fa-star', true).first();
9837 this.el.removeClass(this.invalidClass);
9839 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9841 var feedback = this.el.select('.form-control-feedback', true).first();
9844 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9849 this.fireEvent('valid', this);
9853 * Mark this field as valid
9855 markValid : function()
9857 if(!this.el || this.preventMark){ // not rendered
9861 this.el.removeClass([this.invalidClass, this.validClass]);
9863 var feedback = this.el.select('.form-control-feedback', true).first();
9866 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9869 if(this.disabled || this.allowBlank){
9873 var label = this.el.select('label', true).first();
9874 var icon = this.el.select('i.fa-star', true).first();
9880 this.el.addClass(this.validClass);
9882 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9884 var feedback = this.el.select('.form-control-feedback', true).first();
9887 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9888 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9893 this.fireEvent('valid', this);
9897 * Mark this field as invalid
9898 * @param {String} msg The validation message
9900 markInvalid : function(msg)
9902 if(!this.el || this.preventMark){ // not rendered
9906 this.el.removeClass([this.invalidClass, this.validClass]);
9908 var feedback = this.el.select('.form-control-feedback', true).first();
9911 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9914 if(this.disabled || this.allowBlank){
9918 var label = this.el.select('label', true).first();
9919 var icon = this.el.select('i.fa-star', true).first();
9921 if(!this.getValue().length && label && !icon){
9922 this.el.createChild({
9924 cls : 'text-danger fa fa-lg fa-star',
9925 tooltip : 'This field is required',
9926 style : 'margin-right:5px;'
9930 this.el.addClass(this.invalidClass);
9932 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9934 var feedback = this.el.select('.form-control-feedback', true).first();
9937 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9939 if(this.getValue().length || this.forceFeedback){
9940 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9947 this.fireEvent('invalid', this, msg);
9955 * trigger field - base class for combo..
9960 * @class Roo.bootstrap.TriggerField
9961 * @extends Roo.bootstrap.Input
9962 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9963 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9964 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9965 * for which you can provide a custom implementation. For example:
9967 var trigger = new Roo.bootstrap.TriggerField();
9968 trigger.onTriggerClick = myTriggerFn;
9969 trigger.applyTo('my-field');
9972 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9973 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9974 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9975 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9976 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9979 * Create a new TriggerField.
9980 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9981 * to the base TextField)
9983 Roo.bootstrap.TriggerField = function(config){
9984 this.mimicing = false;
9985 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9988 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9990 * @cfg {String} triggerClass A CSS class to apply to the trigger
9993 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9998 * @cfg {Boolean} removable (true|false) special filter default false
10002 /** @cfg {Boolean} grow @hide */
10003 /** @cfg {Number} growMin @hide */
10004 /** @cfg {Number} growMax @hide */
10010 autoSize: Roo.emptyFn,
10014 deferHeight : true,
10017 actionMode : 'wrap',
10022 getAutoCreate : function(){
10024 var align = this.labelAlign || this.parentLabelAlign();
10029 cls: 'form-group' //input-group
10036 type : this.inputType,
10037 cls : 'form-control',
10038 autocomplete: 'new-password',
10039 placeholder : this.placeholder || ''
10043 input.name = this.name;
10046 input.cls += ' input-' + this.size;
10049 if (this.disabled) {
10050 input.disabled=true;
10053 var inputblock = input;
10055 if(this.hasFeedback && !this.allowBlank){
10059 cls: 'glyphicon form-control-feedback'
10062 if(this.removable && !this.editable && !this.tickable){
10064 cls : 'has-feedback',
10070 cls : 'roo-combo-removable-btn close'
10077 cls : 'has-feedback',
10086 if(this.removable && !this.editable && !this.tickable){
10088 cls : 'roo-removable',
10094 cls : 'roo-combo-removable-btn close'
10101 if (this.before || this.after) {
10104 cls : 'input-group',
10108 inputblock.cn.push({
10110 cls : 'input-group-addon',
10115 inputblock.cn.push(input);
10117 if(this.hasFeedback && !this.allowBlank){
10118 inputblock.cls += ' has-feedback';
10119 inputblock.cn.push(feedback);
10123 inputblock.cn.push({
10125 cls : 'input-group-addon',
10138 cls: 'form-hidden-field'
10152 cls: 'form-hidden-field'
10156 cls: 'roo-select2-choices',
10160 cls: 'roo-select2-search-field',
10173 cls: 'roo-select2-container input-group',
10178 // cls: 'typeahead typeahead-long dropdown-menu',
10179 // style: 'display:none'
10184 if(!this.multiple && this.showToggleBtn){
10190 if (this.caret != false) {
10193 cls: 'fa fa-' + this.caret
10200 cls : 'input-group-addon btn dropdown-toggle',
10205 cls: 'combobox-clear',
10219 combobox.cls += ' roo-select2-container-multi';
10222 if (align ==='left' && this.fieldLabel.length) {
10224 cfg.cls += ' roo-form-group-label-left';
10229 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10230 tooltip : 'This field is required'
10235 cls : 'control-label',
10236 html : this.fieldLabel
10248 var labelCfg = cfg.cn[1];
10249 var contentCfg = cfg.cn[2];
10251 if(this.indicatorpos == 'right'){
10256 cls : 'control-label',
10260 html : this.fieldLabel
10264 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10265 tooltip : 'This field is required'
10278 labelCfg = cfg.cn[0];
10279 contentCfg = cfg.cn[1];
10282 if(this.labelWidth > 12){
10283 labelCfg.style = "width: " + this.labelWidth + 'px';
10286 if(this.labelWidth < 13 && this.labelmd == 0){
10287 this.labelmd = this.labelWidth;
10290 if(this.labellg > 0){
10291 labelCfg.cls += ' col-lg-' + this.labellg;
10292 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10295 if(this.labelmd > 0){
10296 labelCfg.cls += ' col-md-' + this.labelmd;
10297 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10300 if(this.labelsm > 0){
10301 labelCfg.cls += ' col-sm-' + this.labelsm;
10302 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10305 if(this.labelxs > 0){
10306 labelCfg.cls += ' col-xs-' + this.labelxs;
10307 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10310 } else if ( this.fieldLabel.length) {
10311 // Roo.log(" label");
10315 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10316 tooltip : 'This field is required'
10320 //cls : 'input-group-addon',
10321 html : this.fieldLabel
10329 if(this.indicatorpos == 'right'){
10337 html : this.fieldLabel
10341 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10342 tooltip : 'This field is required'
10355 // Roo.log(" no label && no align");
10362 ['xs','sm','md','lg'].map(function(size){
10363 if (settings[size]) {
10364 cfg.cls += ' col-' + size + '-' + settings[size];
10375 onResize : function(w, h){
10376 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10377 // if(typeof w == 'number'){
10378 // var x = w - this.trigger.getWidth();
10379 // this.inputEl().setWidth(this.adjustWidth('input', x));
10380 // this.trigger.setStyle('left', x+'px');
10385 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10388 getResizeEl : function(){
10389 return this.inputEl();
10393 getPositionEl : function(){
10394 return this.inputEl();
10398 alignErrorIcon : function(){
10399 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10403 initEvents : function(){
10407 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10408 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10409 if(!this.multiple && this.showToggleBtn){
10410 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10411 if(this.hideTrigger){
10412 this.trigger.setDisplayed(false);
10414 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10418 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10421 if(this.removable && !this.editable && !this.tickable){
10422 var close = this.closeTriggerEl();
10425 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10426 close.on('click', this.removeBtnClick, this, close);
10430 //this.trigger.addClassOnOver('x-form-trigger-over');
10431 //this.trigger.addClassOnClick('x-form-trigger-click');
10434 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10438 closeTriggerEl : function()
10440 var close = this.el.select('.roo-combo-removable-btn', true).first();
10441 return close ? close : false;
10444 removeBtnClick : function(e, h, el)
10446 e.preventDefault();
10448 if(this.fireEvent("remove", this) !== false){
10450 this.fireEvent("afterremove", this)
10454 createList : function()
10456 this.list = Roo.get(document.body).createChild({
10458 cls: 'typeahead typeahead-long dropdown-menu',
10459 style: 'display:none'
10462 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10467 initTrigger : function(){
10472 onDestroy : function(){
10474 this.trigger.removeAllListeners();
10475 // this.trigger.remove();
10478 // this.wrap.remove();
10480 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10484 onFocus : function(){
10485 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10487 if(!this.mimicing){
10488 this.wrap.addClass('x-trigger-wrap-focus');
10489 this.mimicing = true;
10490 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10491 if(this.monitorTab){
10492 this.el.on("keydown", this.checkTab, this);
10499 checkTab : function(e){
10500 if(e.getKey() == e.TAB){
10501 this.triggerBlur();
10506 onBlur : function(){
10511 mimicBlur : function(e, t){
10513 if(!this.wrap.contains(t) && this.validateBlur()){
10514 this.triggerBlur();
10520 triggerBlur : function(){
10521 this.mimicing = false;
10522 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10523 if(this.monitorTab){
10524 this.el.un("keydown", this.checkTab, this);
10526 //this.wrap.removeClass('x-trigger-wrap-focus');
10527 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10531 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10532 validateBlur : function(e, t){
10537 onDisable : function(){
10538 this.inputEl().dom.disabled = true;
10539 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10541 // this.wrap.addClass('x-item-disabled');
10546 onEnable : function(){
10547 this.inputEl().dom.disabled = false;
10548 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10550 // this.el.removeClass('x-item-disabled');
10555 onShow : function(){
10556 var ae = this.getActionEl();
10559 ae.dom.style.display = '';
10560 ae.dom.style.visibility = 'visible';
10566 onHide : function(){
10567 var ae = this.getActionEl();
10568 ae.dom.style.display = 'none';
10572 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10573 * by an implementing function.
10575 * @param {EventObject} e
10577 onTriggerClick : Roo.emptyFn
10581 * Ext JS Library 1.1.1
10582 * Copyright(c) 2006-2007, Ext JS, LLC.
10584 * Originally Released Under LGPL - original licence link has changed is not relivant.
10587 * <script type="text/javascript">
10592 * @class Roo.data.SortTypes
10594 * Defines the default sorting (casting?) comparison functions used when sorting data.
10596 Roo.data.SortTypes = {
10598 * Default sort that does nothing
10599 * @param {Mixed} s The value being converted
10600 * @return {Mixed} The comparison value
10602 none : function(s){
10607 * The regular expression used to strip tags
10611 stripTagsRE : /<\/?[^>]+>/gi,
10614 * Strips all HTML tags to sort on text only
10615 * @param {Mixed} s The value being converted
10616 * @return {String} The comparison value
10618 asText : function(s){
10619 return String(s).replace(this.stripTagsRE, "");
10623 * Strips all HTML tags to sort on text only - Case insensitive
10624 * @param {Mixed} s The value being converted
10625 * @return {String} The comparison value
10627 asUCText : function(s){
10628 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10632 * Case insensitive string
10633 * @param {Mixed} s The value being converted
10634 * @return {String} The comparison value
10636 asUCString : function(s) {
10637 return String(s).toUpperCase();
10642 * @param {Mixed} s The value being converted
10643 * @return {Number} The comparison value
10645 asDate : function(s) {
10649 if(s instanceof Date){
10650 return s.getTime();
10652 return Date.parse(String(s));
10657 * @param {Mixed} s The value being converted
10658 * @return {Float} The comparison value
10660 asFloat : function(s) {
10661 var val = parseFloat(String(s).replace(/,/g, ""));
10670 * @param {Mixed} s The value being converted
10671 * @return {Number} The comparison value
10673 asInt : function(s) {
10674 var val = parseInt(String(s).replace(/,/g, ""));
10682 * Ext JS Library 1.1.1
10683 * Copyright(c) 2006-2007, Ext JS, LLC.
10685 * Originally Released Under LGPL - original licence link has changed is not relivant.
10688 * <script type="text/javascript">
10692 * @class Roo.data.Record
10693 * Instances of this class encapsulate both record <em>definition</em> information, and record
10694 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10695 * to access Records cached in an {@link Roo.data.Store} object.<br>
10697 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10698 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10701 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10703 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10704 * {@link #create}. The parameters are the same.
10705 * @param {Array} data An associative Array of data values keyed by the field name.
10706 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10707 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10708 * not specified an integer id is generated.
10710 Roo.data.Record = function(data, id){
10711 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10716 * Generate a constructor for a specific record layout.
10717 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10718 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10719 * Each field definition object may contain the following properties: <ul>
10720 * <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,
10721 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10722 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10723 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10724 * is being used, then this is a string containing the javascript expression to reference the data relative to
10725 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10726 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10727 * this may be omitted.</p></li>
10728 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10729 * <ul><li>auto (Default, implies no conversion)</li>
10734 * <li>date</li></ul></p></li>
10735 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10736 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10737 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10738 * by the Reader into an object that will be stored in the Record. It is passed the
10739 * following parameters:<ul>
10740 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10742 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10744 * <br>usage:<br><pre><code>
10745 var TopicRecord = Roo.data.Record.create(
10746 {name: 'title', mapping: 'topic_title'},
10747 {name: 'author', mapping: 'username'},
10748 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10749 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10750 {name: 'lastPoster', mapping: 'user2'},
10751 {name: 'excerpt', mapping: 'post_text'}
10754 var myNewRecord = new TopicRecord({
10755 title: 'Do my job please',
10758 lastPost: new Date(),
10759 lastPoster: 'Animal',
10760 excerpt: 'No way dude!'
10762 myStore.add(myNewRecord);
10767 Roo.data.Record.create = function(o){
10768 var f = function(){
10769 f.superclass.constructor.apply(this, arguments);
10771 Roo.extend(f, Roo.data.Record);
10772 var p = f.prototype;
10773 p.fields = new Roo.util.MixedCollection(false, function(field){
10776 for(var i = 0, len = o.length; i < len; i++){
10777 p.fields.add(new Roo.data.Field(o[i]));
10779 f.getField = function(name){
10780 return p.fields.get(name);
10785 Roo.data.Record.AUTO_ID = 1000;
10786 Roo.data.Record.EDIT = 'edit';
10787 Roo.data.Record.REJECT = 'reject';
10788 Roo.data.Record.COMMIT = 'commit';
10790 Roo.data.Record.prototype = {
10792 * Readonly flag - true if this record has been modified.
10801 join : function(store){
10802 this.store = store;
10806 * Set the named field to the specified value.
10807 * @param {String} name The name of the field to set.
10808 * @param {Object} value The value to set the field to.
10810 set : function(name, value){
10811 if(this.data[name] == value){
10815 if(!this.modified){
10816 this.modified = {};
10818 if(typeof this.modified[name] == 'undefined'){
10819 this.modified[name] = this.data[name];
10821 this.data[name] = value;
10822 if(!this.editing && this.store){
10823 this.store.afterEdit(this);
10828 * Get the value of the named field.
10829 * @param {String} name The name of the field to get the value of.
10830 * @return {Object} The value of the field.
10832 get : function(name){
10833 return this.data[name];
10837 beginEdit : function(){
10838 this.editing = true;
10839 this.modified = {};
10843 cancelEdit : function(){
10844 this.editing = false;
10845 delete this.modified;
10849 endEdit : function(){
10850 this.editing = false;
10851 if(this.dirty && this.store){
10852 this.store.afterEdit(this);
10857 * Usually called by the {@link Roo.data.Store} which owns the Record.
10858 * Rejects all changes made to the Record since either creation, or the last commit operation.
10859 * Modified fields are reverted to their original values.
10861 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10862 * of reject operations.
10864 reject : function(){
10865 var m = this.modified;
10867 if(typeof m[n] != "function"){
10868 this.data[n] = m[n];
10871 this.dirty = false;
10872 delete this.modified;
10873 this.editing = false;
10875 this.store.afterReject(this);
10880 * Usually called by the {@link Roo.data.Store} which owns the Record.
10881 * Commits all changes made to the Record since either creation, or the last commit operation.
10883 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10884 * of commit operations.
10886 commit : function(){
10887 this.dirty = false;
10888 delete this.modified;
10889 this.editing = false;
10891 this.store.afterCommit(this);
10896 hasError : function(){
10897 return this.error != null;
10901 clearError : function(){
10906 * Creates a copy of this record.
10907 * @param {String} id (optional) A new record id if you don't want to use this record's id
10910 copy : function(newId) {
10911 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10915 * Ext JS Library 1.1.1
10916 * Copyright(c) 2006-2007, Ext JS, LLC.
10918 * Originally Released Under LGPL - original licence link has changed is not relivant.
10921 * <script type="text/javascript">
10927 * @class Roo.data.Store
10928 * @extends Roo.util.Observable
10929 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10930 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10932 * 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
10933 * has no knowledge of the format of the data returned by the Proxy.<br>
10935 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10936 * instances from the data object. These records are cached and made available through accessor functions.
10938 * Creates a new Store.
10939 * @param {Object} config A config object containing the objects needed for the Store to access data,
10940 * and read the data into Records.
10942 Roo.data.Store = function(config){
10943 this.data = new Roo.util.MixedCollection(false);
10944 this.data.getKey = function(o){
10947 this.baseParams = {};
10949 this.paramNames = {
10954 "multisort" : "_multisort"
10957 if(config && config.data){
10958 this.inlineData = config.data;
10959 delete config.data;
10962 Roo.apply(this, config);
10964 if(this.reader){ // reader passed
10965 this.reader = Roo.factory(this.reader, Roo.data);
10966 this.reader.xmodule = this.xmodule || false;
10967 if(!this.recordType){
10968 this.recordType = this.reader.recordType;
10970 if(this.reader.onMetaChange){
10971 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10975 if(this.recordType){
10976 this.fields = this.recordType.prototype.fields;
10978 this.modified = [];
10982 * @event datachanged
10983 * Fires when the data cache has changed, and a widget which is using this Store
10984 * as a Record cache should refresh its view.
10985 * @param {Store} this
10987 datachanged : true,
10989 * @event metachange
10990 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10991 * @param {Store} this
10992 * @param {Object} meta The JSON metadata
10997 * Fires when Records have been added to the Store
10998 * @param {Store} this
10999 * @param {Roo.data.Record[]} records The array of Records added
11000 * @param {Number} index The index at which the record(s) were added
11005 * Fires when a Record has been removed from the Store
11006 * @param {Store} this
11007 * @param {Roo.data.Record} record The Record that was removed
11008 * @param {Number} index The index at which the record was removed
11013 * Fires when a Record has been updated
11014 * @param {Store} this
11015 * @param {Roo.data.Record} record The Record that was updated
11016 * @param {String} operation The update operation being performed. Value may be one of:
11018 Roo.data.Record.EDIT
11019 Roo.data.Record.REJECT
11020 Roo.data.Record.COMMIT
11026 * Fires when the data cache has been cleared.
11027 * @param {Store} this
11031 * @event beforeload
11032 * Fires before a request is made for a new data object. If the beforeload handler returns false
11033 * the load action will be canceled.
11034 * @param {Store} this
11035 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11039 * @event beforeloadadd
11040 * Fires after a new set of Records has been loaded.
11041 * @param {Store} this
11042 * @param {Roo.data.Record[]} records The Records that were loaded
11043 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11045 beforeloadadd : true,
11048 * Fires after a new set of Records has been loaded, before they are added to the store.
11049 * @param {Store} this
11050 * @param {Roo.data.Record[]} records The Records that were loaded
11051 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11052 * @params {Object} return from reader
11056 * @event loadexception
11057 * Fires if an exception occurs in the Proxy during loading.
11058 * Called with the signature of the Proxy's "loadexception" event.
11059 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11062 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11063 * @param {Object} load options
11064 * @param {Object} jsonData from your request (normally this contains the Exception)
11066 loadexception : true
11070 this.proxy = Roo.factory(this.proxy, Roo.data);
11071 this.proxy.xmodule = this.xmodule || false;
11072 this.relayEvents(this.proxy, ["loadexception"]);
11074 this.sortToggle = {};
11075 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11077 Roo.data.Store.superclass.constructor.call(this);
11079 if(this.inlineData){
11080 this.loadData(this.inlineData);
11081 delete this.inlineData;
11085 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11087 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11088 * without a remote query - used by combo/forms at present.
11092 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11095 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11098 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11099 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11102 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11103 * on any HTTP request
11106 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11109 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11113 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11114 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11116 remoteSort : false,
11119 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11120 * loaded or when a record is removed. (defaults to false).
11122 pruneModifiedRecords : false,
11125 lastOptions : null,
11128 * Add Records to the Store and fires the add event.
11129 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11131 add : function(records){
11132 records = [].concat(records);
11133 for(var i = 0, len = records.length; i < len; i++){
11134 records[i].join(this);
11136 var index = this.data.length;
11137 this.data.addAll(records);
11138 this.fireEvent("add", this, records, index);
11142 * Remove a Record from the Store and fires the remove event.
11143 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11145 remove : function(record){
11146 var index = this.data.indexOf(record);
11147 this.data.removeAt(index);
11149 if(this.pruneModifiedRecords){
11150 this.modified.remove(record);
11152 this.fireEvent("remove", this, record, index);
11156 * Remove all Records from the Store and fires the clear event.
11158 removeAll : function(){
11160 if(this.pruneModifiedRecords){
11161 this.modified = [];
11163 this.fireEvent("clear", this);
11167 * Inserts Records to the Store at the given index and fires the add event.
11168 * @param {Number} index The start index at which to insert the passed Records.
11169 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11171 insert : function(index, records){
11172 records = [].concat(records);
11173 for(var i = 0, len = records.length; i < len; i++){
11174 this.data.insert(index, records[i]);
11175 records[i].join(this);
11177 this.fireEvent("add", this, records, index);
11181 * Get the index within the cache of the passed Record.
11182 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11183 * @return {Number} The index of the passed Record. Returns -1 if not found.
11185 indexOf : function(record){
11186 return this.data.indexOf(record);
11190 * Get the index within the cache of the Record with the passed id.
11191 * @param {String} id The id of the Record to find.
11192 * @return {Number} The index of the Record. Returns -1 if not found.
11194 indexOfId : function(id){
11195 return this.data.indexOfKey(id);
11199 * Get the Record with the specified id.
11200 * @param {String} id The id of the Record to find.
11201 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11203 getById : function(id){
11204 return this.data.key(id);
11208 * Get the Record at the specified index.
11209 * @param {Number} index The index of the Record to find.
11210 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11212 getAt : function(index){
11213 return this.data.itemAt(index);
11217 * Returns a range of Records between specified indices.
11218 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11219 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11220 * @return {Roo.data.Record[]} An array of Records
11222 getRange : function(start, end){
11223 return this.data.getRange(start, end);
11227 storeOptions : function(o){
11228 o = Roo.apply({}, o);
11231 this.lastOptions = o;
11235 * Loads the Record cache from the configured Proxy using the configured Reader.
11237 * If using remote paging, then the first load call must specify the <em>start</em>
11238 * and <em>limit</em> properties in the options.params property to establish the initial
11239 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11241 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11242 * and this call will return before the new data has been loaded. Perform any post-processing
11243 * in a callback function, or in a "load" event handler.</strong>
11245 * @param {Object} options An object containing properties which control loading options:<ul>
11246 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11247 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11248 * passed the following arguments:<ul>
11249 * <li>r : Roo.data.Record[]</li>
11250 * <li>options: Options object from the load call</li>
11251 * <li>success: Boolean success indicator</li></ul></li>
11252 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11253 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11256 load : function(options){
11257 options = options || {};
11258 if(this.fireEvent("beforeload", this, options) !== false){
11259 this.storeOptions(options);
11260 var p = Roo.apply(options.params || {}, this.baseParams);
11261 // if meta was not loaded from remote source.. try requesting it.
11262 if (!this.reader.metaFromRemote) {
11263 p._requestMeta = 1;
11265 if(this.sortInfo && this.remoteSort){
11266 var pn = this.paramNames;
11267 p[pn["sort"]] = this.sortInfo.field;
11268 p[pn["dir"]] = this.sortInfo.direction;
11270 if (this.multiSort) {
11271 var pn = this.paramNames;
11272 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11275 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11280 * Reloads the Record cache from the configured Proxy using the configured Reader and
11281 * the options from the last load operation performed.
11282 * @param {Object} options (optional) An object containing properties which may override the options
11283 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11284 * the most recently used options are reused).
11286 reload : function(options){
11287 this.load(Roo.applyIf(options||{}, this.lastOptions));
11291 // Called as a callback by the Reader during a load operation.
11292 loadRecords : function(o, options, success){
11293 if(!o || success === false){
11294 if(success !== false){
11295 this.fireEvent("load", this, [], options, o);
11297 if(options.callback){
11298 options.callback.call(options.scope || this, [], options, false);
11302 // if data returned failure - throw an exception.
11303 if (o.success === false) {
11304 // show a message if no listener is registered.
11305 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11306 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11308 // loadmask wil be hooked into this..
11309 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11312 var r = o.records, t = o.totalRecords || r.length;
11314 this.fireEvent("beforeloadadd", this, r, options, o);
11316 if(!options || options.add !== true){
11317 if(this.pruneModifiedRecords){
11318 this.modified = [];
11320 for(var i = 0, len = r.length; i < len; i++){
11324 this.data = this.snapshot;
11325 delete this.snapshot;
11328 this.data.addAll(r);
11329 this.totalLength = t;
11331 this.fireEvent("datachanged", this);
11333 this.totalLength = Math.max(t, this.data.length+r.length);
11337 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11339 var e = new Roo.data.Record({});
11341 e.set(this.parent.displayField, this.parent.emptyTitle);
11342 e.set(this.parent.valueField, '');
11347 this.fireEvent("load", this, r, options, o);
11348 if(options.callback){
11349 options.callback.call(options.scope || this, r, options, true);
11355 * Loads data from a passed data block. A Reader which understands the format of the data
11356 * must have been configured in the constructor.
11357 * @param {Object} data The data block from which to read the Records. The format of the data expected
11358 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11359 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11361 loadData : function(o, append){
11362 var r = this.reader.readRecords(o);
11363 this.loadRecords(r, {add: append}, true);
11367 * Gets the number of cached records.
11369 * <em>If using paging, this may not be the total size of the dataset. If the data object
11370 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11371 * the data set size</em>
11373 getCount : function(){
11374 return this.data.length || 0;
11378 * Gets the total number of records in the dataset as returned by the server.
11380 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11381 * the dataset size</em>
11383 getTotalCount : function(){
11384 return this.totalLength || 0;
11388 * Returns the sort state of the Store as an object with two properties:
11390 field {String} The name of the field by which the Records are sorted
11391 direction {String} The sort order, "ASC" or "DESC"
11394 getSortState : function(){
11395 return this.sortInfo;
11399 applySort : function(){
11400 if(this.sortInfo && !this.remoteSort){
11401 var s = this.sortInfo, f = s.field;
11402 var st = this.fields.get(f).sortType;
11403 var fn = function(r1, r2){
11404 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11405 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11407 this.data.sort(s.direction, fn);
11408 if(this.snapshot && this.snapshot != this.data){
11409 this.snapshot.sort(s.direction, fn);
11415 * Sets the default sort column and order to be used by the next load operation.
11416 * @param {String} fieldName The name of the field to sort by.
11417 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11419 setDefaultSort : function(field, dir){
11420 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11424 * Sort the Records.
11425 * If remote sorting is used, the sort is performed on the server, and the cache is
11426 * reloaded. If local sorting is used, the cache is sorted internally.
11427 * @param {String} fieldName The name of the field to sort by.
11428 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11430 sort : function(fieldName, dir){
11431 var f = this.fields.get(fieldName);
11433 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11435 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11436 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11441 this.sortToggle[f.name] = dir;
11442 this.sortInfo = {field: f.name, direction: dir};
11443 if(!this.remoteSort){
11445 this.fireEvent("datachanged", this);
11447 this.load(this.lastOptions);
11452 * Calls the specified function for each of the Records in the cache.
11453 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11454 * Returning <em>false</em> aborts and exits the iteration.
11455 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11457 each : function(fn, scope){
11458 this.data.each(fn, scope);
11462 * Gets all records modified since the last commit. Modified records are persisted across load operations
11463 * (e.g., during paging).
11464 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11466 getModifiedRecords : function(){
11467 return this.modified;
11471 createFilterFn : function(property, value, anyMatch){
11472 if(!value.exec){ // not a regex
11473 value = String(value);
11474 if(value.length == 0){
11477 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11479 return function(r){
11480 return value.test(r.data[property]);
11485 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11486 * @param {String} property A field on your records
11487 * @param {Number} start The record index to start at (defaults to 0)
11488 * @param {Number} end The last record index to include (defaults to length - 1)
11489 * @return {Number} The sum
11491 sum : function(property, start, end){
11492 var rs = this.data.items, v = 0;
11493 start = start || 0;
11494 end = (end || end === 0) ? end : rs.length-1;
11496 for(var i = start; i <= end; i++){
11497 v += (rs[i].data[property] || 0);
11503 * Filter the records by a specified property.
11504 * @param {String} field A field on your records
11505 * @param {String/RegExp} value Either a string that the field
11506 * should start with or a RegExp to test against the field
11507 * @param {Boolean} anyMatch True to match any part not just the beginning
11509 filter : function(property, value, anyMatch){
11510 var fn = this.createFilterFn(property, value, anyMatch);
11511 return fn ? this.filterBy(fn) : this.clearFilter();
11515 * Filter by a function. The specified function will be called with each
11516 * record in this data source. If the function returns true the record is included,
11517 * otherwise it is filtered.
11518 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11519 * @param {Object} scope (optional) The scope of the function (defaults to this)
11521 filterBy : function(fn, scope){
11522 this.snapshot = this.snapshot || this.data;
11523 this.data = this.queryBy(fn, scope||this);
11524 this.fireEvent("datachanged", this);
11528 * Query the records by a specified property.
11529 * @param {String} field A field on your records
11530 * @param {String/RegExp} value Either a string that the field
11531 * should start with or a RegExp to test against the field
11532 * @param {Boolean} anyMatch True to match any part not just the beginning
11533 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11535 query : function(property, value, anyMatch){
11536 var fn = this.createFilterFn(property, value, anyMatch);
11537 return fn ? this.queryBy(fn) : this.data.clone();
11541 * Query by a function. The specified function will be called with each
11542 * record in this data source. If the function returns true the record is included
11544 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11545 * @param {Object} scope (optional) The scope of the function (defaults to this)
11546 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11548 queryBy : function(fn, scope){
11549 var data = this.snapshot || this.data;
11550 return data.filterBy(fn, scope||this);
11554 * Collects unique values for a particular dataIndex from this store.
11555 * @param {String} dataIndex The property to collect
11556 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11557 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11558 * @return {Array} An array of the unique values
11560 collect : function(dataIndex, allowNull, bypassFilter){
11561 var d = (bypassFilter === true && this.snapshot) ?
11562 this.snapshot.items : this.data.items;
11563 var v, sv, r = [], l = {};
11564 for(var i = 0, len = d.length; i < len; i++){
11565 v = d[i].data[dataIndex];
11567 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11576 * Revert to a view of the Record cache with no filtering applied.
11577 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11579 clearFilter : function(suppressEvent){
11580 if(this.snapshot && this.snapshot != this.data){
11581 this.data = this.snapshot;
11582 delete this.snapshot;
11583 if(suppressEvent !== true){
11584 this.fireEvent("datachanged", this);
11590 afterEdit : function(record){
11591 if(this.modified.indexOf(record) == -1){
11592 this.modified.push(record);
11594 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11598 afterReject : function(record){
11599 this.modified.remove(record);
11600 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11604 afterCommit : function(record){
11605 this.modified.remove(record);
11606 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11610 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11611 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11613 commitChanges : function(){
11614 var m = this.modified.slice(0);
11615 this.modified = [];
11616 for(var i = 0, len = m.length; i < len; i++){
11622 * Cancel outstanding changes on all changed records.
11624 rejectChanges : function(){
11625 var m = this.modified.slice(0);
11626 this.modified = [];
11627 for(var i = 0, len = m.length; i < len; i++){
11632 onMetaChange : function(meta, rtype, o){
11633 this.recordType = rtype;
11634 this.fields = rtype.prototype.fields;
11635 delete this.snapshot;
11636 this.sortInfo = meta.sortInfo || this.sortInfo;
11637 this.modified = [];
11638 this.fireEvent('metachange', this, this.reader.meta);
11641 moveIndex : function(data, type)
11643 var index = this.indexOf(data);
11645 var newIndex = index + type;
11649 this.insert(newIndex, data);
11654 * Ext JS Library 1.1.1
11655 * Copyright(c) 2006-2007, Ext JS, LLC.
11657 * Originally Released Under LGPL - original licence link has changed is not relivant.
11660 * <script type="text/javascript">
11664 * @class Roo.data.SimpleStore
11665 * @extends Roo.data.Store
11666 * Small helper class to make creating Stores from Array data easier.
11667 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11668 * @cfg {Array} fields An array of field definition objects, or field name strings.
11669 * @cfg {Array} data The multi-dimensional array of data
11671 * @param {Object} config
11673 Roo.data.SimpleStore = function(config){
11674 Roo.data.SimpleStore.superclass.constructor.call(this, {
11676 reader: new Roo.data.ArrayReader({
11679 Roo.data.Record.create(config.fields)
11681 proxy : new Roo.data.MemoryProxy(config.data)
11685 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11687 * Ext JS Library 1.1.1
11688 * Copyright(c) 2006-2007, Ext JS, LLC.
11690 * Originally Released Under LGPL - original licence link has changed is not relivant.
11693 * <script type="text/javascript">
11698 * @extends Roo.data.Store
11699 * @class Roo.data.JsonStore
11700 * Small helper class to make creating Stores for JSON data easier. <br/>
11702 var store = new Roo.data.JsonStore({
11703 url: 'get-images.php',
11705 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11708 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11709 * JsonReader and HttpProxy (unless inline data is provided).</b>
11710 * @cfg {Array} fields An array of field definition objects, or field name strings.
11712 * @param {Object} config
11714 Roo.data.JsonStore = function(c){
11715 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11716 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11717 reader: new Roo.data.JsonReader(c, c.fields)
11720 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11722 * Ext JS Library 1.1.1
11723 * Copyright(c) 2006-2007, Ext JS, LLC.
11725 * Originally Released Under LGPL - original licence link has changed is not relivant.
11728 * <script type="text/javascript">
11732 Roo.data.Field = function(config){
11733 if(typeof config == "string"){
11734 config = {name: config};
11736 Roo.apply(this, config);
11739 this.type = "auto";
11742 var st = Roo.data.SortTypes;
11743 // named sortTypes are supported, here we look them up
11744 if(typeof this.sortType == "string"){
11745 this.sortType = st[this.sortType];
11748 // set default sortType for strings and dates
11749 if(!this.sortType){
11752 this.sortType = st.asUCString;
11755 this.sortType = st.asDate;
11758 this.sortType = st.none;
11763 var stripRe = /[\$,%]/g;
11765 // prebuilt conversion function for this field, instead of
11766 // switching every time we're reading a value
11768 var cv, dateFormat = this.dateFormat;
11773 cv = function(v){ return v; };
11776 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11780 return v !== undefined && v !== null && v !== '' ?
11781 parseInt(String(v).replace(stripRe, ""), 10) : '';
11786 return v !== undefined && v !== null && v !== '' ?
11787 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11792 cv = function(v){ return v === true || v === "true" || v == 1; };
11799 if(v instanceof Date){
11803 if(dateFormat == "timestamp"){
11804 return new Date(v*1000);
11806 return Date.parseDate(v, dateFormat);
11808 var parsed = Date.parse(v);
11809 return parsed ? new Date(parsed) : null;
11818 Roo.data.Field.prototype = {
11826 * Ext JS Library 1.1.1
11827 * Copyright(c) 2006-2007, Ext JS, LLC.
11829 * Originally Released Under LGPL - original licence link has changed is not relivant.
11832 * <script type="text/javascript">
11835 // Base class for reading structured data from a data source. This class is intended to be
11836 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11839 * @class Roo.data.DataReader
11840 * Base class for reading structured data from a data source. This class is intended to be
11841 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11844 Roo.data.DataReader = function(meta, recordType){
11848 this.recordType = recordType instanceof Array ?
11849 Roo.data.Record.create(recordType) : recordType;
11852 Roo.data.DataReader.prototype = {
11854 * Create an empty record
11855 * @param {Object} data (optional) - overlay some values
11856 * @return {Roo.data.Record} record created.
11858 newRow : function(d) {
11860 this.recordType.prototype.fields.each(function(c) {
11862 case 'int' : da[c.name] = 0; break;
11863 case 'date' : da[c.name] = new Date(); break;
11864 case 'float' : da[c.name] = 0.0; break;
11865 case 'boolean' : da[c.name] = false; break;
11866 default : da[c.name] = ""; break;
11870 return new this.recordType(Roo.apply(da, d));
11875 * Ext JS Library 1.1.1
11876 * Copyright(c) 2006-2007, Ext JS, LLC.
11878 * Originally Released Under LGPL - original licence link has changed is not relivant.
11881 * <script type="text/javascript">
11885 * @class Roo.data.DataProxy
11886 * @extends Roo.data.Observable
11887 * This class is an abstract base class for implementations which provide retrieval of
11888 * unformatted data objects.<br>
11890 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11891 * (of the appropriate type which knows how to parse the data object) to provide a block of
11892 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11894 * Custom implementations must implement the load method as described in
11895 * {@link Roo.data.HttpProxy#load}.
11897 Roo.data.DataProxy = function(){
11900 * @event beforeload
11901 * Fires before a network request is made to retrieve a data object.
11902 * @param {Object} This DataProxy object.
11903 * @param {Object} params The params parameter to the load function.
11908 * Fires before the load method's callback is called.
11909 * @param {Object} This DataProxy object.
11910 * @param {Object} o The data object.
11911 * @param {Object} arg The callback argument object passed to the load function.
11915 * @event loadexception
11916 * Fires if an Exception occurs during data retrieval.
11917 * @param {Object} This DataProxy object.
11918 * @param {Object} o The data object.
11919 * @param {Object} arg The callback argument object passed to the load function.
11920 * @param {Object} e The Exception.
11922 loadexception : true
11924 Roo.data.DataProxy.superclass.constructor.call(this);
11927 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11930 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
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 * @class Roo.data.MemoryProxy
11944 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11945 * to the Reader when its load method is called.
11947 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11949 Roo.data.MemoryProxy = function(data){
11953 Roo.data.MemoryProxy.superclass.constructor.call(this);
11957 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11960 * Load data from the requested source (in this case an in-memory
11961 * data object passed to the constructor), read the data object into
11962 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11963 * process that block using the passed callback.
11964 * @param {Object} params This parameter is not used by the MemoryProxy class.
11965 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11966 * object into a block of Roo.data.Records.
11967 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11968 * The function must be passed <ul>
11969 * <li>The Record block object</li>
11970 * <li>The "arg" argument from the load function</li>
11971 * <li>A boolean success indicator</li>
11973 * @param {Object} scope The scope in which to call the callback
11974 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11976 load : function(params, reader, callback, scope, arg){
11977 params = params || {};
11980 result = reader.readRecords(this.data);
11982 this.fireEvent("loadexception", this, arg, null, e);
11983 callback.call(scope, null, arg, false);
11986 callback.call(scope, result, arg, true);
11990 update : function(params, records){
11995 * Ext JS Library 1.1.1
11996 * Copyright(c) 2006-2007, Ext JS, LLC.
11998 * Originally Released Under LGPL - original licence link has changed is not relivant.
12001 * <script type="text/javascript">
12004 * @class Roo.data.HttpProxy
12005 * @extends Roo.data.DataProxy
12006 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12007 * configured to reference a certain URL.<br><br>
12009 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12010 * from which the running page was served.<br><br>
12012 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12014 * Be aware that to enable the browser to parse an XML document, the server must set
12015 * the Content-Type header in the HTTP response to "text/xml".
12017 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12018 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12019 * will be used to make the request.
12021 Roo.data.HttpProxy = function(conn){
12022 Roo.data.HttpProxy.superclass.constructor.call(this);
12023 // is conn a conn config or a real conn?
12025 this.useAjax = !conn || !conn.events;
12029 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12030 // thse are take from connection...
12033 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12036 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12037 * extra parameters to each request made by this object. (defaults to undefined)
12040 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12041 * to each request made by this object. (defaults to undefined)
12044 * @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)
12047 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12050 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12056 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12060 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12061 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12062 * a finer-grained basis than the DataProxy events.
12064 getConnection : function(){
12065 return this.useAjax ? Roo.Ajax : this.conn;
12069 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12070 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12071 * process that block using the passed callback.
12072 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12073 * for the request to the remote server.
12074 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12075 * object into a block of Roo.data.Records.
12076 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12077 * The function must be passed <ul>
12078 * <li>The Record block object</li>
12079 * <li>The "arg" argument from the load function</li>
12080 * <li>A boolean success indicator</li>
12082 * @param {Object} scope The scope in which to call the callback
12083 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12085 load : function(params, reader, callback, scope, arg){
12086 if(this.fireEvent("beforeload", this, params) !== false){
12088 params : params || {},
12090 callback : callback,
12095 callback : this.loadResponse,
12099 Roo.applyIf(o, this.conn);
12100 if(this.activeRequest){
12101 Roo.Ajax.abort(this.activeRequest);
12103 this.activeRequest = Roo.Ajax.request(o);
12105 this.conn.request(o);
12108 callback.call(scope||this, null, arg, false);
12113 loadResponse : function(o, success, response){
12114 delete this.activeRequest;
12116 this.fireEvent("loadexception", this, o, response);
12117 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12122 result = o.reader.read(response);
12124 this.fireEvent("loadexception", this, o, response, e);
12125 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12129 this.fireEvent("load", this, o, o.request.arg);
12130 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12134 update : function(dataSet){
12139 updateResponse : function(dataSet){
12144 * Ext JS Library 1.1.1
12145 * Copyright(c) 2006-2007, Ext JS, LLC.
12147 * Originally Released Under LGPL - original licence link has changed is not relivant.
12150 * <script type="text/javascript">
12154 * @class Roo.data.ScriptTagProxy
12155 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12156 * other than the originating domain of the running page.<br><br>
12158 * <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
12159 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12161 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12162 * source code that is used as the source inside a <script> tag.<br><br>
12164 * In order for the browser to process the returned data, the server must wrap the data object
12165 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12166 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12167 * depending on whether the callback name was passed:
12170 boolean scriptTag = false;
12171 String cb = request.getParameter("callback");
12174 response.setContentType("text/javascript");
12176 response.setContentType("application/x-json");
12178 Writer out = response.getWriter();
12180 out.write(cb + "(");
12182 out.print(dataBlock.toJsonString());
12189 * @param {Object} config A configuration object.
12191 Roo.data.ScriptTagProxy = function(config){
12192 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12193 Roo.apply(this, config);
12194 this.head = document.getElementsByTagName("head")[0];
12197 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12199 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12201 * @cfg {String} url The URL from which to request the data object.
12204 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12208 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12209 * the server the name of the callback function set up by the load call to process the returned data object.
12210 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12211 * javascript output which calls this named function passing the data object as its only parameter.
12213 callbackParam : "callback",
12215 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12216 * name to the request.
12221 * Load data from the configured URL, read the data object into
12222 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12223 * process that block using the passed callback.
12224 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12225 * for the request to the remote server.
12226 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12227 * object into a block of Roo.data.Records.
12228 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12229 * The function must be passed <ul>
12230 * <li>The Record block object</li>
12231 * <li>The "arg" argument from the load function</li>
12232 * <li>A boolean success indicator</li>
12234 * @param {Object} scope The scope in which to call the callback
12235 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12237 load : function(params, reader, callback, scope, arg){
12238 if(this.fireEvent("beforeload", this, params) !== false){
12240 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12242 var url = this.url;
12243 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12245 url += "&_dc=" + (new Date().getTime());
12247 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12250 cb : "stcCallback"+transId,
12251 scriptId : "stcScript"+transId,
12255 callback : callback,
12261 window[trans.cb] = function(o){
12262 conn.handleResponse(o, trans);
12265 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12267 if(this.autoAbort !== false){
12271 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12273 var script = document.createElement("script");
12274 script.setAttribute("src", url);
12275 script.setAttribute("type", "text/javascript");
12276 script.setAttribute("id", trans.scriptId);
12277 this.head.appendChild(script);
12279 this.trans = trans;
12281 callback.call(scope||this, null, arg, false);
12286 isLoading : function(){
12287 return this.trans ? true : false;
12291 * Abort the current server request.
12293 abort : function(){
12294 if(this.isLoading()){
12295 this.destroyTrans(this.trans);
12300 destroyTrans : function(trans, isLoaded){
12301 this.head.removeChild(document.getElementById(trans.scriptId));
12302 clearTimeout(trans.timeoutId);
12304 window[trans.cb] = undefined;
12306 delete window[trans.cb];
12309 // if hasn't been loaded, wait for load to remove it to prevent script error
12310 window[trans.cb] = function(){
12311 window[trans.cb] = undefined;
12313 delete window[trans.cb];
12320 handleResponse : function(o, trans){
12321 this.trans = false;
12322 this.destroyTrans(trans, true);
12325 result = trans.reader.readRecords(o);
12327 this.fireEvent("loadexception", this, o, trans.arg, e);
12328 trans.callback.call(trans.scope||window, null, trans.arg, false);
12331 this.fireEvent("load", this, o, trans.arg);
12332 trans.callback.call(trans.scope||window, result, trans.arg, true);
12336 handleFailure : function(trans){
12337 this.trans = false;
12338 this.destroyTrans(trans, false);
12339 this.fireEvent("loadexception", this, null, trans.arg);
12340 trans.callback.call(trans.scope||window, null, trans.arg, false);
12344 * Ext JS Library 1.1.1
12345 * Copyright(c) 2006-2007, Ext JS, LLC.
12347 * Originally Released Under LGPL - original licence link has changed is not relivant.
12350 * <script type="text/javascript">
12354 * @class Roo.data.JsonReader
12355 * @extends Roo.data.DataReader
12356 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12357 * based on mappings in a provided Roo.data.Record constructor.
12359 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12360 * in the reply previously.
12365 var RecordDef = Roo.data.Record.create([
12366 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12367 {name: 'occupation'} // This field will use "occupation" as the mapping.
12369 var myReader = new Roo.data.JsonReader({
12370 totalProperty: "results", // The property which contains the total dataset size (optional)
12371 root: "rows", // The property which contains an Array of row objects
12372 id: "id" // The property within each row object that provides an ID for the record (optional)
12376 * This would consume a JSON file like this:
12378 { 'results': 2, 'rows': [
12379 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12380 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12383 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12384 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12385 * paged from the remote server.
12386 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12387 * @cfg {String} root name of the property which contains the Array of row objects.
12388 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12389 * @cfg {Array} fields Array of field definition objects
12391 * Create a new JsonReader
12392 * @param {Object} meta Metadata configuration options
12393 * @param {Object} recordType Either an Array of field definition objects,
12394 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12396 Roo.data.JsonReader = function(meta, recordType){
12399 // set some defaults:
12400 Roo.applyIf(meta, {
12401 totalProperty: 'total',
12402 successProperty : 'success',
12407 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12409 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12412 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12413 * Used by Store query builder to append _requestMeta to params.
12416 metaFromRemote : false,
12418 * This method is only used by a DataProxy which has retrieved data from a remote server.
12419 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12420 * @return {Object} data A data block which is used by an Roo.data.Store object as
12421 * a cache of Roo.data.Records.
12423 read : function(response){
12424 var json = response.responseText;
12426 var o = /* eval:var:o */ eval("("+json+")");
12428 throw {message: "JsonReader.read: Json object not found"};
12434 this.metaFromRemote = true;
12435 this.meta = o.metaData;
12436 this.recordType = Roo.data.Record.create(o.metaData.fields);
12437 this.onMetaChange(this.meta, this.recordType, o);
12439 return this.readRecords(o);
12442 // private function a store will implement
12443 onMetaChange : function(meta, recordType, o){
12450 simpleAccess: function(obj, subsc) {
12457 getJsonAccessor: function(){
12459 return function(expr) {
12461 return(re.test(expr))
12462 ? new Function("obj", "return obj." + expr)
12467 return Roo.emptyFn;
12472 * Create a data block containing Roo.data.Records from an XML document.
12473 * @param {Object} o An object which contains an Array of row objects in the property specified
12474 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12475 * which contains the total size of the dataset.
12476 * @return {Object} data A data block which is used by an Roo.data.Store object as
12477 * a cache of Roo.data.Records.
12479 readRecords : function(o){
12481 * After any data loads, the raw JSON data is available for further custom processing.
12485 var s = this.meta, Record = this.recordType,
12486 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12488 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12490 if(s.totalProperty) {
12491 this.getTotal = this.getJsonAccessor(s.totalProperty);
12493 if(s.successProperty) {
12494 this.getSuccess = this.getJsonAccessor(s.successProperty);
12496 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12498 var g = this.getJsonAccessor(s.id);
12499 this.getId = function(rec) {
12501 return (r === undefined || r === "") ? null : r;
12504 this.getId = function(){return null;};
12507 for(var jj = 0; jj < fl; jj++){
12509 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12510 this.ef[jj] = this.getJsonAccessor(map);
12514 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12515 if(s.totalProperty){
12516 var vt = parseInt(this.getTotal(o), 10);
12521 if(s.successProperty){
12522 var vs = this.getSuccess(o);
12523 if(vs === false || vs === 'false'){
12528 for(var i = 0; i < c; i++){
12531 var id = this.getId(n);
12532 for(var j = 0; j < fl; j++){
12534 var v = this.ef[j](n);
12536 Roo.log('missing convert for ' + f.name);
12540 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12542 var record = new Record(values, id);
12544 records[i] = record;
12550 totalRecords : totalRecords
12555 * Ext JS Library 1.1.1
12556 * Copyright(c) 2006-2007, Ext JS, LLC.
12558 * Originally Released Under LGPL - original licence link has changed is not relivant.
12561 * <script type="text/javascript">
12565 * @class Roo.data.ArrayReader
12566 * @extends Roo.data.DataReader
12567 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12568 * Each element of that Array represents a row of data fields. The
12569 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12570 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12574 var RecordDef = Roo.data.Record.create([
12575 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12576 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12578 var myReader = new Roo.data.ArrayReader({
12579 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12583 * This would consume an Array like this:
12585 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12587 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12589 * Create a new JsonReader
12590 * @param {Object} meta Metadata configuration options.
12591 * @param {Object} recordType Either an Array of field definition objects
12592 * as specified to {@link Roo.data.Record#create},
12593 * or an {@link Roo.data.Record} object
12594 * created using {@link Roo.data.Record#create}.
12596 Roo.data.ArrayReader = function(meta, recordType){
12597 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12600 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12602 * Create a data block containing Roo.data.Records from an XML document.
12603 * @param {Object} o An Array of row objects which represents the dataset.
12604 * @return {Object} data A data block which is used by an Roo.data.Store object as
12605 * a cache of Roo.data.Records.
12607 readRecords : function(o){
12608 var sid = this.meta ? this.meta.id : null;
12609 var recordType = this.recordType, fields = recordType.prototype.fields;
12612 for(var i = 0; i < root.length; i++){
12615 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12616 for(var j = 0, jlen = fields.length; j < jlen; j++){
12617 var f = fields.items[j];
12618 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12619 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12621 values[f.name] = v;
12623 var record = new recordType(values, id);
12625 records[records.length] = record;
12629 totalRecords : records.length
12638 * @class Roo.bootstrap.ComboBox
12639 * @extends Roo.bootstrap.TriggerField
12640 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12641 * @cfg {Boolean} append (true|false) default false
12642 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12643 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12644 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12645 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12646 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12647 * @cfg {Boolean} animate default true
12648 * @cfg {Boolean} emptyResultText only for touch device
12649 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12650 * @cfg {String} emptyTitle default ''
12652 * Create a new ComboBox.
12653 * @param {Object} config Configuration options
12655 Roo.bootstrap.ComboBox = function(config){
12656 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12660 * Fires when the dropdown list is expanded
12661 * @param {Roo.bootstrap.ComboBox} combo This combo box
12666 * Fires when the dropdown list is collapsed
12667 * @param {Roo.bootstrap.ComboBox} combo This combo box
12671 * @event beforeselect
12672 * Fires before a list item is selected. Return false to cancel the selection.
12673 * @param {Roo.bootstrap.ComboBox} combo This combo box
12674 * @param {Roo.data.Record} record The data record returned from the underlying store
12675 * @param {Number} index The index of the selected item in the dropdown list
12677 'beforeselect' : true,
12680 * Fires when a list item is selected
12681 * @param {Roo.bootstrap.ComboBox} combo This combo box
12682 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12683 * @param {Number} index The index of the selected item in the dropdown list
12687 * @event beforequery
12688 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12689 * The event object passed has these properties:
12690 * @param {Roo.bootstrap.ComboBox} combo This combo box
12691 * @param {String} query The query
12692 * @param {Boolean} forceAll true to force "all" query
12693 * @param {Boolean} cancel true to cancel the query
12694 * @param {Object} e The query event object
12696 'beforequery': true,
12699 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12700 * @param {Roo.bootstrap.ComboBox} combo This combo box
12705 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12706 * @param {Roo.bootstrap.ComboBox} combo This combo box
12707 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12712 * Fires when the remove value from the combobox array
12713 * @param {Roo.bootstrap.ComboBox} combo This combo box
12717 * @event afterremove
12718 * Fires when the remove value from the combobox array
12719 * @param {Roo.bootstrap.ComboBox} combo This combo box
12721 'afterremove' : true,
12723 * @event specialfilter
12724 * Fires when specialfilter
12725 * @param {Roo.bootstrap.ComboBox} combo This combo box
12727 'specialfilter' : true,
12730 * Fires when tick the element
12731 * @param {Roo.bootstrap.ComboBox} combo This combo box
12735 * @event touchviewdisplay
12736 * Fires when touch view require special display (default is using displayField)
12737 * @param {Roo.bootstrap.ComboBox} combo This combo box
12738 * @param {Object} cfg set html .
12740 'touchviewdisplay' : true
12745 this.tickItems = [];
12747 this.selectedIndex = -1;
12748 if(this.mode == 'local'){
12749 if(config.queryDelay === undefined){
12750 this.queryDelay = 10;
12752 if(config.minChars === undefined){
12758 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12761 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12762 * rendering into an Roo.Editor, defaults to false)
12765 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12766 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12769 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12772 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12773 * the dropdown list (defaults to undefined, with no header element)
12777 * @cfg {String/Roo.Template} tpl The template to use to render the output
12781 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12783 listWidth: undefined,
12785 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12786 * mode = 'remote' or 'text' if mode = 'local')
12788 displayField: undefined,
12791 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12792 * mode = 'remote' or 'value' if mode = 'local').
12793 * Note: use of a valueField requires the user make a selection
12794 * in order for a value to be mapped.
12796 valueField: undefined,
12798 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12803 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12804 * field's data value (defaults to the underlying DOM element's name)
12806 hiddenName: undefined,
12808 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12812 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12814 selectedClass: 'active',
12817 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12821 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12822 * anchor positions (defaults to 'tl-bl')
12824 listAlign: 'tl-bl?',
12826 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12830 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12831 * query specified by the allQuery config option (defaults to 'query')
12833 triggerAction: 'query',
12835 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12836 * (defaults to 4, does not apply if editable = false)
12840 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12841 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12845 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12846 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12850 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12851 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12855 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12856 * when editable = true (defaults to false)
12858 selectOnFocus:false,
12860 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12862 queryParam: 'query',
12864 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12865 * when mode = 'remote' (defaults to 'Loading...')
12867 loadingText: 'Loading...',
12869 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12873 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12877 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12878 * traditional select (defaults to true)
12882 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12886 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12890 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12891 * listWidth has a higher value)
12895 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12896 * allow the user to set arbitrary text into the field (defaults to false)
12898 forceSelection:false,
12900 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12901 * if typeAhead = true (defaults to 250)
12903 typeAheadDelay : 250,
12905 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12906 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12908 valueNotFoundText : undefined,
12910 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12912 blockFocus : false,
12915 * @cfg {Boolean} disableClear Disable showing of clear button.
12917 disableClear : false,
12919 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12921 alwaysQuery : false,
12924 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12929 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12931 invalidClass : "has-warning",
12934 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12936 validClass : "has-success",
12939 * @cfg {Boolean} specialFilter (true|false) special filter default false
12941 specialFilter : false,
12944 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12946 mobileTouchView : true,
12949 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12951 useNativeIOS : false,
12953 ios_options : false,
12965 btnPosition : 'right',
12966 triggerList : true,
12967 showToggleBtn : true,
12969 emptyResultText: 'Empty',
12970 triggerText : 'Select',
12973 // element that contains real text value.. (when hidden is used..)
12975 getAutoCreate : function()
12980 * Render classic select for iso
12983 if(Roo.isIOS && this.useNativeIOS){
12984 cfg = this.getAutoCreateNativeIOS();
12992 if(Roo.isTouch && this.mobileTouchView){
12993 cfg = this.getAutoCreateTouchView();
13000 if(!this.tickable){
13001 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13006 * ComboBox with tickable selections
13009 var align = this.labelAlign || this.parentLabelAlign();
13012 cls : 'form-group roo-combobox-tickable' //input-group
13015 var btn_text_select = '';
13016 var btn_text_done = '';
13017 var btn_text_cancel = '';
13019 if (this.btn_text_show) {
13020 btn_text_select = 'Select';
13021 btn_text_done = 'Done';
13022 btn_text_cancel = 'Cancel';
13027 cls : 'tickable-buttons',
13032 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13033 //html : this.triggerText
13034 html: btn_text_select
13040 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13042 html: btn_text_done
13048 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13050 html: btn_text_cancel
13056 buttons.cn.unshift({
13058 cls: 'roo-select2-search-field-input'
13064 Roo.each(buttons.cn, function(c){
13066 c.cls += ' btn-' + _this.size;
13069 if (_this.disabled) {
13080 cls: 'form-hidden-field'
13084 cls: 'roo-select2-choices',
13088 cls: 'roo-select2-search-field',
13099 cls: 'roo-select2-container input-group roo-select2-container-multi',
13104 // cls: 'typeahead typeahead-long dropdown-menu',
13105 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13110 if(this.hasFeedback && !this.allowBlank){
13114 cls: 'glyphicon form-control-feedback'
13117 combobox.cn.push(feedback);
13121 if (align ==='left' && this.fieldLabel.length) {
13123 cfg.cls += ' roo-form-group-label-left';
13128 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13129 tooltip : 'This field is required'
13134 cls : 'control-label',
13135 html : this.fieldLabel
13147 var labelCfg = cfg.cn[1];
13148 var contentCfg = cfg.cn[2];
13151 if(this.indicatorpos == 'right'){
13157 cls : 'control-label',
13161 html : this.fieldLabel
13165 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13166 tooltip : 'This field is required'
13181 labelCfg = cfg.cn[0];
13182 contentCfg = cfg.cn[1];
13186 if(this.labelWidth > 12){
13187 labelCfg.style = "width: " + this.labelWidth + 'px';
13190 if(this.labelWidth < 13 && this.labelmd == 0){
13191 this.labelmd = this.labelWidth;
13194 if(this.labellg > 0){
13195 labelCfg.cls += ' col-lg-' + this.labellg;
13196 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13199 if(this.labelmd > 0){
13200 labelCfg.cls += ' col-md-' + this.labelmd;
13201 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13204 if(this.labelsm > 0){
13205 labelCfg.cls += ' col-sm-' + this.labelsm;
13206 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13209 if(this.labelxs > 0){
13210 labelCfg.cls += ' col-xs-' + this.labelxs;
13211 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13215 } else if ( this.fieldLabel.length) {
13216 // Roo.log(" label");
13220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13221 tooltip : 'This field is required'
13225 //cls : 'input-group-addon',
13226 html : this.fieldLabel
13231 if(this.indicatorpos == 'right'){
13235 //cls : 'input-group-addon',
13236 html : this.fieldLabel
13240 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13241 tooltip : 'This field is required'
13250 // Roo.log(" no label && no align");
13257 ['xs','sm','md','lg'].map(function(size){
13258 if (settings[size]) {
13259 cfg.cls += ' col-' + size + '-' + settings[size];
13267 _initEventsCalled : false,
13270 initEvents: function()
13272 if (this._initEventsCalled) { // as we call render... prevent looping...
13275 this._initEventsCalled = true;
13278 throw "can not find store for combo";
13281 this.indicator = this.indicatorEl();
13283 this.store = Roo.factory(this.store, Roo.data);
13284 this.store.parent = this;
13286 // if we are building from html. then this element is so complex, that we can not really
13287 // use the rendered HTML.
13288 // so we have to trash and replace the previous code.
13289 if (Roo.XComponent.build_from_html) {
13290 // remove this element....
13291 var e = this.el.dom, k=0;
13292 while (e ) { e = e.previousSibling; ++k;}
13297 this.rendered = false;
13299 this.render(this.parent().getChildContainer(true), k);
13302 if(Roo.isIOS && this.useNativeIOS){
13303 this.initIOSView();
13311 if(Roo.isTouch && this.mobileTouchView){
13312 this.initTouchView();
13317 this.initTickableEvents();
13321 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13323 if(this.hiddenName){
13325 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13327 this.hiddenField.dom.value =
13328 this.hiddenValue !== undefined ? this.hiddenValue :
13329 this.value !== undefined ? this.value : '';
13331 // prevent input submission
13332 this.el.dom.removeAttribute('name');
13333 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13338 // this.el.dom.setAttribute('autocomplete', 'off');
13341 var cls = 'x-combo-list';
13343 //this.list = new Roo.Layer({
13344 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13350 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13351 _this.list.setWidth(lw);
13354 this.list.on('mouseover', this.onViewOver, this);
13355 this.list.on('mousemove', this.onViewMove, this);
13356 this.list.on('scroll', this.onViewScroll, this);
13359 this.list.swallowEvent('mousewheel');
13360 this.assetHeight = 0;
13363 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13364 this.assetHeight += this.header.getHeight();
13367 this.innerList = this.list.createChild({cls:cls+'-inner'});
13368 this.innerList.on('mouseover', this.onViewOver, this);
13369 this.innerList.on('mousemove', this.onViewMove, this);
13370 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13372 if(this.allowBlank && !this.pageSize && !this.disableClear){
13373 this.footer = this.list.createChild({cls:cls+'-ft'});
13374 this.pageTb = new Roo.Toolbar(this.footer);
13378 this.footer = this.list.createChild({cls:cls+'-ft'});
13379 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13380 {pageSize: this.pageSize});
13384 if (this.pageTb && this.allowBlank && !this.disableClear) {
13386 this.pageTb.add(new Roo.Toolbar.Fill(), {
13387 cls: 'x-btn-icon x-btn-clear',
13389 handler: function()
13392 _this.clearValue();
13393 _this.onSelect(false, -1);
13398 this.assetHeight += this.footer.getHeight();
13403 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13406 this.view = new Roo.View(this.list, this.tpl, {
13407 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13409 //this.view.wrapEl.setDisplayed(false);
13410 this.view.on('click', this.onViewClick, this);
13413 this.store.on('beforeload', this.onBeforeLoad, this);
13414 this.store.on('load', this.onLoad, this);
13415 this.store.on('loadexception', this.onLoadException, this);
13417 if(this.resizable){
13418 this.resizer = new Roo.Resizable(this.list, {
13419 pinned:true, handles:'se'
13421 this.resizer.on('resize', function(r, w, h){
13422 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13423 this.listWidth = w;
13424 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13425 this.restrictHeight();
13427 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13430 if(!this.editable){
13431 this.editable = true;
13432 this.setEditable(false);
13437 if (typeof(this.events.add.listeners) != 'undefined') {
13439 this.addicon = this.wrap.createChild(
13440 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13442 this.addicon.on('click', function(e) {
13443 this.fireEvent('add', this);
13446 if (typeof(this.events.edit.listeners) != 'undefined') {
13448 this.editicon = this.wrap.createChild(
13449 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13450 if (this.addicon) {
13451 this.editicon.setStyle('margin-left', '40px');
13453 this.editicon.on('click', function(e) {
13455 // we fire even if inothing is selected..
13456 this.fireEvent('edit', this, this.lastData );
13462 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13463 "up" : function(e){
13464 this.inKeyMode = true;
13468 "down" : function(e){
13469 if(!this.isExpanded()){
13470 this.onTriggerClick();
13472 this.inKeyMode = true;
13477 "enter" : function(e){
13478 // this.onViewClick();
13482 if(this.fireEvent("specialkey", this, e)){
13483 this.onViewClick(false);
13489 "esc" : function(e){
13493 "tab" : function(e){
13496 if(this.fireEvent("specialkey", this, e)){
13497 this.onViewClick(false);
13505 doRelay : function(foo, bar, hname){
13506 if(hname == 'down' || this.scope.isExpanded()){
13507 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13516 this.queryDelay = Math.max(this.queryDelay || 10,
13517 this.mode == 'local' ? 10 : 250);
13520 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13522 if(this.typeAhead){
13523 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13525 if(this.editable !== false){
13526 this.inputEl().on("keyup", this.onKeyUp, this);
13528 if(this.forceSelection){
13529 this.inputEl().on('blur', this.doForce, this);
13533 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13534 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13538 initTickableEvents: function()
13542 if(this.hiddenName){
13544 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13546 this.hiddenField.dom.value =
13547 this.hiddenValue !== undefined ? this.hiddenValue :
13548 this.value !== undefined ? this.value : '';
13550 // prevent input submission
13551 this.el.dom.removeAttribute('name');
13552 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13557 // this.list = this.el.select('ul.dropdown-menu',true).first();
13559 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13560 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13561 if(this.triggerList){
13562 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13565 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13566 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13568 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13569 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13571 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13572 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13574 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13575 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13576 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13579 this.cancelBtn.hide();
13584 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13585 _this.list.setWidth(lw);
13588 this.list.on('mouseover', this.onViewOver, this);
13589 this.list.on('mousemove', this.onViewMove, this);
13591 this.list.on('scroll', this.onViewScroll, this);
13594 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13597 this.view = new Roo.View(this.list, this.tpl, {
13598 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13601 //this.view.wrapEl.setDisplayed(false);
13602 this.view.on('click', this.onViewClick, this);
13606 this.store.on('beforeload', this.onBeforeLoad, this);
13607 this.store.on('load', this.onLoad, this);
13608 this.store.on('loadexception', this.onLoadException, this);
13611 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13612 "up" : function(e){
13613 this.inKeyMode = true;
13617 "down" : function(e){
13618 this.inKeyMode = true;
13622 "enter" : function(e){
13623 if(this.fireEvent("specialkey", this, e)){
13624 this.onViewClick(false);
13630 "esc" : function(e){
13631 this.onTickableFooterButtonClick(e, false, false);
13634 "tab" : function(e){
13635 this.fireEvent("specialkey", this, e);
13637 this.onTickableFooterButtonClick(e, false, false);
13644 doRelay : function(e, fn, key){
13645 if(this.scope.isExpanded()){
13646 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13655 this.queryDelay = Math.max(this.queryDelay || 10,
13656 this.mode == 'local' ? 10 : 250);
13659 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13661 if(this.typeAhead){
13662 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13665 if(this.editable !== false){
13666 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13669 this.indicator = this.indicatorEl();
13671 if(this.indicator){
13672 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13673 this.indicator.hide();
13678 onDestroy : function(){
13680 this.view.setStore(null);
13681 this.view.el.removeAllListeners();
13682 this.view.el.remove();
13683 this.view.purgeListeners();
13686 this.list.dom.innerHTML = '';
13690 this.store.un('beforeload', this.onBeforeLoad, this);
13691 this.store.un('load', this.onLoad, this);
13692 this.store.un('loadexception', this.onLoadException, this);
13694 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13698 fireKey : function(e){
13699 if(e.isNavKeyPress() && !this.list.isVisible()){
13700 this.fireEvent("specialkey", this, e);
13705 onResize: function(w, h){
13706 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13708 // if(typeof w != 'number'){
13709 // // we do not handle it!?!?
13712 // var tw = this.trigger.getWidth();
13713 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13714 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13716 // this.inputEl().setWidth( this.adjustWidth('input', x));
13718 // //this.trigger.setStyle('left', x+'px');
13720 // if(this.list && this.listWidth === undefined){
13721 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13722 // this.list.setWidth(lw);
13723 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13731 * Allow or prevent the user from directly editing the field text. If false is passed,
13732 * the user will only be able to select from the items defined in the dropdown list. This method
13733 * is the runtime equivalent of setting the 'editable' config option at config time.
13734 * @param {Boolean} value True to allow the user to directly edit the field text
13736 setEditable : function(value){
13737 if(value == this.editable){
13740 this.editable = value;
13742 this.inputEl().dom.setAttribute('readOnly', true);
13743 this.inputEl().on('mousedown', this.onTriggerClick, this);
13744 this.inputEl().addClass('x-combo-noedit');
13746 this.inputEl().dom.setAttribute('readOnly', false);
13747 this.inputEl().un('mousedown', this.onTriggerClick, this);
13748 this.inputEl().removeClass('x-combo-noedit');
13754 onBeforeLoad : function(combo,opts){
13755 if(!this.hasFocus){
13759 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13761 this.restrictHeight();
13762 this.selectedIndex = -1;
13766 onLoad : function(){
13768 this.hasQuery = false;
13770 if(!this.hasFocus){
13774 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13775 this.loading.hide();
13778 if(this.store.getCount() > 0){
13781 this.restrictHeight();
13782 if(this.lastQuery == this.allQuery){
13783 if(this.editable && !this.tickable){
13784 this.inputEl().dom.select();
13788 !this.selectByValue(this.value, true) &&
13791 !this.store.lastOptions ||
13792 typeof(this.store.lastOptions.add) == 'undefined' ||
13793 this.store.lastOptions.add != true
13796 this.select(0, true);
13799 if(this.autoFocus){
13802 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13803 this.taTask.delay(this.typeAheadDelay);
13807 this.onEmptyResults();
13813 onLoadException : function()
13815 this.hasQuery = false;
13817 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13818 this.loading.hide();
13821 if(this.tickable && this.editable){
13826 // only causes errors at present
13827 //Roo.log(this.store.reader.jsonData);
13828 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13830 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13836 onTypeAhead : function(){
13837 if(this.store.getCount() > 0){
13838 var r = this.store.getAt(0);
13839 var newValue = r.data[this.displayField];
13840 var len = newValue.length;
13841 var selStart = this.getRawValue().length;
13843 if(selStart != len){
13844 this.setRawValue(newValue);
13845 this.selectText(selStart, newValue.length);
13851 onSelect : function(record, index){
13853 if(this.fireEvent('beforeselect', this, record, index) !== false){
13855 this.setFromData(index > -1 ? record.data : false);
13858 this.fireEvent('select', this, record, index);
13863 * Returns the currently selected field value or empty string if no value is set.
13864 * @return {String} value The selected value
13866 getValue : function()
13868 if(Roo.isIOS && this.useNativeIOS){
13869 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13873 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13876 if(this.valueField){
13877 return typeof this.value != 'undefined' ? this.value : '';
13879 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13883 getRawValue : function()
13885 if(Roo.isIOS && this.useNativeIOS){
13886 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13889 var v = this.inputEl().getValue();
13895 * Clears any text/value currently set in the field
13897 clearValue : function(){
13899 if(this.hiddenField){
13900 this.hiddenField.dom.value = '';
13903 this.setRawValue('');
13904 this.lastSelectionText = '';
13905 this.lastData = false;
13907 var close = this.closeTriggerEl();
13918 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13919 * will be displayed in the field. If the value does not match the data value of an existing item,
13920 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13921 * Otherwise the field will be blank (although the value will still be set).
13922 * @param {String} value The value to match
13924 setValue : function(v)
13926 if(Roo.isIOS && this.useNativeIOS){
13927 this.setIOSValue(v);
13937 if(this.valueField){
13938 var r = this.findRecord(this.valueField, v);
13940 text = r.data[this.displayField];
13941 }else if(this.valueNotFoundText !== undefined){
13942 text = this.valueNotFoundText;
13945 this.lastSelectionText = text;
13946 if(this.hiddenField){
13947 this.hiddenField.dom.value = v;
13949 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13952 var close = this.closeTriggerEl();
13955 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13961 * @property {Object} the last set data for the element
13966 * Sets the value of the field based on a object which is related to the record format for the store.
13967 * @param {Object} value the value to set as. or false on reset?
13969 setFromData : function(o){
13976 var dv = ''; // display value
13977 var vv = ''; // value value..
13979 if (this.displayField) {
13980 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13982 // this is an error condition!!!
13983 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13986 if(this.valueField){
13987 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13990 var close = this.closeTriggerEl();
13993 if(dv.length || vv * 1 > 0){
13995 this.blockFocus=true;
14001 if(this.hiddenField){
14002 this.hiddenField.dom.value = vv;
14004 this.lastSelectionText = dv;
14005 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14009 // no hidden field.. - we store the value in 'value', but still display
14010 // display field!!!!
14011 this.lastSelectionText = dv;
14012 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14019 reset : function(){
14020 // overridden so that last data is reset..
14027 this.setValue(this.originalValue);
14028 //this.clearInvalid();
14029 this.lastData = false;
14031 this.view.clearSelections();
14037 findRecord : function(prop, value){
14039 if(this.store.getCount() > 0){
14040 this.store.each(function(r){
14041 if(r.data[prop] == value){
14051 getName: function()
14053 // returns hidden if it's set..
14054 if (!this.rendered) {return ''};
14055 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14059 onViewMove : function(e, t){
14060 this.inKeyMode = false;
14064 onViewOver : function(e, t){
14065 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14068 var item = this.view.findItemFromChild(t);
14071 var index = this.view.indexOf(item);
14072 this.select(index, false);
14077 onViewClick : function(view, doFocus, el, e)
14079 var index = this.view.getSelectedIndexes()[0];
14081 var r = this.store.getAt(index);
14085 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14092 Roo.each(this.tickItems, function(v,k){
14094 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14096 _this.tickItems.splice(k, 1);
14098 if(typeof(e) == 'undefined' && view == false){
14099 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14111 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14112 this.tickItems.push(r.data);
14115 if(typeof(e) == 'undefined' && view == false){
14116 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14123 this.onSelect(r, index);
14125 if(doFocus !== false && !this.blockFocus){
14126 this.inputEl().focus();
14131 restrictHeight : function(){
14132 //this.innerList.dom.style.height = '';
14133 //var inner = this.innerList.dom;
14134 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14135 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14136 //this.list.beginUpdate();
14137 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14138 this.list.alignTo(this.inputEl(), this.listAlign);
14139 this.list.alignTo(this.inputEl(), this.listAlign);
14140 //this.list.endUpdate();
14144 onEmptyResults : function(){
14146 if(this.tickable && this.editable){
14147 this.hasFocus = false;
14148 this.restrictHeight();
14156 * Returns true if the dropdown list is expanded, else false.
14158 isExpanded : function(){
14159 return this.list.isVisible();
14163 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14164 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14165 * @param {String} value The data value of the item to select
14166 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14167 * selected item if it is not currently in view (defaults to true)
14168 * @return {Boolean} True if the value matched an item in the list, else false
14170 selectByValue : function(v, scrollIntoView){
14171 if(v !== undefined && v !== null){
14172 var r = this.findRecord(this.valueField || this.displayField, v);
14174 this.select(this.store.indexOf(r), scrollIntoView);
14182 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14183 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14184 * @param {Number} index The zero-based index of the list item to select
14185 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14186 * selected item if it is not currently in view (defaults to true)
14188 select : function(index, scrollIntoView){
14189 this.selectedIndex = index;
14190 this.view.select(index);
14191 if(scrollIntoView !== false){
14192 var el = this.view.getNode(index);
14194 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14197 this.list.scrollChildIntoView(el, false);
14203 selectNext : function(){
14204 var ct = this.store.getCount();
14206 if(this.selectedIndex == -1){
14208 }else if(this.selectedIndex < ct-1){
14209 this.select(this.selectedIndex+1);
14215 selectPrev : function(){
14216 var ct = this.store.getCount();
14218 if(this.selectedIndex == -1){
14220 }else if(this.selectedIndex != 0){
14221 this.select(this.selectedIndex-1);
14227 onKeyUp : function(e){
14228 if(this.editable !== false && !e.isSpecialKey()){
14229 this.lastKey = e.getKey();
14230 this.dqTask.delay(this.queryDelay);
14235 validateBlur : function(){
14236 return !this.list || !this.list.isVisible();
14240 initQuery : function(){
14242 var v = this.getRawValue();
14244 if(this.tickable && this.editable){
14245 v = this.tickableInputEl().getValue();
14252 doForce : function(){
14253 if(this.inputEl().dom.value.length > 0){
14254 this.inputEl().dom.value =
14255 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14261 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14262 * query allowing the query action to be canceled if needed.
14263 * @param {String} query The SQL query to execute
14264 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14265 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14266 * saved in the current store (defaults to false)
14268 doQuery : function(q, forceAll){
14270 if(q === undefined || q === null){
14275 forceAll: forceAll,
14279 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14284 forceAll = qe.forceAll;
14285 if(forceAll === true || (q.length >= this.minChars)){
14287 this.hasQuery = true;
14289 if(this.lastQuery != q || this.alwaysQuery){
14290 this.lastQuery = q;
14291 if(this.mode == 'local'){
14292 this.selectedIndex = -1;
14294 this.store.clearFilter();
14297 if(this.specialFilter){
14298 this.fireEvent('specialfilter', this);
14303 this.store.filter(this.displayField, q);
14306 this.store.fireEvent("datachanged", this.store);
14313 this.store.baseParams[this.queryParam] = q;
14315 var options = {params : this.getParams(q)};
14318 options.add = true;
14319 options.params.start = this.page * this.pageSize;
14322 this.store.load(options);
14325 * this code will make the page width larger, at the beginning, the list not align correctly,
14326 * we should expand the list on onLoad
14327 * so command out it
14332 this.selectedIndex = -1;
14337 this.loadNext = false;
14341 getParams : function(q){
14343 //p[this.queryParam] = q;
14347 p.limit = this.pageSize;
14353 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14355 collapse : function(){
14356 if(!this.isExpanded()){
14362 this.hasFocus = false;
14366 this.cancelBtn.hide();
14367 this.trigger.show();
14370 this.tickableInputEl().dom.value = '';
14371 this.tickableInputEl().blur();
14376 Roo.get(document).un('mousedown', this.collapseIf, this);
14377 Roo.get(document).un('mousewheel', this.collapseIf, this);
14378 if (!this.editable) {
14379 Roo.get(document).un('keydown', this.listKeyPress, this);
14381 this.fireEvent('collapse', this);
14387 collapseIf : function(e){
14388 var in_combo = e.within(this.el);
14389 var in_list = e.within(this.list);
14390 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14392 if (in_combo || in_list || is_list) {
14393 //e.stopPropagation();
14398 this.onTickableFooterButtonClick(e, false, false);
14406 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14408 expand : function(){
14410 if(this.isExpanded() || !this.hasFocus){
14414 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14415 this.list.setWidth(lw);
14421 this.restrictHeight();
14425 this.tickItems = Roo.apply([], this.item);
14428 this.cancelBtn.show();
14429 this.trigger.hide();
14432 this.tickableInputEl().focus();
14437 Roo.get(document).on('mousedown', this.collapseIf, this);
14438 Roo.get(document).on('mousewheel', this.collapseIf, this);
14439 if (!this.editable) {
14440 Roo.get(document).on('keydown', this.listKeyPress, this);
14443 this.fireEvent('expand', this);
14447 // Implements the default empty TriggerField.onTriggerClick function
14448 onTriggerClick : function(e)
14450 Roo.log('trigger click');
14452 if(this.disabled || !this.triggerList){
14457 this.loadNext = false;
14459 if(this.isExpanded()){
14461 if (!this.blockFocus) {
14462 this.inputEl().focus();
14466 this.hasFocus = true;
14467 if(this.triggerAction == 'all') {
14468 this.doQuery(this.allQuery, true);
14470 this.doQuery(this.getRawValue());
14472 if (!this.blockFocus) {
14473 this.inputEl().focus();
14478 onTickableTriggerClick : function(e)
14485 this.loadNext = false;
14486 this.hasFocus = true;
14488 if(this.triggerAction == 'all') {
14489 this.doQuery(this.allQuery, true);
14491 this.doQuery(this.getRawValue());
14495 onSearchFieldClick : function(e)
14497 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14498 this.onTickableFooterButtonClick(e, false, false);
14502 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14507 this.loadNext = false;
14508 this.hasFocus = true;
14510 if(this.triggerAction == 'all') {
14511 this.doQuery(this.allQuery, true);
14513 this.doQuery(this.getRawValue());
14517 listKeyPress : function(e)
14519 //Roo.log('listkeypress');
14520 // scroll to first matching element based on key pres..
14521 if (e.isSpecialKey()) {
14524 var k = String.fromCharCode(e.getKey()).toUpperCase();
14527 var csel = this.view.getSelectedNodes();
14528 var cselitem = false;
14530 var ix = this.view.indexOf(csel[0]);
14531 cselitem = this.store.getAt(ix);
14532 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14538 this.store.each(function(v) {
14540 // start at existing selection.
14541 if (cselitem.id == v.id) {
14547 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14548 match = this.store.indexOf(v);
14554 if (match === false) {
14555 return true; // no more action?
14558 this.view.select(match);
14559 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14560 sn.scrollIntoView(sn.dom.parentNode, false);
14563 onViewScroll : function(e, t){
14565 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){
14569 this.hasQuery = true;
14571 this.loading = this.list.select('.loading', true).first();
14573 if(this.loading === null){
14574 this.list.createChild({
14576 cls: 'loading roo-select2-more-results roo-select2-active',
14577 html: 'Loading more results...'
14580 this.loading = this.list.select('.loading', true).first();
14582 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14584 this.loading.hide();
14587 this.loading.show();
14592 this.loadNext = true;
14594 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14599 addItem : function(o)
14601 var dv = ''; // display value
14603 if (this.displayField) {
14604 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14606 // this is an error condition!!!
14607 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14614 var choice = this.choices.createChild({
14616 cls: 'roo-select2-search-choice',
14625 cls: 'roo-select2-search-choice-close fa fa-times',
14630 }, this.searchField);
14632 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14634 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14642 this.inputEl().dom.value = '';
14647 onRemoveItem : function(e, _self, o)
14649 e.preventDefault();
14651 this.lastItem = Roo.apply([], this.item);
14653 var index = this.item.indexOf(o.data) * 1;
14656 Roo.log('not this item?!');
14660 this.item.splice(index, 1);
14665 this.fireEvent('remove', this, e);
14671 syncValue : function()
14673 if(!this.item.length){
14680 Roo.each(this.item, function(i){
14681 if(_this.valueField){
14682 value.push(i[_this.valueField]);
14689 this.value = value.join(',');
14691 if(this.hiddenField){
14692 this.hiddenField.dom.value = this.value;
14695 this.store.fireEvent("datachanged", this.store);
14700 clearItem : function()
14702 if(!this.multiple){
14708 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14716 if(this.tickable && !Roo.isTouch){
14717 this.view.refresh();
14721 inputEl: function ()
14723 if(Roo.isIOS && this.useNativeIOS){
14724 return this.el.select('select.roo-ios-select', true).first();
14727 if(Roo.isTouch && this.mobileTouchView){
14728 return this.el.select('input.form-control',true).first();
14732 return this.searchField;
14735 return this.el.select('input.form-control',true).first();
14738 onTickableFooterButtonClick : function(e, btn, el)
14740 e.preventDefault();
14742 this.lastItem = Roo.apply([], this.item);
14744 if(btn && btn.name == 'cancel'){
14745 this.tickItems = Roo.apply([], this.item);
14754 Roo.each(this.tickItems, function(o){
14762 validate : function()
14764 if(this.getVisibilityEl().hasClass('hidden')){
14768 var v = this.getRawValue();
14771 v = this.getValue();
14774 if(this.disabled || this.allowBlank || v.length){
14779 this.markInvalid();
14783 tickableInputEl : function()
14785 if(!this.tickable || !this.editable){
14786 return this.inputEl();
14789 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14793 getAutoCreateTouchView : function()
14798 cls: 'form-group' //input-group
14804 type : this.inputType,
14805 cls : 'form-control x-combo-noedit',
14806 autocomplete: 'new-password',
14807 placeholder : this.placeholder || '',
14812 input.name = this.name;
14816 input.cls += ' input-' + this.size;
14819 if (this.disabled) {
14820 input.disabled = true;
14831 inputblock.cls += ' input-group';
14833 inputblock.cn.unshift({
14835 cls : 'input-group-addon',
14840 if(this.removable && !this.multiple){
14841 inputblock.cls += ' roo-removable';
14843 inputblock.cn.push({
14846 cls : 'roo-combo-removable-btn close'
14850 if(this.hasFeedback && !this.allowBlank){
14852 inputblock.cls += ' has-feedback';
14854 inputblock.cn.push({
14856 cls: 'glyphicon form-control-feedback'
14863 inputblock.cls += (this.before) ? '' : ' input-group';
14865 inputblock.cn.push({
14867 cls : 'input-group-addon',
14878 cls: 'form-hidden-field'
14892 cls: 'form-hidden-field'
14896 cls: 'roo-select2-choices',
14900 cls: 'roo-select2-search-field',
14913 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14919 if(!this.multiple && this.showToggleBtn){
14926 if (this.caret != false) {
14929 cls: 'fa fa-' + this.caret
14936 cls : 'input-group-addon btn dropdown-toggle',
14941 cls: 'combobox-clear',
14955 combobox.cls += ' roo-select2-container-multi';
14958 var align = this.labelAlign || this.parentLabelAlign();
14960 if (align ==='left' && this.fieldLabel.length) {
14965 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14966 tooltip : 'This field is required'
14970 cls : 'control-label',
14971 html : this.fieldLabel
14982 var labelCfg = cfg.cn[1];
14983 var contentCfg = cfg.cn[2];
14986 if(this.indicatorpos == 'right'){
14991 cls : 'control-label',
14995 html : this.fieldLabel
14999 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15000 tooltip : 'This field is required'
15013 labelCfg = cfg.cn[0];
15014 contentCfg = cfg.cn[1];
15019 if(this.labelWidth > 12){
15020 labelCfg.style = "width: " + this.labelWidth + 'px';
15023 if(this.labelWidth < 13 && this.labelmd == 0){
15024 this.labelmd = this.labelWidth;
15027 if(this.labellg > 0){
15028 labelCfg.cls += ' col-lg-' + this.labellg;
15029 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15032 if(this.labelmd > 0){
15033 labelCfg.cls += ' col-md-' + this.labelmd;
15034 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15037 if(this.labelsm > 0){
15038 labelCfg.cls += ' col-sm-' + this.labelsm;
15039 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15042 if(this.labelxs > 0){
15043 labelCfg.cls += ' col-xs-' + this.labelxs;
15044 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15048 } else if ( this.fieldLabel.length) {
15052 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15053 tooltip : 'This field is required'
15057 cls : 'control-label',
15058 html : this.fieldLabel
15069 if(this.indicatorpos == 'right'){
15073 cls : 'control-label',
15074 html : this.fieldLabel,
15078 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15079 tooltip : 'This field is required'
15096 var settings = this;
15098 ['xs','sm','md','lg'].map(function(size){
15099 if (settings[size]) {
15100 cfg.cls += ' col-' + size + '-' + settings[size];
15107 initTouchView : function()
15109 this.renderTouchView();
15111 this.touchViewEl.on('scroll', function(){
15112 this.el.dom.scrollTop = 0;
15115 this.originalValue = this.getValue();
15117 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15119 this.inputEl().on("click", this.showTouchView, this);
15120 if (this.triggerEl) {
15121 this.triggerEl.on("click", this.showTouchView, this);
15125 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15126 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15128 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15130 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15131 this.store.on('load', this.onTouchViewLoad, this);
15132 this.store.on('loadexception', this.onTouchViewLoadException, this);
15134 if(this.hiddenName){
15136 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15138 this.hiddenField.dom.value =
15139 this.hiddenValue !== undefined ? this.hiddenValue :
15140 this.value !== undefined ? this.value : '';
15142 this.el.dom.removeAttribute('name');
15143 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15147 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15148 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15151 if(this.removable && !this.multiple){
15152 var close = this.closeTriggerEl();
15154 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15155 close.on('click', this.removeBtnClick, this, close);
15159 * fix the bug in Safari iOS8
15161 this.inputEl().on("focus", function(e){
15162 document.activeElement.blur();
15170 renderTouchView : function()
15172 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15173 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15176 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15178 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15179 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15180 this.touchViewBodyEl.setStyle('overflow', 'auto');
15182 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15183 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15185 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15186 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15190 showTouchView : function()
15196 this.touchViewHeaderEl.hide();
15198 if(this.modalTitle.length){
15199 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15200 this.touchViewHeaderEl.show();
15203 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15204 this.touchViewEl.show();
15206 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15208 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15209 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15211 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15213 if(this.modalTitle.length){
15214 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15217 this.touchViewBodyEl.setHeight(bodyHeight);
15221 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15223 this.touchViewEl.addClass('in');
15226 this.doTouchViewQuery();
15230 hideTouchView : function()
15232 this.touchViewEl.removeClass('in');
15236 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15238 this.touchViewEl.setStyle('display', 'none');
15243 setTouchViewValue : function()
15250 Roo.each(this.tickItems, function(o){
15255 this.hideTouchView();
15258 doTouchViewQuery : function()
15267 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15271 if(!this.alwaysQuery || this.mode == 'local'){
15272 this.onTouchViewLoad();
15279 onTouchViewBeforeLoad : function(combo,opts)
15285 onTouchViewLoad : function()
15287 if(this.store.getCount() < 1){
15288 this.onTouchViewEmptyResults();
15292 this.clearTouchView();
15294 var rawValue = this.getRawValue();
15296 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15298 this.tickItems = [];
15300 this.store.data.each(function(d, rowIndex){
15301 var row = this.touchViewListGroup.createChild(template);
15303 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15304 row.addClass(d.data.cls);
15307 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15310 html : d.data[this.displayField]
15313 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15314 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15317 row.removeClass('selected');
15318 if(!this.multiple && this.valueField &&
15319 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15322 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15323 row.addClass('selected');
15326 if(this.multiple && this.valueField &&
15327 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15331 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15332 this.tickItems.push(d.data);
15335 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15339 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15341 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15343 if(this.modalTitle.length){
15344 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15347 var listHeight = this.touchViewListGroup.getHeight();
15351 if(firstChecked && listHeight > bodyHeight){
15352 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15357 onTouchViewLoadException : function()
15359 this.hideTouchView();
15362 onTouchViewEmptyResults : function()
15364 this.clearTouchView();
15366 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15368 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15372 clearTouchView : function()
15374 this.touchViewListGroup.dom.innerHTML = '';
15377 onTouchViewClick : function(e, el, o)
15379 e.preventDefault();
15382 var rowIndex = o.rowIndex;
15384 var r = this.store.getAt(rowIndex);
15386 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15388 if(!this.multiple){
15389 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15390 c.dom.removeAttribute('checked');
15393 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15395 this.setFromData(r.data);
15397 var close = this.closeTriggerEl();
15403 this.hideTouchView();
15405 this.fireEvent('select', this, r, rowIndex);
15410 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15411 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15412 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15416 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15417 this.addItem(r.data);
15418 this.tickItems.push(r.data);
15422 getAutoCreateNativeIOS : function()
15425 cls: 'form-group' //input-group,
15430 cls : 'roo-ios-select'
15434 combobox.name = this.name;
15437 if (this.disabled) {
15438 combobox.disabled = true;
15441 var settings = this;
15443 ['xs','sm','md','lg'].map(function(size){
15444 if (settings[size]) {
15445 cfg.cls += ' col-' + size + '-' + settings[size];
15455 initIOSView : function()
15457 this.store.on('load', this.onIOSViewLoad, this);
15462 onIOSViewLoad : function()
15464 if(this.store.getCount() < 1){
15468 this.clearIOSView();
15470 if(this.allowBlank) {
15472 var default_text = '-- SELECT --';
15474 if(this.placeholder.length){
15475 default_text = this.placeholder;
15478 if(this.emptyTitle.length){
15479 default_text += ' - ' + this.emptyTitle + ' -';
15482 var opt = this.inputEl().createChild({
15485 html : default_text
15489 o[this.valueField] = 0;
15490 o[this.displayField] = default_text;
15492 this.ios_options.push({
15499 this.store.data.each(function(d, rowIndex){
15503 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15504 html = d.data[this.displayField];
15509 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15510 value = d.data[this.valueField];
15519 if(this.value == d.data[this.valueField]){
15520 option['selected'] = true;
15523 var opt = this.inputEl().createChild(option);
15525 this.ios_options.push({
15532 this.inputEl().on('change', function(){
15533 this.fireEvent('select', this);
15538 clearIOSView: function()
15540 this.inputEl().dom.innerHTML = '';
15542 this.ios_options = [];
15545 setIOSValue: function(v)
15549 if(!this.ios_options){
15553 Roo.each(this.ios_options, function(opts){
15555 opts.el.dom.removeAttribute('selected');
15557 if(opts.data[this.valueField] != v){
15561 opts.el.dom.setAttribute('selected', true);
15567 * @cfg {Boolean} grow
15571 * @cfg {Number} growMin
15575 * @cfg {Number} growMax
15584 Roo.apply(Roo.bootstrap.ComboBox, {
15588 cls: 'modal-header',
15610 cls: 'list-group-item',
15614 cls: 'roo-combobox-list-group-item-value'
15618 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15632 listItemCheckbox : {
15634 cls: 'list-group-item',
15638 cls: 'roo-combobox-list-group-item-value'
15642 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15658 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15663 cls: 'modal-footer',
15671 cls: 'col-xs-6 text-left',
15674 cls: 'btn btn-danger roo-touch-view-cancel',
15680 cls: 'col-xs-6 text-right',
15683 cls: 'btn btn-success roo-touch-view-ok',
15694 Roo.apply(Roo.bootstrap.ComboBox, {
15696 touchViewTemplate : {
15698 cls: 'modal fade roo-combobox-touch-view',
15702 cls: 'modal-dialog',
15703 style : 'position:fixed', // we have to fix position....
15707 cls: 'modal-content',
15709 Roo.bootstrap.ComboBox.header,
15710 Roo.bootstrap.ComboBox.body,
15711 Roo.bootstrap.ComboBox.footer
15720 * Ext JS Library 1.1.1
15721 * Copyright(c) 2006-2007, Ext JS, LLC.
15723 * Originally Released Under LGPL - original licence link has changed is not relivant.
15726 * <script type="text/javascript">
15731 * @extends Roo.util.Observable
15732 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15733 * This class also supports single and multi selection modes. <br>
15734 * Create a data model bound view:
15736 var store = new Roo.data.Store(...);
15738 var view = new Roo.View({
15740 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15742 singleSelect: true,
15743 selectedClass: "ydataview-selected",
15747 // listen for node click?
15748 view.on("click", function(vw, index, node, e){
15749 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15753 dataModel.load("foobar.xml");
15755 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15757 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15758 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15760 * Note: old style constructor is still suported (container, template, config)
15763 * Create a new View
15764 * @param {Object} config The config object
15767 Roo.View = function(config, depreciated_tpl, depreciated_config){
15769 this.parent = false;
15771 if (typeof(depreciated_tpl) == 'undefined') {
15772 // new way.. - universal constructor.
15773 Roo.apply(this, config);
15774 this.el = Roo.get(this.el);
15777 this.el = Roo.get(config);
15778 this.tpl = depreciated_tpl;
15779 Roo.apply(this, depreciated_config);
15781 this.wrapEl = this.el.wrap().wrap();
15782 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15785 if(typeof(this.tpl) == "string"){
15786 this.tpl = new Roo.Template(this.tpl);
15788 // support xtype ctors..
15789 this.tpl = new Roo.factory(this.tpl, Roo);
15793 this.tpl.compile();
15798 * @event beforeclick
15799 * Fires before a click is processed. Returns false to cancel the default action.
15800 * @param {Roo.View} this
15801 * @param {Number} index The index of the target node
15802 * @param {HTMLElement} node The target node
15803 * @param {Roo.EventObject} e The raw event object
15805 "beforeclick" : true,
15808 * Fires when a template node is clicked.
15809 * @param {Roo.View} this
15810 * @param {Number} index The index of the target node
15811 * @param {HTMLElement} node The target node
15812 * @param {Roo.EventObject} e The raw event object
15817 * Fires when a template node is double clicked.
15818 * @param {Roo.View} this
15819 * @param {Number} index The index of the target node
15820 * @param {HTMLElement} node The target node
15821 * @param {Roo.EventObject} e The raw event object
15825 * @event contextmenu
15826 * Fires when a template node is right clicked.
15827 * @param {Roo.View} this
15828 * @param {Number} index The index of the target node
15829 * @param {HTMLElement} node The target node
15830 * @param {Roo.EventObject} e The raw event object
15832 "contextmenu" : true,
15834 * @event selectionchange
15835 * Fires when the selected nodes change.
15836 * @param {Roo.View} this
15837 * @param {Array} selections Array of the selected nodes
15839 "selectionchange" : true,
15842 * @event beforeselect
15843 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15844 * @param {Roo.View} this
15845 * @param {HTMLElement} node The node to be selected
15846 * @param {Array} selections Array of currently selected nodes
15848 "beforeselect" : true,
15850 * @event preparedata
15851 * Fires on every row to render, to allow you to change the data.
15852 * @param {Roo.View} this
15853 * @param {Object} data to be rendered (change this)
15855 "preparedata" : true
15863 "click": this.onClick,
15864 "dblclick": this.onDblClick,
15865 "contextmenu": this.onContextMenu,
15869 this.selections = [];
15871 this.cmp = new Roo.CompositeElementLite([]);
15873 this.store = Roo.factory(this.store, Roo.data);
15874 this.setStore(this.store, true);
15877 if ( this.footer && this.footer.xtype) {
15879 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15881 this.footer.dataSource = this.store;
15882 this.footer.container = fctr;
15883 this.footer = Roo.factory(this.footer, Roo);
15884 fctr.insertFirst(this.el);
15886 // this is a bit insane - as the paging toolbar seems to detach the el..
15887 // dom.parentNode.parentNode.parentNode
15888 // they get detached?
15892 Roo.View.superclass.constructor.call(this);
15897 Roo.extend(Roo.View, Roo.util.Observable, {
15900 * @cfg {Roo.data.Store} store Data store to load data from.
15905 * @cfg {String|Roo.Element} el The container element.
15910 * @cfg {String|Roo.Template} tpl The template used by this View
15914 * @cfg {String} dataName the named area of the template to use as the data area
15915 * Works with domtemplates roo-name="name"
15919 * @cfg {String} selectedClass The css class to add to selected nodes
15921 selectedClass : "x-view-selected",
15923 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15928 * @cfg {String} text to display on mask (default Loading)
15932 * @cfg {Boolean} multiSelect Allow multiple selection
15934 multiSelect : false,
15936 * @cfg {Boolean} singleSelect Allow single selection
15938 singleSelect: false,
15941 * @cfg {Boolean} toggleSelect - selecting
15943 toggleSelect : false,
15946 * @cfg {Boolean} tickable - selecting
15951 * Returns the element this view is bound to.
15952 * @return {Roo.Element}
15954 getEl : function(){
15955 return this.wrapEl;
15961 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15963 refresh : function(){
15964 //Roo.log('refresh');
15967 // if we are using something like 'domtemplate', then
15968 // the what gets used is:
15969 // t.applySubtemplate(NAME, data, wrapping data..)
15970 // the outer template then get' applied with
15971 // the store 'extra data'
15972 // and the body get's added to the
15973 // roo-name="data" node?
15974 // <span class='roo-tpl-{name}'></span> ?????
15978 this.clearSelections();
15979 this.el.update("");
15981 var records = this.store.getRange();
15982 if(records.length < 1) {
15984 // is this valid?? = should it render a template??
15986 this.el.update(this.emptyText);
15990 if (this.dataName) {
15991 this.el.update(t.apply(this.store.meta)); //????
15992 el = this.el.child('.roo-tpl-' + this.dataName);
15995 for(var i = 0, len = records.length; i < len; i++){
15996 var data = this.prepareData(records[i].data, i, records[i]);
15997 this.fireEvent("preparedata", this, data, i, records[i]);
15999 var d = Roo.apply({}, data);
16002 Roo.apply(d, {'roo-id' : Roo.id()});
16006 Roo.each(this.parent.item, function(item){
16007 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16010 Roo.apply(d, {'roo-data-checked' : 'checked'});
16014 html[html.length] = Roo.util.Format.trim(
16016 t.applySubtemplate(this.dataName, d, this.store.meta) :
16023 el.update(html.join(""));
16024 this.nodes = el.dom.childNodes;
16025 this.updateIndexes(0);
16030 * Function to override to reformat the data that is sent to
16031 * the template for each node.
16032 * DEPRICATED - use the preparedata event handler.
16033 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16034 * a JSON object for an UpdateManager bound view).
16036 prepareData : function(data, index, record)
16038 this.fireEvent("preparedata", this, data, index, record);
16042 onUpdate : function(ds, record){
16043 // Roo.log('on update');
16044 this.clearSelections();
16045 var index = this.store.indexOf(record);
16046 var n = this.nodes[index];
16047 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16048 n.parentNode.removeChild(n);
16049 this.updateIndexes(index, index);
16055 onAdd : function(ds, records, index)
16057 //Roo.log(['on Add', ds, records, index] );
16058 this.clearSelections();
16059 if(this.nodes.length == 0){
16063 var n = this.nodes[index];
16064 for(var i = 0, len = records.length; i < len; i++){
16065 var d = this.prepareData(records[i].data, i, records[i]);
16067 this.tpl.insertBefore(n, d);
16070 this.tpl.append(this.el, d);
16073 this.updateIndexes(index);
16076 onRemove : function(ds, record, index){
16077 // Roo.log('onRemove');
16078 this.clearSelections();
16079 var el = this.dataName ?
16080 this.el.child('.roo-tpl-' + this.dataName) :
16083 el.dom.removeChild(this.nodes[index]);
16084 this.updateIndexes(index);
16088 * Refresh an individual node.
16089 * @param {Number} index
16091 refreshNode : function(index){
16092 this.onUpdate(this.store, this.store.getAt(index));
16095 updateIndexes : function(startIndex, endIndex){
16096 var ns = this.nodes;
16097 startIndex = startIndex || 0;
16098 endIndex = endIndex || ns.length - 1;
16099 for(var i = startIndex; i <= endIndex; i++){
16100 ns[i].nodeIndex = i;
16105 * Changes the data store this view uses and refresh the view.
16106 * @param {Store} store
16108 setStore : function(store, initial){
16109 if(!initial && this.store){
16110 this.store.un("datachanged", this.refresh);
16111 this.store.un("add", this.onAdd);
16112 this.store.un("remove", this.onRemove);
16113 this.store.un("update", this.onUpdate);
16114 this.store.un("clear", this.refresh);
16115 this.store.un("beforeload", this.onBeforeLoad);
16116 this.store.un("load", this.onLoad);
16117 this.store.un("loadexception", this.onLoad);
16121 store.on("datachanged", this.refresh, this);
16122 store.on("add", this.onAdd, this);
16123 store.on("remove", this.onRemove, this);
16124 store.on("update", this.onUpdate, this);
16125 store.on("clear", this.refresh, this);
16126 store.on("beforeload", this.onBeforeLoad, this);
16127 store.on("load", this.onLoad, this);
16128 store.on("loadexception", this.onLoad, this);
16136 * onbeforeLoad - masks the loading area.
16139 onBeforeLoad : function(store,opts)
16141 //Roo.log('onBeforeLoad');
16143 this.el.update("");
16145 this.el.mask(this.mask ? this.mask : "Loading" );
16147 onLoad : function ()
16154 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16155 * @param {HTMLElement} node
16156 * @return {HTMLElement} The template node
16158 findItemFromChild : function(node){
16159 var el = this.dataName ?
16160 this.el.child('.roo-tpl-' + this.dataName,true) :
16163 if(!node || node.parentNode == el){
16166 var p = node.parentNode;
16167 while(p && p != el){
16168 if(p.parentNode == el){
16177 onClick : function(e){
16178 var item = this.findItemFromChild(e.getTarget());
16180 var index = this.indexOf(item);
16181 if(this.onItemClick(item, index, e) !== false){
16182 this.fireEvent("click", this, index, item, e);
16185 this.clearSelections();
16190 onContextMenu : function(e){
16191 var item = this.findItemFromChild(e.getTarget());
16193 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16198 onDblClick : function(e){
16199 var item = this.findItemFromChild(e.getTarget());
16201 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16205 onItemClick : function(item, index, e)
16207 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16210 if (this.toggleSelect) {
16211 var m = this.isSelected(item) ? 'unselect' : 'select';
16214 _t[m](item, true, false);
16217 if(this.multiSelect || this.singleSelect){
16218 if(this.multiSelect && e.shiftKey && this.lastSelection){
16219 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16221 this.select(item, this.multiSelect && e.ctrlKey);
16222 this.lastSelection = item;
16225 if(!this.tickable){
16226 e.preventDefault();
16234 * Get the number of selected nodes.
16237 getSelectionCount : function(){
16238 return this.selections.length;
16242 * Get the currently selected nodes.
16243 * @return {Array} An array of HTMLElements
16245 getSelectedNodes : function(){
16246 return this.selections;
16250 * Get the indexes of the selected nodes.
16253 getSelectedIndexes : function(){
16254 var indexes = [], s = this.selections;
16255 for(var i = 0, len = s.length; i < len; i++){
16256 indexes.push(s[i].nodeIndex);
16262 * Clear all selections
16263 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16265 clearSelections : function(suppressEvent){
16266 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16267 this.cmp.elements = this.selections;
16268 this.cmp.removeClass(this.selectedClass);
16269 this.selections = [];
16270 if(!suppressEvent){
16271 this.fireEvent("selectionchange", this, this.selections);
16277 * Returns true if the passed node is selected
16278 * @param {HTMLElement/Number} node The node or node index
16279 * @return {Boolean}
16281 isSelected : function(node){
16282 var s = this.selections;
16286 node = this.getNode(node);
16287 return s.indexOf(node) !== -1;
16292 * @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
16293 * @param {Boolean} keepExisting (optional) true to keep existing selections
16294 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16296 select : function(nodeInfo, keepExisting, suppressEvent){
16297 if(nodeInfo instanceof Array){
16299 this.clearSelections(true);
16301 for(var i = 0, len = nodeInfo.length; i < len; i++){
16302 this.select(nodeInfo[i], true, true);
16306 var node = this.getNode(nodeInfo);
16307 if(!node || this.isSelected(node)){
16308 return; // already selected.
16311 this.clearSelections(true);
16314 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16315 Roo.fly(node).addClass(this.selectedClass);
16316 this.selections.push(node);
16317 if(!suppressEvent){
16318 this.fireEvent("selectionchange", this, this.selections);
16326 * @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
16327 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16328 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16330 unselect : function(nodeInfo, keepExisting, suppressEvent)
16332 if(nodeInfo instanceof Array){
16333 Roo.each(this.selections, function(s) {
16334 this.unselect(s, nodeInfo);
16338 var node = this.getNode(nodeInfo);
16339 if(!node || !this.isSelected(node)){
16340 //Roo.log("not selected");
16341 return; // not selected.
16345 Roo.each(this.selections, function(s) {
16347 Roo.fly(node).removeClass(this.selectedClass);
16354 this.selections= ns;
16355 this.fireEvent("selectionchange", this, this.selections);
16359 * Gets a template node.
16360 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16361 * @return {HTMLElement} The node or null if it wasn't found
16363 getNode : function(nodeInfo){
16364 if(typeof nodeInfo == "string"){
16365 return document.getElementById(nodeInfo);
16366 }else if(typeof nodeInfo == "number"){
16367 return this.nodes[nodeInfo];
16373 * Gets a range template nodes.
16374 * @param {Number} startIndex
16375 * @param {Number} endIndex
16376 * @return {Array} An array of nodes
16378 getNodes : function(start, end){
16379 var ns = this.nodes;
16380 start = start || 0;
16381 end = typeof end == "undefined" ? ns.length - 1 : end;
16384 for(var i = start; i <= end; i++){
16388 for(var i = start; i >= end; i--){
16396 * Finds the index of the passed node
16397 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16398 * @return {Number} The index of the node or -1
16400 indexOf : function(node){
16401 node = this.getNode(node);
16402 if(typeof node.nodeIndex == "number"){
16403 return node.nodeIndex;
16405 var ns = this.nodes;
16406 for(var i = 0, len = ns.length; i < len; i++){
16417 * based on jquery fullcalendar
16421 Roo.bootstrap = Roo.bootstrap || {};
16423 * @class Roo.bootstrap.Calendar
16424 * @extends Roo.bootstrap.Component
16425 * Bootstrap Calendar class
16426 * @cfg {Boolean} loadMask (true|false) default false
16427 * @cfg {Object} header generate the user specific header of the calendar, default false
16430 * Create a new Container
16431 * @param {Object} config The config object
16436 Roo.bootstrap.Calendar = function(config){
16437 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16441 * Fires when a date is selected
16442 * @param {DatePicker} this
16443 * @param {Date} date The selected date
16447 * @event monthchange
16448 * Fires when the displayed month changes
16449 * @param {DatePicker} this
16450 * @param {Date} date The selected month
16452 'monthchange': true,
16454 * @event evententer
16455 * Fires when mouse over an event
16456 * @param {Calendar} this
16457 * @param {event} Event
16459 'evententer': true,
16461 * @event eventleave
16462 * Fires when the mouse leaves an
16463 * @param {Calendar} this
16466 'eventleave': true,
16468 * @event eventclick
16469 * Fires when the mouse click an
16470 * @param {Calendar} this
16479 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16482 * @cfg {Number} startDay
16483 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16491 getAutoCreate : function(){
16494 var fc_button = function(name, corner, style, content ) {
16495 return Roo.apply({},{
16497 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16499 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16502 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16513 style : 'width:100%',
16520 cls : 'fc-header-left',
16522 fc_button('prev', 'left', 'arrow', '‹' ),
16523 fc_button('next', 'right', 'arrow', '›' ),
16524 { tag: 'span', cls: 'fc-header-space' },
16525 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16533 cls : 'fc-header-center',
16537 cls: 'fc-header-title',
16540 html : 'month / year'
16548 cls : 'fc-header-right',
16550 /* fc_button('month', 'left', '', 'month' ),
16551 fc_button('week', '', '', 'week' ),
16552 fc_button('day', 'right', '', 'day' )
16564 header = this.header;
16567 var cal_heads = function() {
16569 // fixme - handle this.
16571 for (var i =0; i < Date.dayNames.length; i++) {
16572 var d = Date.dayNames[i];
16575 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16576 html : d.substring(0,3)
16580 ret[0].cls += ' fc-first';
16581 ret[6].cls += ' fc-last';
16584 var cal_cell = function(n) {
16587 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16592 cls: 'fc-day-number',
16596 cls: 'fc-day-content',
16600 style: 'position: relative;' // height: 17px;
16612 var cal_rows = function() {
16615 for (var r = 0; r < 6; r++) {
16622 for (var i =0; i < Date.dayNames.length; i++) {
16623 var d = Date.dayNames[i];
16624 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16627 row.cn[0].cls+=' fc-first';
16628 row.cn[0].cn[0].style = 'min-height:90px';
16629 row.cn[6].cls+=' fc-last';
16633 ret[0].cls += ' fc-first';
16634 ret[4].cls += ' fc-prev-last';
16635 ret[5].cls += ' fc-last';
16642 cls: 'fc-border-separate',
16643 style : 'width:100%',
16651 cls : 'fc-first fc-last',
16669 cls : 'fc-content',
16670 style : "position: relative;",
16673 cls : 'fc-view fc-view-month fc-grid',
16674 style : 'position: relative',
16675 unselectable : 'on',
16678 cls : 'fc-event-container',
16679 style : 'position:absolute;z-index:8;top:0;left:0;'
16697 initEvents : function()
16700 throw "can not find store for calendar";
16706 style: "text-align:center",
16710 style: "background-color:white;width:50%;margin:250 auto",
16714 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16725 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16727 var size = this.el.select('.fc-content', true).first().getSize();
16728 this.maskEl.setSize(size.width, size.height);
16729 this.maskEl.enableDisplayMode("block");
16730 if(!this.loadMask){
16731 this.maskEl.hide();
16734 this.store = Roo.factory(this.store, Roo.data);
16735 this.store.on('load', this.onLoad, this);
16736 this.store.on('beforeload', this.onBeforeLoad, this);
16740 this.cells = this.el.select('.fc-day',true);
16741 //Roo.log(this.cells);
16742 this.textNodes = this.el.query('.fc-day-number');
16743 this.cells.addClassOnOver('fc-state-hover');
16745 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16746 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16747 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16748 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16750 this.on('monthchange', this.onMonthChange, this);
16752 this.update(new Date().clearTime());
16755 resize : function() {
16756 var sz = this.el.getSize();
16758 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16759 this.el.select('.fc-day-content div',true).setHeight(34);
16764 showPrevMonth : function(e){
16765 this.update(this.activeDate.add("mo", -1));
16767 showToday : function(e){
16768 this.update(new Date().clearTime());
16771 showNextMonth : function(e){
16772 this.update(this.activeDate.add("mo", 1));
16776 showPrevYear : function(){
16777 this.update(this.activeDate.add("y", -1));
16781 showNextYear : function(){
16782 this.update(this.activeDate.add("y", 1));
16787 update : function(date)
16789 var vd = this.activeDate;
16790 this.activeDate = date;
16791 // if(vd && this.el){
16792 // var t = date.getTime();
16793 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16794 // Roo.log('using add remove');
16796 // this.fireEvent('monthchange', this, date);
16798 // this.cells.removeClass("fc-state-highlight");
16799 // this.cells.each(function(c){
16800 // if(c.dateValue == t){
16801 // c.addClass("fc-state-highlight");
16802 // setTimeout(function(){
16803 // try{c.dom.firstChild.focus();}catch(e){}
16813 var days = date.getDaysInMonth();
16815 var firstOfMonth = date.getFirstDateOfMonth();
16816 var startingPos = firstOfMonth.getDay()-this.startDay;
16818 if(startingPos < this.startDay){
16822 var pm = date.add(Date.MONTH, -1);
16823 var prevStart = pm.getDaysInMonth()-startingPos;
16825 this.cells = this.el.select('.fc-day',true);
16826 this.textNodes = this.el.query('.fc-day-number');
16827 this.cells.addClassOnOver('fc-state-hover');
16829 var cells = this.cells.elements;
16830 var textEls = this.textNodes;
16832 Roo.each(cells, function(cell){
16833 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16836 days += startingPos;
16838 // convert everything to numbers so it's fast
16839 var day = 86400000;
16840 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16843 //Roo.log(prevStart);
16845 var today = new Date().clearTime().getTime();
16846 var sel = date.clearTime().getTime();
16847 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16848 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16849 var ddMatch = this.disabledDatesRE;
16850 var ddText = this.disabledDatesText;
16851 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16852 var ddaysText = this.disabledDaysText;
16853 var format = this.format;
16855 var setCellClass = function(cal, cell){
16859 //Roo.log('set Cell Class');
16861 var t = d.getTime();
16865 cell.dateValue = t;
16867 cell.className += " fc-today";
16868 cell.className += " fc-state-highlight";
16869 cell.title = cal.todayText;
16872 // disable highlight in other month..
16873 //cell.className += " fc-state-highlight";
16878 cell.className = " fc-state-disabled";
16879 cell.title = cal.minText;
16883 cell.className = " fc-state-disabled";
16884 cell.title = cal.maxText;
16888 if(ddays.indexOf(d.getDay()) != -1){
16889 cell.title = ddaysText;
16890 cell.className = " fc-state-disabled";
16893 if(ddMatch && format){
16894 var fvalue = d.dateFormat(format);
16895 if(ddMatch.test(fvalue)){
16896 cell.title = ddText.replace("%0", fvalue);
16897 cell.className = " fc-state-disabled";
16901 if (!cell.initialClassName) {
16902 cell.initialClassName = cell.dom.className;
16905 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16910 for(; i < startingPos; i++) {
16911 textEls[i].innerHTML = (++prevStart);
16912 d.setDate(d.getDate()+1);
16914 cells[i].className = "fc-past fc-other-month";
16915 setCellClass(this, cells[i]);
16920 for(; i < days; i++){
16921 intDay = i - startingPos + 1;
16922 textEls[i].innerHTML = (intDay);
16923 d.setDate(d.getDate()+1);
16925 cells[i].className = ''; // "x-date-active";
16926 setCellClass(this, cells[i]);
16930 for(; i < 42; i++) {
16931 textEls[i].innerHTML = (++extraDays);
16932 d.setDate(d.getDate()+1);
16934 cells[i].className = "fc-future fc-other-month";
16935 setCellClass(this, cells[i]);
16938 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16940 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16942 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16943 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16945 if(totalRows != 6){
16946 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16947 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16950 this.fireEvent('monthchange', this, date);
16954 if(!this.internalRender){
16955 var main = this.el.dom.firstChild;
16956 var w = main.offsetWidth;
16957 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16958 Roo.fly(main).setWidth(w);
16959 this.internalRender = true;
16960 // opera does not respect the auto grow header center column
16961 // then, after it gets a width opera refuses to recalculate
16962 // without a second pass
16963 if(Roo.isOpera && !this.secondPass){
16964 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16965 this.secondPass = true;
16966 this.update.defer(10, this, [date]);
16973 findCell : function(dt) {
16974 dt = dt.clearTime().getTime();
16976 this.cells.each(function(c){
16977 //Roo.log("check " +c.dateValue + '?=' + dt);
16978 if(c.dateValue == dt){
16988 findCells : function(ev) {
16989 var s = ev.start.clone().clearTime().getTime();
16991 var e= ev.end.clone().clearTime().getTime();
16994 this.cells.each(function(c){
16995 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16997 if(c.dateValue > e){
17000 if(c.dateValue < s){
17009 // findBestRow: function(cells)
17013 // for (var i =0 ; i < cells.length;i++) {
17014 // ret = Math.max(cells[i].rows || 0,ret);
17021 addItem : function(ev)
17023 // look for vertical location slot in
17024 var cells = this.findCells(ev);
17026 // ev.row = this.findBestRow(cells);
17028 // work out the location.
17032 for(var i =0; i < cells.length; i++) {
17034 cells[i].row = cells[0].row;
17037 cells[i].row = cells[i].row + 1;
17047 if (crow.start.getY() == cells[i].getY()) {
17049 crow.end = cells[i];
17066 cells[0].events.push(ev);
17068 this.calevents.push(ev);
17071 clearEvents: function() {
17073 if(!this.calevents){
17077 Roo.each(this.cells.elements, function(c){
17083 Roo.each(this.calevents, function(e) {
17084 Roo.each(e.els, function(el) {
17085 el.un('mouseenter' ,this.onEventEnter, this);
17086 el.un('mouseleave' ,this.onEventLeave, this);
17091 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17097 renderEvents: function()
17101 this.cells.each(function(c) {
17110 if(c.row != c.events.length){
17111 r = 4 - (4 - (c.row - c.events.length));
17114 c.events = ev.slice(0, r);
17115 c.more = ev.slice(r);
17117 if(c.more.length && c.more.length == 1){
17118 c.events.push(c.more.pop());
17121 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17125 this.cells.each(function(c) {
17127 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17130 for (var e = 0; e < c.events.length; e++){
17131 var ev = c.events[e];
17132 var rows = ev.rows;
17134 for(var i = 0; i < rows.length; i++) {
17136 // how many rows should it span..
17139 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17140 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17142 unselectable : "on",
17145 cls: 'fc-event-inner',
17149 // cls: 'fc-event-time',
17150 // html : cells.length > 1 ? '' : ev.time
17154 cls: 'fc-event-title',
17155 html : String.format('{0}', ev.title)
17162 cls: 'ui-resizable-handle ui-resizable-e',
17163 html : '  '
17170 cfg.cls += ' fc-event-start';
17172 if ((i+1) == rows.length) {
17173 cfg.cls += ' fc-event-end';
17176 var ctr = _this.el.select('.fc-event-container',true).first();
17177 var cg = ctr.createChild(cfg);
17179 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17180 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17182 var r = (c.more.length) ? 1 : 0;
17183 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17184 cg.setWidth(ebox.right - sbox.x -2);
17186 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17187 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17188 cg.on('click', _this.onEventClick, _this, ev);
17199 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17200 style : 'position: absolute',
17201 unselectable : "on",
17204 cls: 'fc-event-inner',
17208 cls: 'fc-event-title',
17216 cls: 'ui-resizable-handle ui-resizable-e',
17217 html : '  '
17223 var ctr = _this.el.select('.fc-event-container',true).first();
17224 var cg = ctr.createChild(cfg);
17226 var sbox = c.select('.fc-day-content',true).first().getBox();
17227 var ebox = c.select('.fc-day-content',true).first().getBox();
17229 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17230 cg.setWidth(ebox.right - sbox.x -2);
17232 cg.on('click', _this.onMoreEventClick, _this, c.more);
17242 onEventEnter: function (e, el,event,d) {
17243 this.fireEvent('evententer', this, el, event);
17246 onEventLeave: function (e, el,event,d) {
17247 this.fireEvent('eventleave', this, el, event);
17250 onEventClick: function (e, el,event,d) {
17251 this.fireEvent('eventclick', this, el, event);
17254 onMonthChange: function () {
17258 onMoreEventClick: function(e, el, more)
17262 this.calpopover.placement = 'right';
17263 this.calpopover.setTitle('More');
17265 this.calpopover.setContent('');
17267 var ctr = this.calpopover.el.select('.popover-content', true).first();
17269 Roo.each(more, function(m){
17271 cls : 'fc-event-hori fc-event-draggable',
17274 var cg = ctr.createChild(cfg);
17276 cg.on('click', _this.onEventClick, _this, m);
17279 this.calpopover.show(el);
17284 onLoad: function ()
17286 this.calevents = [];
17289 if(this.store.getCount() > 0){
17290 this.store.data.each(function(d){
17293 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17294 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17295 time : d.data.start_time,
17296 title : d.data.title,
17297 description : d.data.description,
17298 venue : d.data.venue
17303 this.renderEvents();
17305 if(this.calevents.length && this.loadMask){
17306 this.maskEl.hide();
17310 onBeforeLoad: function()
17312 this.clearEvents();
17314 this.maskEl.show();
17328 * @class Roo.bootstrap.Popover
17329 * @extends Roo.bootstrap.Component
17330 * Bootstrap Popover class
17331 * @cfg {String} html contents of the popover (or false to use children..)
17332 * @cfg {String} title of popover (or false to hide)
17333 * @cfg {String} placement how it is placed
17334 * @cfg {String} trigger click || hover (or false to trigger manually)
17335 * @cfg {String} over what (parent or false to trigger manually.)
17336 * @cfg {Number} delay - delay before showing
17339 * Create a new Popover
17340 * @param {Object} config The config object
17343 Roo.bootstrap.Popover = function(config){
17344 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17350 * After the popover show
17352 * @param {Roo.bootstrap.Popover} this
17357 * After the popover hide
17359 * @param {Roo.bootstrap.Popover} this
17365 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17367 title: 'Fill in a title',
17370 placement : 'right',
17371 trigger : 'hover', // hover
17377 can_build_overlaid : false,
17379 getChildContainer : function()
17381 return this.el.select('.popover-content',true).first();
17384 getAutoCreate : function(){
17387 cls : 'popover roo-dynamic',
17388 style: 'display:block',
17394 cls : 'popover-inner',
17398 cls: 'popover-title',
17402 cls : 'popover-content',
17413 setTitle: function(str)
17416 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17418 setContent: function(str)
17421 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17423 // as it get's added to the bottom of the page.
17424 onRender : function(ct, position)
17426 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17428 var cfg = Roo.apply({}, this.getAutoCreate());
17432 cfg.cls += ' ' + this.cls;
17435 cfg.style = this.style;
17437 //Roo.log("adding to ");
17438 this.el = Roo.get(document.body).createChild(cfg, position);
17439 // Roo.log(this.el);
17444 initEvents : function()
17446 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17447 this.el.enableDisplayMode('block');
17449 if (this.over === false) {
17452 if (this.triggers === false) {
17455 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17456 var triggers = this.trigger ? this.trigger.split(' ') : [];
17457 Roo.each(triggers, function(trigger) {
17459 if (trigger == 'click') {
17460 on_el.on('click', this.toggle, this);
17461 } else if (trigger != 'manual') {
17462 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17463 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17465 on_el.on(eventIn ,this.enter, this);
17466 on_el.on(eventOut, this.leave, this);
17477 toggle : function () {
17478 this.hoverState == 'in' ? this.leave() : this.enter();
17481 enter : function () {
17483 clearTimeout(this.timeout);
17485 this.hoverState = 'in';
17487 if (!this.delay || !this.delay.show) {
17492 this.timeout = setTimeout(function () {
17493 if (_t.hoverState == 'in') {
17496 }, this.delay.show)
17499 leave : function() {
17500 clearTimeout(this.timeout);
17502 this.hoverState = 'out';
17504 if (!this.delay || !this.delay.hide) {
17509 this.timeout = setTimeout(function () {
17510 if (_t.hoverState == 'out') {
17513 }, this.delay.hide)
17516 show : function (on_el)
17519 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17523 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17524 if (this.html !== false) {
17525 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17527 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17528 if (!this.title.length) {
17529 this.el.select('.popover-title',true).hide();
17532 var placement = typeof this.placement == 'function' ?
17533 this.placement.call(this, this.el, on_el) :
17536 var autoToken = /\s?auto?\s?/i;
17537 var autoPlace = autoToken.test(placement);
17539 placement = placement.replace(autoToken, '') || 'top';
17543 //this.el.setXY([0,0]);
17545 this.el.dom.style.display='block';
17546 this.el.addClass(placement);
17548 //this.el.appendTo(on_el);
17550 var p = this.getPosition();
17551 var box = this.el.getBox();
17556 var align = Roo.bootstrap.Popover.alignment[placement];
17559 this.el.alignTo(on_el, align[0],align[1]);
17560 //var arrow = this.el.select('.arrow',true).first();
17561 //arrow.set(align[2],
17563 this.el.addClass('in');
17566 if (this.el.hasClass('fade')) {
17570 this.hoverState = 'in';
17572 this.fireEvent('show', this);
17577 this.el.setXY([0,0]);
17578 this.el.removeClass('in');
17580 this.hoverState = null;
17582 this.fireEvent('hide', this);
17587 Roo.bootstrap.Popover.alignment = {
17588 'left' : ['r-l', [-10,0], 'right'],
17589 'right' : ['l-r', [10,0], 'left'],
17590 'bottom' : ['t-b', [0,10], 'top'],
17591 'top' : [ 'b-t', [0,-10], 'bottom']
17602 * @class Roo.bootstrap.Progress
17603 * @extends Roo.bootstrap.Component
17604 * Bootstrap Progress class
17605 * @cfg {Boolean} striped striped of the progress bar
17606 * @cfg {Boolean} active animated of the progress bar
17610 * Create a new Progress
17611 * @param {Object} config The config object
17614 Roo.bootstrap.Progress = function(config){
17615 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17618 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17623 getAutoCreate : function(){
17631 cfg.cls += ' progress-striped';
17635 cfg.cls += ' active';
17654 * @class Roo.bootstrap.ProgressBar
17655 * @extends Roo.bootstrap.Component
17656 * Bootstrap ProgressBar class
17657 * @cfg {Number} aria_valuenow aria-value now
17658 * @cfg {Number} aria_valuemin aria-value min
17659 * @cfg {Number} aria_valuemax aria-value max
17660 * @cfg {String} label label for the progress bar
17661 * @cfg {String} panel (success | info | warning | danger )
17662 * @cfg {String} role role of the progress bar
17663 * @cfg {String} sr_only text
17667 * Create a new ProgressBar
17668 * @param {Object} config The config object
17671 Roo.bootstrap.ProgressBar = function(config){
17672 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17675 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17679 aria_valuemax : 100,
17685 getAutoCreate : function()
17690 cls: 'progress-bar',
17691 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17703 cfg.role = this.role;
17706 if(this.aria_valuenow){
17707 cfg['aria-valuenow'] = this.aria_valuenow;
17710 if(this.aria_valuemin){
17711 cfg['aria-valuemin'] = this.aria_valuemin;
17714 if(this.aria_valuemax){
17715 cfg['aria-valuemax'] = this.aria_valuemax;
17718 if(this.label && !this.sr_only){
17719 cfg.html = this.label;
17723 cfg.cls += ' progress-bar-' + this.panel;
17729 update : function(aria_valuenow)
17731 this.aria_valuenow = aria_valuenow;
17733 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17748 * @class Roo.bootstrap.TabGroup
17749 * @extends Roo.bootstrap.Column
17750 * Bootstrap Column class
17751 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17752 * @cfg {Boolean} carousel true to make the group behave like a carousel
17753 * @cfg {Boolean} bullets show bullets for the panels
17754 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17755 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17756 * @cfg {Boolean} showarrow (true|false) show arrow default true
17759 * Create a new TabGroup
17760 * @param {Object} config The config object
17763 Roo.bootstrap.TabGroup = function(config){
17764 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17766 this.navId = Roo.id();
17769 Roo.bootstrap.TabGroup.register(this);
17773 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17776 transition : false,
17781 slideOnTouch : false,
17784 getAutoCreate : function()
17786 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17788 cfg.cls += ' tab-content';
17790 if (this.carousel) {
17791 cfg.cls += ' carousel slide';
17794 cls : 'carousel-inner',
17798 if(this.bullets && !Roo.isTouch){
17801 cls : 'carousel-bullets',
17805 if(this.bullets_cls){
17806 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17813 cfg.cn[0].cn.push(bullets);
17816 if(this.showarrow){
17817 cfg.cn[0].cn.push({
17819 class : 'carousel-arrow',
17823 class : 'carousel-prev',
17827 class : 'fa fa-chevron-left'
17833 class : 'carousel-next',
17837 class : 'fa fa-chevron-right'
17850 initEvents: function()
17852 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17853 // this.el.on("touchstart", this.onTouchStart, this);
17856 if(this.autoslide){
17859 this.slideFn = window.setInterval(function() {
17860 _this.showPanelNext();
17864 if(this.showarrow){
17865 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17866 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17872 // onTouchStart : function(e, el, o)
17874 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17878 // this.showPanelNext();
17882 getChildContainer : function()
17884 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17888 * register a Navigation item
17889 * @param {Roo.bootstrap.NavItem} the navitem to add
17891 register : function(item)
17893 this.tabs.push( item);
17894 item.navId = this.navId; // not really needed..
17899 getActivePanel : function()
17902 Roo.each(this.tabs, function(t) {
17912 getPanelByName : function(n)
17915 Roo.each(this.tabs, function(t) {
17916 if (t.tabId == n) {
17924 indexOfPanel : function(p)
17927 Roo.each(this.tabs, function(t,i) {
17928 if (t.tabId == p.tabId) {
17937 * show a specific panel
17938 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17939 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17941 showPanel : function (pan)
17943 if(this.transition || typeof(pan) == 'undefined'){
17944 Roo.log("waiting for the transitionend");
17948 if (typeof(pan) == 'number') {
17949 pan = this.tabs[pan];
17952 if (typeof(pan) == 'string') {
17953 pan = this.getPanelByName(pan);
17956 var cur = this.getActivePanel();
17959 Roo.log('pan or acitve pan is undefined');
17963 if (pan.tabId == this.getActivePanel().tabId) {
17967 if (false === cur.fireEvent('beforedeactivate')) {
17971 if(this.bullets > 0 && !Roo.isTouch){
17972 this.setActiveBullet(this.indexOfPanel(pan));
17975 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17977 this.transition = true;
17978 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17979 var lr = dir == 'next' ? 'left' : 'right';
17980 pan.el.addClass(dir); // or prev
17981 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17982 cur.el.addClass(lr); // or right
17983 pan.el.addClass(lr);
17986 cur.el.on('transitionend', function() {
17987 Roo.log("trans end?");
17989 pan.el.removeClass([lr,dir]);
17990 pan.setActive(true);
17992 cur.el.removeClass([lr]);
17993 cur.setActive(false);
17995 _this.transition = false;
17997 }, this, { single: true } );
18002 cur.setActive(false);
18003 pan.setActive(true);
18008 showPanelNext : function()
18010 var i = this.indexOfPanel(this.getActivePanel());
18012 if (i >= this.tabs.length - 1 && !this.autoslide) {
18016 if (i >= this.tabs.length - 1 && this.autoslide) {
18020 this.showPanel(this.tabs[i+1]);
18023 showPanelPrev : function()
18025 var i = this.indexOfPanel(this.getActivePanel());
18027 if (i < 1 && !this.autoslide) {
18031 if (i < 1 && this.autoslide) {
18032 i = this.tabs.length;
18035 this.showPanel(this.tabs[i-1]);
18039 addBullet: function()
18041 if(!this.bullets || Roo.isTouch){
18044 var ctr = this.el.select('.carousel-bullets',true).first();
18045 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18046 var bullet = ctr.createChild({
18047 cls : 'bullet bullet-' + i
18048 },ctr.dom.lastChild);
18053 bullet.on('click', (function(e, el, o, ii, t){
18055 e.preventDefault();
18057 this.showPanel(ii);
18059 if(this.autoslide && this.slideFn){
18060 clearInterval(this.slideFn);
18061 this.slideFn = window.setInterval(function() {
18062 _this.showPanelNext();
18066 }).createDelegate(this, [i, bullet], true));
18071 setActiveBullet : function(i)
18077 Roo.each(this.el.select('.bullet', true).elements, function(el){
18078 el.removeClass('selected');
18081 var bullet = this.el.select('.bullet-' + i, true).first();
18087 bullet.addClass('selected');
18098 Roo.apply(Roo.bootstrap.TabGroup, {
18102 * register a Navigation Group
18103 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18105 register : function(navgrp)
18107 this.groups[navgrp.navId] = navgrp;
18111 * fetch a Navigation Group based on the navigation ID
18112 * if one does not exist , it will get created.
18113 * @param {string} the navgroup to add
18114 * @returns {Roo.bootstrap.NavGroup} the navgroup
18116 get: function(navId) {
18117 if (typeof(this.groups[navId]) == 'undefined') {
18118 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18120 return this.groups[navId] ;
18135 * @class Roo.bootstrap.TabPanel
18136 * @extends Roo.bootstrap.Component
18137 * Bootstrap TabPanel class
18138 * @cfg {Boolean} active panel active
18139 * @cfg {String} html panel content
18140 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18141 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18142 * @cfg {String} href click to link..
18146 * Create a new TabPanel
18147 * @param {Object} config The config object
18150 Roo.bootstrap.TabPanel = function(config){
18151 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18155 * Fires when the active status changes
18156 * @param {Roo.bootstrap.TabPanel} this
18157 * @param {Boolean} state the new state
18162 * @event beforedeactivate
18163 * Fires before a tab is de-activated - can be used to do validation on a form.
18164 * @param {Roo.bootstrap.TabPanel} this
18165 * @return {Boolean} false if there is an error
18168 'beforedeactivate': true
18171 this.tabId = this.tabId || Roo.id();
18175 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18183 getAutoCreate : function(){
18186 // item is needed for carousel - not sure if it has any effect otherwise
18187 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18188 html: this.html || ''
18192 cfg.cls += ' active';
18196 cfg.tabId = this.tabId;
18203 initEvents: function()
18205 var p = this.parent();
18207 this.navId = this.navId || p.navId;
18209 if (typeof(this.navId) != 'undefined') {
18210 // not really needed.. but just in case.. parent should be a NavGroup.
18211 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18215 var i = tg.tabs.length - 1;
18217 if(this.active && tg.bullets > 0 && i < tg.bullets){
18218 tg.setActiveBullet(i);
18222 this.el.on('click', this.onClick, this);
18225 this.el.on("touchstart", this.onTouchStart, this);
18226 this.el.on("touchmove", this.onTouchMove, this);
18227 this.el.on("touchend", this.onTouchEnd, this);
18232 onRender : function(ct, position)
18234 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18237 setActive : function(state)
18239 Roo.log("panel - set active " + this.tabId + "=" + state);
18241 this.active = state;
18243 this.el.removeClass('active');
18245 } else if (!this.el.hasClass('active')) {
18246 this.el.addClass('active');
18249 this.fireEvent('changed', this, state);
18252 onClick : function(e)
18254 e.preventDefault();
18256 if(!this.href.length){
18260 window.location.href = this.href;
18269 onTouchStart : function(e)
18271 this.swiping = false;
18273 this.startX = e.browserEvent.touches[0].clientX;
18274 this.startY = e.browserEvent.touches[0].clientY;
18277 onTouchMove : function(e)
18279 this.swiping = true;
18281 this.endX = e.browserEvent.touches[0].clientX;
18282 this.endY = e.browserEvent.touches[0].clientY;
18285 onTouchEnd : function(e)
18292 var tabGroup = this.parent();
18294 if(this.endX > this.startX){ // swiping right
18295 tabGroup.showPanelPrev();
18299 if(this.startX > this.endX){ // swiping left
18300 tabGroup.showPanelNext();
18319 * @class Roo.bootstrap.DateField
18320 * @extends Roo.bootstrap.Input
18321 * Bootstrap DateField class
18322 * @cfg {Number} weekStart default 0
18323 * @cfg {String} viewMode default empty, (months|years)
18324 * @cfg {String} minViewMode default empty, (months|years)
18325 * @cfg {Number} startDate default -Infinity
18326 * @cfg {Number} endDate default Infinity
18327 * @cfg {Boolean} todayHighlight default false
18328 * @cfg {Boolean} todayBtn default false
18329 * @cfg {Boolean} calendarWeeks default false
18330 * @cfg {Object} daysOfWeekDisabled default empty
18331 * @cfg {Boolean} singleMode default false (true | false)
18333 * @cfg {Boolean} keyboardNavigation default true
18334 * @cfg {String} language default en
18337 * Create a new DateField
18338 * @param {Object} config The config object
18341 Roo.bootstrap.DateField = function(config){
18342 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18346 * Fires when this field show.
18347 * @param {Roo.bootstrap.DateField} this
18348 * @param {Mixed} date The date value
18353 * Fires when this field hide.
18354 * @param {Roo.bootstrap.DateField} this
18355 * @param {Mixed} date The date value
18360 * Fires when select a date.
18361 * @param {Roo.bootstrap.DateField} this
18362 * @param {Mixed} date The date value
18366 * @event beforeselect
18367 * Fires when before select a date.
18368 * @param {Roo.bootstrap.DateField} this
18369 * @param {Mixed} date The date value
18371 beforeselect : true
18375 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18378 * @cfg {String} format
18379 * The default date format string which can be overriden for localization support. The format must be
18380 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18384 * @cfg {String} altFormats
18385 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18386 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18388 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18396 todayHighlight : false,
18402 keyboardNavigation: true,
18404 calendarWeeks: false,
18406 startDate: -Infinity,
18410 daysOfWeekDisabled: [],
18414 singleMode : false,
18416 UTCDate: function()
18418 return new Date(Date.UTC.apply(Date, arguments));
18421 UTCToday: function()
18423 var today = new Date();
18424 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18427 getDate: function() {
18428 var d = this.getUTCDate();
18429 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18432 getUTCDate: function() {
18436 setDate: function(d) {
18437 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18440 setUTCDate: function(d) {
18442 this.setValue(this.formatDate(this.date));
18445 onRender: function(ct, position)
18448 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18450 this.language = this.language || 'en';
18451 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18452 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18454 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18455 this.format = this.format || 'm/d/y';
18456 this.isInline = false;
18457 this.isInput = true;
18458 this.component = this.el.select('.add-on', true).first() || false;
18459 this.component = (this.component && this.component.length === 0) ? false : this.component;
18460 this.hasInput = this.component && this.inputEl().length;
18462 if (typeof(this.minViewMode === 'string')) {
18463 switch (this.minViewMode) {
18465 this.minViewMode = 1;
18468 this.minViewMode = 2;
18471 this.minViewMode = 0;
18476 if (typeof(this.viewMode === 'string')) {
18477 switch (this.viewMode) {
18490 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18492 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18494 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18496 this.picker().on('mousedown', this.onMousedown, this);
18497 this.picker().on('click', this.onClick, this);
18499 this.picker().addClass('datepicker-dropdown');
18501 this.startViewMode = this.viewMode;
18503 if(this.singleMode){
18504 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18505 v.setVisibilityMode(Roo.Element.DISPLAY);
18509 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18510 v.setStyle('width', '189px');
18514 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18515 if(!this.calendarWeeks){
18520 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18521 v.attr('colspan', function(i, val){
18522 return parseInt(val) + 1;
18527 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18529 this.setStartDate(this.startDate);
18530 this.setEndDate(this.endDate);
18532 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18539 if(this.isInline) {
18544 picker : function()
18546 return this.pickerEl;
18547 // return this.el.select('.datepicker', true).first();
18550 fillDow: function()
18552 var dowCnt = this.weekStart;
18561 if(this.calendarWeeks){
18569 while (dowCnt < this.weekStart + 7) {
18573 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18577 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18580 fillMonths: function()
18583 var months = this.picker().select('>.datepicker-months td', true).first();
18585 months.dom.innerHTML = '';
18591 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18594 months.createChild(month);
18601 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;
18603 if (this.date < this.startDate) {
18604 this.viewDate = new Date(this.startDate);
18605 } else if (this.date > this.endDate) {
18606 this.viewDate = new Date(this.endDate);
18608 this.viewDate = new Date(this.date);
18616 var d = new Date(this.viewDate),
18617 year = d.getUTCFullYear(),
18618 month = d.getUTCMonth(),
18619 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18620 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18621 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18622 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18623 currentDate = this.date && this.date.valueOf(),
18624 today = this.UTCToday();
18626 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18628 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18630 // this.picker.select('>tfoot th.today').
18631 // .text(dates[this.language].today)
18632 // .toggle(this.todayBtn !== false);
18634 this.updateNavArrows();
18637 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18639 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18641 prevMonth.setUTCDate(day);
18643 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18645 var nextMonth = new Date(prevMonth);
18647 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18649 nextMonth = nextMonth.valueOf();
18651 var fillMonths = false;
18653 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18655 while(prevMonth.valueOf() < nextMonth) {
18658 if (prevMonth.getUTCDay() === this.weekStart) {
18660 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18668 if(this.calendarWeeks){
18669 // ISO 8601: First week contains first thursday.
18670 // ISO also states week starts on Monday, but we can be more abstract here.
18672 // Start of current week: based on weekstart/current date
18673 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18674 // Thursday of this week
18675 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18676 // First Thursday of year, year from thursday
18677 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18678 // Calendar week: ms between thursdays, div ms per day, div 7 days
18679 calWeek = (th - yth) / 864e5 / 7 + 1;
18681 fillMonths.cn.push({
18689 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18691 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18694 if (this.todayHighlight &&
18695 prevMonth.getUTCFullYear() == today.getFullYear() &&
18696 prevMonth.getUTCMonth() == today.getMonth() &&
18697 prevMonth.getUTCDate() == today.getDate()) {
18698 clsName += ' today';
18701 if (currentDate && prevMonth.valueOf() === currentDate) {
18702 clsName += ' active';
18705 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18706 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18707 clsName += ' disabled';
18710 fillMonths.cn.push({
18712 cls: 'day ' + clsName,
18713 html: prevMonth.getDate()
18716 prevMonth.setDate(prevMonth.getDate()+1);
18719 var currentYear = this.date && this.date.getUTCFullYear();
18720 var currentMonth = this.date && this.date.getUTCMonth();
18722 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18724 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18725 v.removeClass('active');
18727 if(currentYear === year && k === currentMonth){
18728 v.addClass('active');
18731 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18732 v.addClass('disabled');
18738 year = parseInt(year/10, 10) * 10;
18740 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18742 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18745 for (var i = -1; i < 11; i++) {
18746 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18748 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18756 showMode: function(dir)
18759 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18762 Roo.each(this.picker().select('>div',true).elements, function(v){
18763 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18766 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18771 if(this.isInline) {
18775 this.picker().removeClass(['bottom', 'top']);
18777 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18779 * place to the top of element!
18783 this.picker().addClass('top');
18784 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18789 this.picker().addClass('bottom');
18791 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18794 parseDate : function(value)
18796 if(!value || value instanceof Date){
18799 var v = Date.parseDate(value, this.format);
18800 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18801 v = Date.parseDate(value, 'Y-m-d');
18803 if(!v && this.altFormats){
18804 if(!this.altFormatsArray){
18805 this.altFormatsArray = this.altFormats.split("|");
18807 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18808 v = Date.parseDate(value, this.altFormatsArray[i]);
18814 formatDate : function(date, fmt)
18816 return (!date || !(date instanceof Date)) ?
18817 date : date.dateFormat(fmt || this.format);
18820 onFocus : function()
18822 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18826 onBlur : function()
18828 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18830 var d = this.inputEl().getValue();
18839 this.picker().show();
18843 this.fireEvent('show', this, this.date);
18848 if(this.isInline) {
18851 this.picker().hide();
18852 this.viewMode = this.startViewMode;
18855 this.fireEvent('hide', this, this.date);
18859 onMousedown: function(e)
18861 e.stopPropagation();
18862 e.preventDefault();
18867 Roo.bootstrap.DateField.superclass.keyup.call(this);
18871 setValue: function(v)
18873 if(this.fireEvent('beforeselect', this, v) !== false){
18874 var d = new Date(this.parseDate(v) ).clearTime();
18876 if(isNaN(d.getTime())){
18877 this.date = this.viewDate = '';
18878 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18882 v = this.formatDate(d);
18884 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18886 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18890 this.fireEvent('select', this, this.date);
18894 getValue: function()
18896 return this.formatDate(this.date);
18899 fireKey: function(e)
18901 if (!this.picker().isVisible()){
18902 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18908 var dateChanged = false,
18910 newDate, newViewDate;
18915 e.preventDefault();
18919 if (!this.keyboardNavigation) {
18922 dir = e.keyCode == 37 ? -1 : 1;
18925 newDate = this.moveYear(this.date, dir);
18926 newViewDate = this.moveYear(this.viewDate, dir);
18927 } else if (e.shiftKey){
18928 newDate = this.moveMonth(this.date, dir);
18929 newViewDate = this.moveMonth(this.viewDate, dir);
18931 newDate = new Date(this.date);
18932 newDate.setUTCDate(this.date.getUTCDate() + dir);
18933 newViewDate = new Date(this.viewDate);
18934 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18936 if (this.dateWithinRange(newDate)){
18937 this.date = newDate;
18938 this.viewDate = newViewDate;
18939 this.setValue(this.formatDate(this.date));
18941 e.preventDefault();
18942 dateChanged = true;
18947 if (!this.keyboardNavigation) {
18950 dir = e.keyCode == 38 ? -1 : 1;
18952 newDate = this.moveYear(this.date, dir);
18953 newViewDate = this.moveYear(this.viewDate, dir);
18954 } else if (e.shiftKey){
18955 newDate = this.moveMonth(this.date, dir);
18956 newViewDate = this.moveMonth(this.viewDate, dir);
18958 newDate = new Date(this.date);
18959 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18960 newViewDate = new Date(this.viewDate);
18961 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18963 if (this.dateWithinRange(newDate)){
18964 this.date = newDate;
18965 this.viewDate = newViewDate;
18966 this.setValue(this.formatDate(this.date));
18968 e.preventDefault();
18969 dateChanged = true;
18973 this.setValue(this.formatDate(this.date));
18975 e.preventDefault();
18978 this.setValue(this.formatDate(this.date));
18992 onClick: function(e)
18994 e.stopPropagation();
18995 e.preventDefault();
18997 var target = e.getTarget();
18999 if(target.nodeName.toLowerCase() === 'i'){
19000 target = Roo.get(target).dom.parentNode;
19003 var nodeName = target.nodeName;
19004 var className = target.className;
19005 var html = target.innerHTML;
19006 //Roo.log(nodeName);
19008 switch(nodeName.toLowerCase()) {
19010 switch(className) {
19016 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19017 switch(this.viewMode){
19019 this.viewDate = this.moveMonth(this.viewDate, dir);
19023 this.viewDate = this.moveYear(this.viewDate, dir);
19029 var date = new Date();
19030 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19032 this.setValue(this.formatDate(this.date));
19039 if (className.indexOf('disabled') < 0) {
19040 this.viewDate.setUTCDate(1);
19041 if (className.indexOf('month') > -1) {
19042 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19044 var year = parseInt(html, 10) || 0;
19045 this.viewDate.setUTCFullYear(year);
19049 if(this.singleMode){
19050 this.setValue(this.formatDate(this.viewDate));
19061 //Roo.log(className);
19062 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19063 var day = parseInt(html, 10) || 1;
19064 var year = this.viewDate.getUTCFullYear(),
19065 month = this.viewDate.getUTCMonth();
19067 if (className.indexOf('old') > -1) {
19074 } else if (className.indexOf('new') > -1) {
19082 //Roo.log([year,month,day]);
19083 this.date = this.UTCDate(year, month, day,0,0,0,0);
19084 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19086 //Roo.log(this.formatDate(this.date));
19087 this.setValue(this.formatDate(this.date));
19094 setStartDate: function(startDate)
19096 this.startDate = startDate || -Infinity;
19097 if (this.startDate !== -Infinity) {
19098 this.startDate = this.parseDate(this.startDate);
19101 this.updateNavArrows();
19104 setEndDate: function(endDate)
19106 this.endDate = endDate || Infinity;
19107 if (this.endDate !== Infinity) {
19108 this.endDate = this.parseDate(this.endDate);
19111 this.updateNavArrows();
19114 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19116 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19117 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19118 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19120 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19121 return parseInt(d, 10);
19124 this.updateNavArrows();
19127 updateNavArrows: function()
19129 if(this.singleMode){
19133 var d = new Date(this.viewDate),
19134 year = d.getUTCFullYear(),
19135 month = d.getUTCMonth();
19137 Roo.each(this.picker().select('.prev', true).elements, function(v){
19139 switch (this.viewMode) {
19142 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19148 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19155 Roo.each(this.picker().select('.next', true).elements, function(v){
19157 switch (this.viewMode) {
19160 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19166 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19174 moveMonth: function(date, dir)
19179 var new_date = new Date(date.valueOf()),
19180 day = new_date.getUTCDate(),
19181 month = new_date.getUTCMonth(),
19182 mag = Math.abs(dir),
19184 dir = dir > 0 ? 1 : -1;
19187 // If going back one month, make sure month is not current month
19188 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19190 return new_date.getUTCMonth() == month;
19192 // If going forward one month, make sure month is as expected
19193 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19195 return new_date.getUTCMonth() != new_month;
19197 new_month = month + dir;
19198 new_date.setUTCMonth(new_month);
19199 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19200 if (new_month < 0 || new_month > 11) {
19201 new_month = (new_month + 12) % 12;
19204 // For magnitudes >1, move one month at a time...
19205 for (var i=0; i<mag; i++) {
19206 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19207 new_date = this.moveMonth(new_date, dir);
19209 // ...then reset the day, keeping it in the new month
19210 new_month = new_date.getUTCMonth();
19211 new_date.setUTCDate(day);
19213 return new_month != new_date.getUTCMonth();
19216 // Common date-resetting loop -- if date is beyond end of month, make it
19219 new_date.setUTCDate(--day);
19220 new_date.setUTCMonth(new_month);
19225 moveYear: function(date, dir)
19227 return this.moveMonth(date, dir*12);
19230 dateWithinRange: function(date)
19232 return date >= this.startDate && date <= this.endDate;
19238 this.picker().remove();
19241 validateValue : function(value)
19243 if(this.getVisibilityEl().hasClass('hidden')){
19247 if(value.length < 1) {
19248 if(this.allowBlank){
19254 if(value.length < this.minLength){
19257 if(value.length > this.maxLength){
19261 var vt = Roo.form.VTypes;
19262 if(!vt[this.vtype](value, this)){
19266 if(typeof this.validator == "function"){
19267 var msg = this.validator(value);
19273 if(this.regex && !this.regex.test(value)){
19277 if(typeof(this.parseDate(value)) == 'undefined'){
19281 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19285 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19293 setVisible : function(visible)
19299 this.getEl().removeClass('hidden');
19305 this.getEl().addClass('hidden');
19310 Roo.apply(Roo.bootstrap.DateField, {
19321 html: '<i class="fa fa-arrow-left"/>'
19331 html: '<i class="fa fa-arrow-right"/>'
19373 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19374 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19375 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19376 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19377 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19390 navFnc: 'FullYear',
19395 navFnc: 'FullYear',
19400 Roo.apply(Roo.bootstrap.DateField, {
19404 cls: 'datepicker dropdown-menu roo-dynamic',
19408 cls: 'datepicker-days',
19412 cls: 'table-condensed',
19414 Roo.bootstrap.DateField.head,
19418 Roo.bootstrap.DateField.footer
19425 cls: 'datepicker-months',
19429 cls: 'table-condensed',
19431 Roo.bootstrap.DateField.head,
19432 Roo.bootstrap.DateField.content,
19433 Roo.bootstrap.DateField.footer
19440 cls: 'datepicker-years',
19444 cls: 'table-condensed',
19446 Roo.bootstrap.DateField.head,
19447 Roo.bootstrap.DateField.content,
19448 Roo.bootstrap.DateField.footer
19467 * @class Roo.bootstrap.TimeField
19468 * @extends Roo.bootstrap.Input
19469 * Bootstrap DateField class
19473 * Create a new TimeField
19474 * @param {Object} config The config object
19477 Roo.bootstrap.TimeField = function(config){
19478 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19482 * Fires when this field show.
19483 * @param {Roo.bootstrap.DateField} thisthis
19484 * @param {Mixed} date The date value
19489 * Fires when this field hide.
19490 * @param {Roo.bootstrap.DateField} this
19491 * @param {Mixed} date The date value
19496 * Fires when select a date.
19497 * @param {Roo.bootstrap.DateField} this
19498 * @param {Mixed} date The date value
19504 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19507 * @cfg {String} format
19508 * The default time format string which can be overriden for localization support. The format must be
19509 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19513 onRender: function(ct, position)
19516 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19518 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19520 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19522 this.pop = this.picker().select('>.datepicker-time',true).first();
19523 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19525 this.picker().on('mousedown', this.onMousedown, this);
19526 this.picker().on('click', this.onClick, this);
19528 this.picker().addClass('datepicker-dropdown');
19533 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19534 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19535 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19536 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19537 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19538 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19542 fireKey: function(e){
19543 if (!this.picker().isVisible()){
19544 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19550 e.preventDefault();
19558 this.onTogglePeriod();
19561 this.onIncrementMinutes();
19564 this.onDecrementMinutes();
19573 onClick: function(e) {
19574 e.stopPropagation();
19575 e.preventDefault();
19578 picker : function()
19580 return this.el.select('.datepicker', true).first();
19583 fillTime: function()
19585 var time = this.pop.select('tbody', true).first();
19587 time.dom.innerHTML = '';
19602 cls: 'hours-up glyphicon glyphicon-chevron-up'
19622 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19643 cls: 'timepicker-hour',
19658 cls: 'timepicker-minute',
19673 cls: 'btn btn-primary period',
19695 cls: 'hours-down glyphicon glyphicon-chevron-down'
19715 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19733 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19740 var hours = this.time.getHours();
19741 var minutes = this.time.getMinutes();
19754 hours = hours - 12;
19758 hours = '0' + hours;
19762 minutes = '0' + minutes;
19765 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19766 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19767 this.pop.select('button', true).first().dom.innerHTML = period;
19773 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19775 var cls = ['bottom'];
19777 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19784 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19789 this.picker().addClass(cls.join('-'));
19793 Roo.each(cls, function(c){
19795 _this.picker().setTop(_this.inputEl().getHeight());
19799 _this.picker().setTop(0 - _this.picker().getHeight());
19804 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19808 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19815 onFocus : function()
19817 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19821 onBlur : function()
19823 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19829 this.picker().show();
19834 this.fireEvent('show', this, this.date);
19839 this.picker().hide();
19842 this.fireEvent('hide', this, this.date);
19845 setTime : function()
19848 this.setValue(this.time.format(this.format));
19850 this.fireEvent('select', this, this.date);
19855 onMousedown: function(e){
19856 e.stopPropagation();
19857 e.preventDefault();
19860 onIncrementHours: function()
19862 Roo.log('onIncrementHours');
19863 this.time = this.time.add(Date.HOUR, 1);
19868 onDecrementHours: function()
19870 Roo.log('onDecrementHours');
19871 this.time = this.time.add(Date.HOUR, -1);
19875 onIncrementMinutes: function()
19877 Roo.log('onIncrementMinutes');
19878 this.time = this.time.add(Date.MINUTE, 1);
19882 onDecrementMinutes: function()
19884 Roo.log('onDecrementMinutes');
19885 this.time = this.time.add(Date.MINUTE, -1);
19889 onTogglePeriod: function()
19891 Roo.log('onTogglePeriod');
19892 this.time = this.time.add(Date.HOUR, 12);
19899 Roo.apply(Roo.bootstrap.TimeField, {
19929 cls: 'btn btn-info ok',
19941 Roo.apply(Roo.bootstrap.TimeField, {
19945 cls: 'datepicker dropdown-menu',
19949 cls: 'datepicker-time',
19953 cls: 'table-condensed',
19955 Roo.bootstrap.TimeField.content,
19956 Roo.bootstrap.TimeField.footer
19975 * @class Roo.bootstrap.MonthField
19976 * @extends Roo.bootstrap.Input
19977 * Bootstrap MonthField class
19979 * @cfg {String} language default en
19982 * Create a new MonthField
19983 * @param {Object} config The config object
19986 Roo.bootstrap.MonthField = function(config){
19987 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19992 * Fires when this field show.
19993 * @param {Roo.bootstrap.MonthField} this
19994 * @param {Mixed} date The date value
19999 * Fires when this field hide.
20000 * @param {Roo.bootstrap.MonthField} this
20001 * @param {Mixed} date The date value
20006 * Fires when select a date.
20007 * @param {Roo.bootstrap.MonthField} this
20008 * @param {String} oldvalue The old value
20009 * @param {String} newvalue The new value
20015 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20017 onRender: function(ct, position)
20020 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20022 this.language = this.language || 'en';
20023 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20024 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20026 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20027 this.isInline = false;
20028 this.isInput = true;
20029 this.component = this.el.select('.add-on', true).first() || false;
20030 this.component = (this.component && this.component.length === 0) ? false : this.component;
20031 this.hasInput = this.component && this.inputEL().length;
20033 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20035 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20037 this.picker().on('mousedown', this.onMousedown, this);
20038 this.picker().on('click', this.onClick, this);
20040 this.picker().addClass('datepicker-dropdown');
20042 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20043 v.setStyle('width', '189px');
20050 if(this.isInline) {
20056 setValue: function(v, suppressEvent)
20058 var o = this.getValue();
20060 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20064 if(suppressEvent !== true){
20065 this.fireEvent('select', this, o, v);
20070 getValue: function()
20075 onClick: function(e)
20077 e.stopPropagation();
20078 e.preventDefault();
20080 var target = e.getTarget();
20082 if(target.nodeName.toLowerCase() === 'i'){
20083 target = Roo.get(target).dom.parentNode;
20086 var nodeName = target.nodeName;
20087 var className = target.className;
20088 var html = target.innerHTML;
20090 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20094 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20096 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20102 picker : function()
20104 return this.pickerEl;
20107 fillMonths: function()
20110 var months = this.picker().select('>.datepicker-months td', true).first();
20112 months.dom.innerHTML = '';
20118 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20121 months.createChild(month);
20130 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20131 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20134 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20135 e.removeClass('active');
20137 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20138 e.addClass('active');
20145 if(this.isInline) {
20149 this.picker().removeClass(['bottom', 'top']);
20151 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20153 * place to the top of element!
20157 this.picker().addClass('top');
20158 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20163 this.picker().addClass('bottom');
20165 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20168 onFocus : function()
20170 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20174 onBlur : function()
20176 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20178 var d = this.inputEl().getValue();
20187 this.picker().show();
20188 this.picker().select('>.datepicker-months', true).first().show();
20192 this.fireEvent('show', this, this.date);
20197 if(this.isInline) {
20200 this.picker().hide();
20201 this.fireEvent('hide', this, this.date);
20205 onMousedown: function(e)
20207 e.stopPropagation();
20208 e.preventDefault();
20213 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20217 fireKey: function(e)
20219 if (!this.picker().isVisible()){
20220 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20231 e.preventDefault();
20235 dir = e.keyCode == 37 ? -1 : 1;
20237 this.vIndex = this.vIndex + dir;
20239 if(this.vIndex < 0){
20243 if(this.vIndex > 11){
20247 if(isNaN(this.vIndex)){
20251 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20257 dir = e.keyCode == 38 ? -1 : 1;
20259 this.vIndex = this.vIndex + dir * 4;
20261 if(this.vIndex < 0){
20265 if(this.vIndex > 11){
20269 if(isNaN(this.vIndex)){
20273 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20278 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20279 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20283 e.preventDefault();
20286 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20287 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20303 this.picker().remove();
20308 Roo.apply(Roo.bootstrap.MonthField, {
20327 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20328 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20333 Roo.apply(Roo.bootstrap.MonthField, {
20337 cls: 'datepicker dropdown-menu roo-dynamic',
20341 cls: 'datepicker-months',
20345 cls: 'table-condensed',
20347 Roo.bootstrap.DateField.content
20367 * @class Roo.bootstrap.CheckBox
20368 * @extends Roo.bootstrap.Input
20369 * Bootstrap CheckBox class
20371 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20372 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20373 * @cfg {String} boxLabel The text that appears beside the checkbox
20374 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20375 * @cfg {Boolean} checked initnal the element
20376 * @cfg {Boolean} inline inline the element (default false)
20377 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20378 * @cfg {String} tooltip label tooltip
20381 * Create a new CheckBox
20382 * @param {Object} config The config object
20385 Roo.bootstrap.CheckBox = function(config){
20386 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20391 * Fires when the element is checked or unchecked.
20392 * @param {Roo.bootstrap.CheckBox} this This input
20393 * @param {Boolean} checked The new checked value
20398 * Fires when the element is click.
20399 * @param {Roo.bootstrap.CheckBox} this This input
20406 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20408 inputType: 'checkbox',
20417 getAutoCreate : function()
20419 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20425 cfg.cls = 'form-group ' + this.inputType; //input-group
20428 cfg.cls += ' ' + this.inputType + '-inline';
20434 type : this.inputType,
20435 value : this.inputValue,
20436 cls : 'roo-' + this.inputType, //'form-box',
20437 placeholder : this.placeholder || ''
20441 if(this.inputType != 'radio'){
20445 cls : 'roo-hidden-value',
20446 value : this.checked ? this.inputValue : this.valueOff
20451 if (this.weight) { // Validity check?
20452 cfg.cls += " " + this.inputType + "-" + this.weight;
20455 if (this.disabled) {
20456 input.disabled=true;
20460 input.checked = this.checked;
20465 input.name = this.name;
20467 if(this.inputType != 'radio'){
20468 hidden.name = this.name;
20469 input.name = '_hidden_' + this.name;
20474 input.cls += ' input-' + this.size;
20479 ['xs','sm','md','lg'].map(function(size){
20480 if (settings[size]) {
20481 cfg.cls += ' col-' + size + '-' + settings[size];
20485 var inputblock = input;
20487 if (this.before || this.after) {
20490 cls : 'input-group',
20495 inputblock.cn.push({
20497 cls : 'input-group-addon',
20502 inputblock.cn.push(input);
20504 if(this.inputType != 'radio'){
20505 inputblock.cn.push(hidden);
20509 inputblock.cn.push({
20511 cls : 'input-group-addon',
20518 if (align ==='left' && this.fieldLabel.length) {
20519 // Roo.log("left and has label");
20524 cls : 'control-label',
20525 html : this.fieldLabel
20535 if(this.labelWidth > 12){
20536 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20539 if(this.labelWidth < 13 && this.labelmd == 0){
20540 this.labelmd = this.labelWidth;
20543 if(this.labellg > 0){
20544 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20545 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20548 if(this.labelmd > 0){
20549 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20550 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20553 if(this.labelsm > 0){
20554 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20555 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20558 if(this.labelxs > 0){
20559 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20560 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20563 } else if ( this.fieldLabel.length) {
20564 // Roo.log(" label");
20568 tag: this.boxLabel ? 'span' : 'label',
20570 cls: 'control-label box-input-label',
20571 //cls : 'input-group-addon',
20572 html : this.fieldLabel
20581 // Roo.log(" no label && no align");
20582 cfg.cn = [ inputblock ] ;
20588 var boxLabelCfg = {
20590 //'for': id, // box label is handled by onclick - so no for...
20592 html: this.boxLabel
20596 boxLabelCfg.tooltip = this.tooltip;
20599 cfg.cn.push(boxLabelCfg);
20602 if(this.inputType != 'radio'){
20603 cfg.cn.push(hidden);
20611 * return the real input element.
20613 inputEl: function ()
20615 return this.el.select('input.roo-' + this.inputType,true).first();
20617 hiddenEl: function ()
20619 return this.el.select('input.roo-hidden-value',true).first();
20622 labelEl: function()
20624 return this.el.select('label.control-label',true).first();
20626 /* depricated... */
20630 return this.labelEl();
20633 boxLabelEl: function()
20635 return this.el.select('label.box-label',true).first();
20638 initEvents : function()
20640 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20642 this.inputEl().on('click', this.onClick, this);
20644 if (this.boxLabel) {
20645 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20648 this.startValue = this.getValue();
20651 Roo.bootstrap.CheckBox.register(this);
20655 onClick : function(e)
20657 if(this.fireEvent('click', this, e) !== false){
20658 this.setChecked(!this.checked);
20663 setChecked : function(state,suppressEvent)
20665 this.startValue = this.getValue();
20667 if(this.inputType == 'radio'){
20669 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20670 e.dom.checked = false;
20673 this.inputEl().dom.checked = true;
20675 this.inputEl().dom.value = this.inputValue;
20677 if(suppressEvent !== true){
20678 this.fireEvent('check', this, true);
20686 this.checked = state;
20688 this.inputEl().dom.checked = state;
20691 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20693 if(suppressEvent !== true){
20694 this.fireEvent('check', this, state);
20700 getValue : function()
20702 if(this.inputType == 'radio'){
20703 return this.getGroupValue();
20706 return this.hiddenEl().dom.value;
20710 getGroupValue : function()
20712 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20716 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20719 setValue : function(v,suppressEvent)
20721 if(this.inputType == 'radio'){
20722 this.setGroupValue(v, suppressEvent);
20726 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20731 setGroupValue : function(v, suppressEvent)
20733 this.startValue = this.getValue();
20735 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20736 e.dom.checked = false;
20738 if(e.dom.value == v){
20739 e.dom.checked = true;
20743 if(suppressEvent !== true){
20744 this.fireEvent('check', this, true);
20752 validate : function()
20754 if(this.getVisibilityEl().hasClass('hidden')){
20760 (this.inputType == 'radio' && this.validateRadio()) ||
20761 (this.inputType == 'checkbox' && this.validateCheckbox())
20767 this.markInvalid();
20771 validateRadio : function()
20773 if(this.getVisibilityEl().hasClass('hidden')){
20777 if(this.allowBlank){
20783 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20784 if(!e.dom.checked){
20796 validateCheckbox : function()
20799 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20800 //return (this.getValue() == this.inputValue) ? true : false;
20803 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20811 for(var i in group){
20812 if(group[i].el.isVisible(true)){
20820 for(var i in group){
20825 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20832 * Mark this field as valid
20834 markValid : function()
20838 this.fireEvent('valid', this);
20840 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20843 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20850 if(this.inputType == 'radio'){
20851 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20852 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20853 e.findParent('.form-group', false, true).addClass(_this.validClass);
20860 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20861 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20865 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20871 for(var i in group){
20872 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20873 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20878 * Mark this field as invalid
20879 * @param {String} msg The validation message
20881 markInvalid : function(msg)
20883 if(this.allowBlank){
20889 this.fireEvent('invalid', this, msg);
20891 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20894 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20898 label.markInvalid();
20901 if(this.inputType == 'radio'){
20902 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20903 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20904 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20911 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20912 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20916 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20922 for(var i in group){
20923 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20924 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20929 clearInvalid : function()
20931 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20933 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20935 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20937 if (label && label.iconEl) {
20938 label.iconEl.removeClass(label.validClass);
20939 label.iconEl.removeClass(label.invalidClass);
20943 disable : function()
20945 if(this.inputType != 'radio'){
20946 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20953 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20954 _this.getActionEl().addClass(this.disabledClass);
20955 e.dom.disabled = true;
20959 this.disabled = true;
20960 this.fireEvent("disable", this);
20964 enable : function()
20966 if(this.inputType != 'radio'){
20967 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20974 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20975 _this.getActionEl().removeClass(this.disabledClass);
20976 e.dom.disabled = false;
20980 this.disabled = false;
20981 this.fireEvent("enable", this);
20985 setBoxLabel : function(v)
20990 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20996 Roo.apply(Roo.bootstrap.CheckBox, {
21001 * register a CheckBox Group
21002 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21004 register : function(checkbox)
21006 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21007 this.groups[checkbox.groupId] = {};
21010 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21014 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21018 * fetch a CheckBox Group based on the group ID
21019 * @param {string} the group ID
21020 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21022 get: function(groupId) {
21023 if (typeof(this.groups[groupId]) == 'undefined') {
21027 return this.groups[groupId] ;
21040 * @class Roo.bootstrap.Radio
21041 * @extends Roo.bootstrap.Component
21042 * Bootstrap Radio class
21043 * @cfg {String} boxLabel - the label associated
21044 * @cfg {String} value - the value of radio
21047 * Create a new Radio
21048 * @param {Object} config The config object
21050 Roo.bootstrap.Radio = function(config){
21051 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21055 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21061 getAutoCreate : function()
21065 cls : 'form-group radio',
21070 html : this.boxLabel
21078 initEvents : function()
21080 this.parent().register(this);
21082 this.el.on('click', this.onClick, this);
21086 onClick : function(e)
21088 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21089 this.setChecked(true);
21093 setChecked : function(state, suppressEvent)
21095 this.parent().setValue(this.value, suppressEvent);
21099 setBoxLabel : function(v)
21104 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21119 * @class Roo.bootstrap.SecurePass
21120 * @extends Roo.bootstrap.Input
21121 * Bootstrap SecurePass class
21125 * Create a new SecurePass
21126 * @param {Object} config The config object
21129 Roo.bootstrap.SecurePass = function (config) {
21130 // these go here, so the translation tool can replace them..
21132 PwdEmpty: "Please type a password, and then retype it to confirm.",
21133 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21134 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21135 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21136 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21137 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21138 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21139 TooWeak: "Your password is Too Weak."
21141 this.meterLabel = "Password strength:";
21142 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21143 this.meterClass = [
21144 "roo-password-meter-tooweak",
21145 "roo-password-meter-weak",
21146 "roo-password-meter-medium",
21147 "roo-password-meter-strong",
21148 "roo-password-meter-grey"
21153 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21156 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21158 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21160 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21161 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21162 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21163 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21164 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21165 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21166 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21176 * @cfg {String/Object} Label for the strength meter (defaults to
21177 * 'Password strength:')
21182 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21183 * ['Weak', 'Medium', 'Strong'])
21186 pwdStrengths: false,
21199 initEvents: function ()
21201 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21203 if (this.el.is('input[type=password]') && Roo.isSafari) {
21204 this.el.on('keydown', this.SafariOnKeyDown, this);
21207 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21210 onRender: function (ct, position)
21212 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21213 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21214 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21216 this.trigger.createChild({
21221 cls: 'roo-password-meter-grey col-xs-12',
21224 //width: this.meterWidth + 'px'
21228 cls: 'roo-password-meter-text'
21234 if (this.hideTrigger) {
21235 this.trigger.setDisplayed(false);
21237 this.setSize(this.width || '', this.height || '');
21240 onDestroy: function ()
21242 if (this.trigger) {
21243 this.trigger.removeAllListeners();
21244 this.trigger.remove();
21247 this.wrap.remove();
21249 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21252 checkStrength: function ()
21254 var pwd = this.inputEl().getValue();
21255 if (pwd == this._lastPwd) {
21260 if (this.ClientSideStrongPassword(pwd)) {
21262 } else if (this.ClientSideMediumPassword(pwd)) {
21264 } else if (this.ClientSideWeakPassword(pwd)) {
21270 Roo.log('strength1: ' + strength);
21272 //var pm = this.trigger.child('div/div/div').dom;
21273 var pm = this.trigger.child('div/div');
21274 pm.removeClass(this.meterClass);
21275 pm.addClass(this.meterClass[strength]);
21278 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21280 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21282 this._lastPwd = pwd;
21286 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21288 this._lastPwd = '';
21290 var pm = this.trigger.child('div/div');
21291 pm.removeClass(this.meterClass);
21292 pm.addClass('roo-password-meter-grey');
21295 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21298 this.inputEl().dom.type='password';
21301 validateValue: function (value)
21304 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21307 if (value.length == 0) {
21308 if (this.allowBlank) {
21309 this.clearInvalid();
21313 this.markInvalid(this.errors.PwdEmpty);
21314 this.errorMsg = this.errors.PwdEmpty;
21322 if ('[\x21-\x7e]*'.match(value)) {
21323 this.markInvalid(this.errors.PwdBadChar);
21324 this.errorMsg = this.errors.PwdBadChar;
21327 if (value.length < 6) {
21328 this.markInvalid(this.errors.PwdShort);
21329 this.errorMsg = this.errors.PwdShort;
21332 if (value.length > 16) {
21333 this.markInvalid(this.errors.PwdLong);
21334 this.errorMsg = this.errors.PwdLong;
21338 if (this.ClientSideStrongPassword(value)) {
21340 } else if (this.ClientSideMediumPassword(value)) {
21342 } else if (this.ClientSideWeakPassword(value)) {
21349 if (strength < 2) {
21350 //this.markInvalid(this.errors.TooWeak);
21351 this.errorMsg = this.errors.TooWeak;
21356 console.log('strength2: ' + strength);
21358 //var pm = this.trigger.child('div/div/div').dom;
21360 var pm = this.trigger.child('div/div');
21361 pm.removeClass(this.meterClass);
21362 pm.addClass(this.meterClass[strength]);
21364 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21366 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21368 this.errorMsg = '';
21372 CharacterSetChecks: function (type)
21375 this.fResult = false;
21378 isctype: function (character, type)
21381 case this.kCapitalLetter:
21382 if (character >= 'A' && character <= 'Z') {
21387 case this.kSmallLetter:
21388 if (character >= 'a' && character <= 'z') {
21394 if (character >= '0' && character <= '9') {
21399 case this.kPunctuation:
21400 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21411 IsLongEnough: function (pwd, size)
21413 return !(pwd == null || isNaN(size) || pwd.length < size);
21416 SpansEnoughCharacterSets: function (word, nb)
21418 if (!this.IsLongEnough(word, nb))
21423 var characterSetChecks = new Array(
21424 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21425 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21428 for (var index = 0; index < word.length; ++index) {
21429 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21430 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21431 characterSetChecks[nCharSet].fResult = true;
21438 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21439 if (characterSetChecks[nCharSet].fResult) {
21444 if (nCharSets < nb) {
21450 ClientSideStrongPassword: function (pwd)
21452 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21455 ClientSideMediumPassword: function (pwd)
21457 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21460 ClientSideWeakPassword: function (pwd)
21462 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21465 })//<script type="text/javascript">
21468 * Based Ext JS Library 1.1.1
21469 * Copyright(c) 2006-2007, Ext JS, LLC.
21475 * @class Roo.HtmlEditorCore
21476 * @extends Roo.Component
21477 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21479 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21482 Roo.HtmlEditorCore = function(config){
21485 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21490 * @event initialize
21491 * Fires when the editor is fully initialized (including the iframe)
21492 * @param {Roo.HtmlEditorCore} this
21497 * Fires when the editor is first receives the focus. Any insertion must wait
21498 * until after this event.
21499 * @param {Roo.HtmlEditorCore} this
21503 * @event beforesync
21504 * Fires before the textarea is updated with content from the editor iframe. Return false
21505 * to cancel the sync.
21506 * @param {Roo.HtmlEditorCore} this
21507 * @param {String} html
21511 * @event beforepush
21512 * Fires before the iframe editor is updated with content from the textarea. Return false
21513 * to cancel the push.
21514 * @param {Roo.HtmlEditorCore} this
21515 * @param {String} html
21520 * Fires when the textarea is updated with content from the editor iframe.
21521 * @param {Roo.HtmlEditorCore} this
21522 * @param {String} html
21527 * Fires when the iframe editor is updated with content from the textarea.
21528 * @param {Roo.HtmlEditorCore} this
21529 * @param {String} html
21534 * @event editorevent
21535 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21536 * @param {Roo.HtmlEditorCore} this
21542 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21544 // defaults : white / black...
21545 this.applyBlacklists();
21552 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21556 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21562 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21567 * @cfg {Number} height (in pixels)
21571 * @cfg {Number} width (in pixels)
21576 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21579 stylesheets: false,
21584 // private properties
21585 validationEvent : false,
21587 initialized : false,
21589 sourceEditMode : false,
21590 onFocus : Roo.emptyFn,
21592 hideMode:'offsets',
21596 // blacklist + whitelisted elements..
21603 * Protected method that will not generally be called directly. It
21604 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21605 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21607 getDocMarkup : function(){
21611 // inherit styels from page...??
21612 if (this.stylesheets === false) {
21614 Roo.get(document.head).select('style').each(function(node) {
21615 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21618 Roo.get(document.head).select('link').each(function(node) {
21619 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21622 } else if (!this.stylesheets.length) {
21624 st = '<style type="text/css">' +
21625 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21628 st = '<style type="text/css">' +
21633 st += '<style type="text/css">' +
21634 'IMG { cursor: pointer } ' +
21637 var cls = 'roo-htmleditor-body';
21639 if(this.bodyCls.length){
21640 cls += ' ' + this.bodyCls;
21643 return '<html><head>' + st +
21644 //<style type="text/css">' +
21645 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21647 ' </head><body class="' + cls + '"></body></html>';
21651 onRender : function(ct, position)
21654 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21655 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21658 this.el.dom.style.border = '0 none';
21659 this.el.dom.setAttribute('tabIndex', -1);
21660 this.el.addClass('x-hidden hide');
21664 if(Roo.isIE){ // fix IE 1px bogus margin
21665 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21669 this.frameId = Roo.id();
21673 var iframe = this.owner.wrap.createChild({
21675 cls: 'form-control', // bootstrap..
21677 name: this.frameId,
21678 frameBorder : 'no',
21679 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21684 this.iframe = iframe.dom;
21686 this.assignDocWin();
21688 this.doc.designMode = 'on';
21691 this.doc.write(this.getDocMarkup());
21695 var task = { // must defer to wait for browser to be ready
21697 //console.log("run task?" + this.doc.readyState);
21698 this.assignDocWin();
21699 if(this.doc.body || this.doc.readyState == 'complete'){
21701 this.doc.designMode="on";
21705 Roo.TaskMgr.stop(task);
21706 this.initEditor.defer(10, this);
21713 Roo.TaskMgr.start(task);
21718 onResize : function(w, h)
21720 Roo.log('resize: ' +w + ',' + h );
21721 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21725 if(typeof w == 'number'){
21727 this.iframe.style.width = w + 'px';
21729 if(typeof h == 'number'){
21731 this.iframe.style.height = h + 'px';
21733 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21740 * Toggles the editor between standard and source edit mode.
21741 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21743 toggleSourceEdit : function(sourceEditMode){
21745 this.sourceEditMode = sourceEditMode === true;
21747 if(this.sourceEditMode){
21749 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21752 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21753 //this.iframe.className = '';
21756 //this.setSize(this.owner.wrap.getSize());
21757 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21764 * Protected method that will not generally be called directly. If you need/want
21765 * custom HTML cleanup, this is the method you should override.
21766 * @param {String} html The HTML to be cleaned
21767 * return {String} The cleaned HTML
21769 cleanHtml : function(html){
21770 html = String(html);
21771 if(html.length > 5){
21772 if(Roo.isSafari){ // strip safari nonsense
21773 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21776 if(html == ' '){
21783 * HTML Editor -> Textarea
21784 * Protected method that will not generally be called directly. Syncs the contents
21785 * of the editor iframe with the textarea.
21787 syncValue : function(){
21788 if(this.initialized){
21789 var bd = (this.doc.body || this.doc.documentElement);
21790 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21791 var html = bd.innerHTML;
21793 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21794 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21796 html = '<div style="'+m[0]+'">' + html + '</div>';
21799 html = this.cleanHtml(html);
21800 // fix up the special chars.. normaly like back quotes in word...
21801 // however we do not want to do this with chinese..
21802 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21803 var cc = b.charCodeAt();
21805 (cc >= 0x4E00 && cc < 0xA000 ) ||
21806 (cc >= 0x3400 && cc < 0x4E00 ) ||
21807 (cc >= 0xf900 && cc < 0xfb00 )
21813 if(this.owner.fireEvent('beforesync', this, html) !== false){
21814 this.el.dom.value = html;
21815 this.owner.fireEvent('sync', this, html);
21821 * Protected method that will not generally be called directly. Pushes the value of the textarea
21822 * into the iframe editor.
21824 pushValue : function(){
21825 if(this.initialized){
21826 var v = this.el.dom.value.trim();
21828 // if(v.length < 1){
21832 if(this.owner.fireEvent('beforepush', this, v) !== false){
21833 var d = (this.doc.body || this.doc.documentElement);
21835 this.cleanUpPaste();
21836 this.el.dom.value = d.innerHTML;
21837 this.owner.fireEvent('push', this, v);
21843 deferFocus : function(){
21844 this.focus.defer(10, this);
21848 focus : function(){
21849 if(this.win && !this.sourceEditMode){
21856 assignDocWin: function()
21858 var iframe = this.iframe;
21861 this.doc = iframe.contentWindow.document;
21862 this.win = iframe.contentWindow;
21864 // if (!Roo.get(this.frameId)) {
21867 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21868 // this.win = Roo.get(this.frameId).dom.contentWindow;
21870 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21874 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21875 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21880 initEditor : function(){
21881 //console.log("INIT EDITOR");
21882 this.assignDocWin();
21886 this.doc.designMode="on";
21888 this.doc.write(this.getDocMarkup());
21891 var dbody = (this.doc.body || this.doc.documentElement);
21892 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21893 // this copies styles from the containing element into thsi one..
21894 // not sure why we need all of this..
21895 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21897 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21898 //ss['background-attachment'] = 'fixed'; // w3c
21899 dbody.bgProperties = 'fixed'; // ie
21900 //Roo.DomHelper.applyStyles(dbody, ss);
21901 Roo.EventManager.on(this.doc, {
21902 //'mousedown': this.onEditorEvent,
21903 'mouseup': this.onEditorEvent,
21904 'dblclick': this.onEditorEvent,
21905 'click': this.onEditorEvent,
21906 'keyup': this.onEditorEvent,
21911 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21913 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21914 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21916 this.initialized = true;
21918 this.owner.fireEvent('initialize', this);
21923 onDestroy : function(){
21929 //for (var i =0; i < this.toolbars.length;i++) {
21930 // // fixme - ask toolbars for heights?
21931 // this.toolbars[i].onDestroy();
21934 //this.wrap.dom.innerHTML = '';
21935 //this.wrap.remove();
21940 onFirstFocus : function(){
21942 this.assignDocWin();
21945 this.activated = true;
21948 if(Roo.isGecko){ // prevent silly gecko errors
21950 var s = this.win.getSelection();
21951 if(!s.focusNode || s.focusNode.nodeType != 3){
21952 var r = s.getRangeAt(0);
21953 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21958 this.execCmd('useCSS', true);
21959 this.execCmd('styleWithCSS', false);
21962 this.owner.fireEvent('activate', this);
21966 adjustFont: function(btn){
21967 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21968 //if(Roo.isSafari){ // safari
21971 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21972 if(Roo.isSafari){ // safari
21973 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21974 v = (v < 10) ? 10 : v;
21975 v = (v > 48) ? 48 : v;
21976 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21981 v = Math.max(1, v+adjust);
21983 this.execCmd('FontSize', v );
21986 onEditorEvent : function(e)
21988 this.owner.fireEvent('editorevent', this, e);
21989 // this.updateToolbar();
21990 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21993 insertTag : function(tg)
21995 // could be a bit smarter... -> wrap the current selected tRoo..
21996 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21998 range = this.createRange(this.getSelection());
21999 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22000 wrappingNode.appendChild(range.extractContents());
22001 range.insertNode(wrappingNode);
22008 this.execCmd("formatblock", tg);
22012 insertText : function(txt)
22016 var range = this.createRange();
22017 range.deleteContents();
22018 //alert(Sender.getAttribute('label'));
22020 range.insertNode(this.doc.createTextNode(txt));
22026 * Executes a Midas editor command on the editor document and performs necessary focus and
22027 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22028 * @param {String} cmd The Midas command
22029 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22031 relayCmd : function(cmd, value){
22033 this.execCmd(cmd, value);
22034 this.owner.fireEvent('editorevent', this);
22035 //this.updateToolbar();
22036 this.owner.deferFocus();
22040 * Executes a Midas editor command directly on the editor document.
22041 * For visual commands, you should use {@link #relayCmd} instead.
22042 * <b>This should only be called after the editor is initialized.</b>
22043 * @param {String} cmd The Midas command
22044 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22046 execCmd : function(cmd, value){
22047 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22054 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22056 * @param {String} text | dom node..
22058 insertAtCursor : function(text)
22061 if(!this.activated){
22067 var r = this.doc.selection.createRange();
22078 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22082 // from jquery ui (MIT licenced)
22084 var win = this.win;
22086 if (win.getSelection && win.getSelection().getRangeAt) {
22087 range = win.getSelection().getRangeAt(0);
22088 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22089 range.insertNode(node);
22090 } else if (win.document.selection && win.document.selection.createRange) {
22091 // no firefox support
22092 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22093 win.document.selection.createRange().pasteHTML(txt);
22095 // no firefox support
22096 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22097 this.execCmd('InsertHTML', txt);
22106 mozKeyPress : function(e){
22108 var c = e.getCharCode(), cmd;
22111 c = String.fromCharCode(c).toLowerCase();
22125 this.cleanUpPaste.defer(100, this);
22133 e.preventDefault();
22141 fixKeys : function(){ // load time branching for fastest keydown performance
22143 return function(e){
22144 var k = e.getKey(), r;
22147 r = this.doc.selection.createRange();
22150 r.pasteHTML('    ');
22157 r = this.doc.selection.createRange();
22159 var target = r.parentElement();
22160 if(!target || target.tagName.toLowerCase() != 'li'){
22162 r.pasteHTML('<br />');
22168 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22169 this.cleanUpPaste.defer(100, this);
22175 }else if(Roo.isOpera){
22176 return function(e){
22177 var k = e.getKey();
22181 this.execCmd('InsertHTML','    ');
22184 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22185 this.cleanUpPaste.defer(100, this);
22190 }else if(Roo.isSafari){
22191 return function(e){
22192 var k = e.getKey();
22196 this.execCmd('InsertText','\t');
22200 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22201 this.cleanUpPaste.defer(100, this);
22209 getAllAncestors: function()
22211 var p = this.getSelectedNode();
22214 a.push(p); // push blank onto stack..
22215 p = this.getParentElement();
22219 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22223 a.push(this.doc.body);
22227 lastSelNode : false,
22230 getSelection : function()
22232 this.assignDocWin();
22233 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22236 getSelectedNode: function()
22238 // this may only work on Gecko!!!
22240 // should we cache this!!!!
22245 var range = this.createRange(this.getSelection()).cloneRange();
22248 var parent = range.parentElement();
22250 var testRange = range.duplicate();
22251 testRange.moveToElementText(parent);
22252 if (testRange.inRange(range)) {
22255 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22258 parent = parent.parentElement;
22263 // is ancestor a text element.
22264 var ac = range.commonAncestorContainer;
22265 if (ac.nodeType == 3) {
22266 ac = ac.parentNode;
22269 var ar = ac.childNodes;
22272 var other_nodes = [];
22273 var has_other_nodes = false;
22274 for (var i=0;i<ar.length;i++) {
22275 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22278 // fullly contained node.
22280 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22285 // probably selected..
22286 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22287 other_nodes.push(ar[i]);
22291 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22296 has_other_nodes = true;
22298 if (!nodes.length && other_nodes.length) {
22299 nodes= other_nodes;
22301 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22307 createRange: function(sel)
22309 // this has strange effects when using with
22310 // top toolbar - not sure if it's a great idea.
22311 //this.editor.contentWindow.focus();
22312 if (typeof sel != "undefined") {
22314 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22316 return this.doc.createRange();
22319 return this.doc.createRange();
22322 getParentElement: function()
22325 this.assignDocWin();
22326 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22328 var range = this.createRange(sel);
22331 var p = range.commonAncestorContainer;
22332 while (p.nodeType == 3) { // text node
22343 * Range intersection.. the hard stuff...
22347 * [ -- selected range --- ]
22351 * if end is before start or hits it. fail.
22352 * if start is after end or hits it fail.
22354 * if either hits (but other is outside. - then it's not
22360 // @see http://www.thismuchiknow.co.uk/?p=64.
22361 rangeIntersectsNode : function(range, node)
22363 var nodeRange = node.ownerDocument.createRange();
22365 nodeRange.selectNode(node);
22367 nodeRange.selectNodeContents(node);
22370 var rangeStartRange = range.cloneRange();
22371 rangeStartRange.collapse(true);
22373 var rangeEndRange = range.cloneRange();
22374 rangeEndRange.collapse(false);
22376 var nodeStartRange = nodeRange.cloneRange();
22377 nodeStartRange.collapse(true);
22379 var nodeEndRange = nodeRange.cloneRange();
22380 nodeEndRange.collapse(false);
22382 return rangeStartRange.compareBoundaryPoints(
22383 Range.START_TO_START, nodeEndRange) == -1 &&
22384 rangeEndRange.compareBoundaryPoints(
22385 Range.START_TO_START, nodeStartRange) == 1;
22389 rangeCompareNode : function(range, node)
22391 var nodeRange = node.ownerDocument.createRange();
22393 nodeRange.selectNode(node);
22395 nodeRange.selectNodeContents(node);
22399 range.collapse(true);
22401 nodeRange.collapse(true);
22403 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22404 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22406 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22408 var nodeIsBefore = ss == 1;
22409 var nodeIsAfter = ee == -1;
22411 if (nodeIsBefore && nodeIsAfter) {
22414 if (!nodeIsBefore && nodeIsAfter) {
22415 return 1; //right trailed.
22418 if (nodeIsBefore && !nodeIsAfter) {
22419 return 2; // left trailed.
22425 // private? - in a new class?
22426 cleanUpPaste : function()
22428 // cleans up the whole document..
22429 Roo.log('cleanuppaste');
22431 this.cleanUpChildren(this.doc.body);
22432 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22433 if (clean != this.doc.body.innerHTML) {
22434 this.doc.body.innerHTML = clean;
22439 cleanWordChars : function(input) {// change the chars to hex code
22440 var he = Roo.HtmlEditorCore;
22442 var output = input;
22443 Roo.each(he.swapCodes, function(sw) {
22444 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22446 output = output.replace(swapper, sw[1]);
22453 cleanUpChildren : function (n)
22455 if (!n.childNodes.length) {
22458 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22459 this.cleanUpChild(n.childNodes[i]);
22466 cleanUpChild : function (node)
22469 //console.log(node);
22470 if (node.nodeName == "#text") {
22471 // clean up silly Windows -- stuff?
22474 if (node.nodeName == "#comment") {
22475 node.parentNode.removeChild(node);
22476 // clean up silly Windows -- stuff?
22479 var lcname = node.tagName.toLowerCase();
22480 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22481 // whitelist of tags..
22483 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22485 node.parentNode.removeChild(node);
22490 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22492 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22493 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22495 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22496 // remove_keep_children = true;
22499 if (remove_keep_children) {
22500 this.cleanUpChildren(node);
22501 // inserts everything just before this node...
22502 while (node.childNodes.length) {
22503 var cn = node.childNodes[0];
22504 node.removeChild(cn);
22505 node.parentNode.insertBefore(cn, node);
22507 node.parentNode.removeChild(node);
22511 if (!node.attributes || !node.attributes.length) {
22512 this.cleanUpChildren(node);
22516 function cleanAttr(n,v)
22519 if (v.match(/^\./) || v.match(/^\//)) {
22522 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22525 if (v.match(/^#/)) {
22528 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22529 node.removeAttribute(n);
22533 var cwhite = this.cwhite;
22534 var cblack = this.cblack;
22536 function cleanStyle(n,v)
22538 if (v.match(/expression/)) { //XSS?? should we even bother..
22539 node.removeAttribute(n);
22543 var parts = v.split(/;/);
22546 Roo.each(parts, function(p) {
22547 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22551 var l = p.split(':').shift().replace(/\s+/g,'');
22552 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22554 if ( cwhite.length && cblack.indexOf(l) > -1) {
22555 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22556 //node.removeAttribute(n);
22560 // only allow 'c whitelisted system attributes'
22561 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22562 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22563 //node.removeAttribute(n);
22573 if (clean.length) {
22574 node.setAttribute(n, clean.join(';'));
22576 node.removeAttribute(n);
22582 for (var i = node.attributes.length-1; i > -1 ; i--) {
22583 var a = node.attributes[i];
22586 if (a.name.toLowerCase().substr(0,2)=='on') {
22587 node.removeAttribute(a.name);
22590 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22591 node.removeAttribute(a.name);
22594 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22595 cleanAttr(a.name,a.value); // fixme..
22598 if (a.name == 'style') {
22599 cleanStyle(a.name,a.value);
22602 /// clean up MS crap..
22603 // tecnically this should be a list of valid class'es..
22606 if (a.name == 'class') {
22607 if (a.value.match(/^Mso/)) {
22608 node.className = '';
22611 if (a.value.match(/^body$/)) {
22612 node.className = '';
22623 this.cleanUpChildren(node);
22629 * Clean up MS wordisms...
22631 cleanWord : function(node)
22636 this.cleanWord(this.doc.body);
22639 if (node.nodeName == "#text") {
22640 // clean up silly Windows -- stuff?
22643 if (node.nodeName == "#comment") {
22644 node.parentNode.removeChild(node);
22645 // clean up silly Windows -- stuff?
22649 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22650 node.parentNode.removeChild(node);
22654 // remove - but keep children..
22655 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22656 while (node.childNodes.length) {
22657 var cn = node.childNodes[0];
22658 node.removeChild(cn);
22659 node.parentNode.insertBefore(cn, node);
22661 node.parentNode.removeChild(node);
22662 this.iterateChildren(node, this.cleanWord);
22666 if (node.className.length) {
22668 var cn = node.className.split(/\W+/);
22670 Roo.each(cn, function(cls) {
22671 if (cls.match(/Mso[a-zA-Z]+/)) {
22676 node.className = cna.length ? cna.join(' ') : '';
22678 node.removeAttribute("class");
22682 if (node.hasAttribute("lang")) {
22683 node.removeAttribute("lang");
22686 if (node.hasAttribute("style")) {
22688 var styles = node.getAttribute("style").split(";");
22690 Roo.each(styles, function(s) {
22691 if (!s.match(/:/)) {
22694 var kv = s.split(":");
22695 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22698 // what ever is left... we allow.
22701 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22702 if (!nstyle.length) {
22703 node.removeAttribute('style');
22706 this.iterateChildren(node, this.cleanWord);
22712 * iterateChildren of a Node, calling fn each time, using this as the scole..
22713 * @param {DomNode} node node to iterate children of.
22714 * @param {Function} fn method of this class to call on each item.
22716 iterateChildren : function(node, fn)
22718 if (!node.childNodes.length) {
22721 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22722 fn.call(this, node.childNodes[i])
22728 * cleanTableWidths.
22730 * Quite often pasting from word etc.. results in tables with column and widths.
22731 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22734 cleanTableWidths : function(node)
22739 this.cleanTableWidths(this.doc.body);
22744 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22747 Roo.log(node.tagName);
22748 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22749 this.iterateChildren(node, this.cleanTableWidths);
22752 if (node.hasAttribute('width')) {
22753 node.removeAttribute('width');
22757 if (node.hasAttribute("style")) {
22760 var styles = node.getAttribute("style").split(";");
22762 Roo.each(styles, function(s) {
22763 if (!s.match(/:/)) {
22766 var kv = s.split(":");
22767 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22770 // what ever is left... we allow.
22773 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22774 if (!nstyle.length) {
22775 node.removeAttribute('style');
22779 this.iterateChildren(node, this.cleanTableWidths);
22787 domToHTML : function(currentElement, depth, nopadtext) {
22789 depth = depth || 0;
22790 nopadtext = nopadtext || false;
22792 if (!currentElement) {
22793 return this.domToHTML(this.doc.body);
22796 //Roo.log(currentElement);
22798 var allText = false;
22799 var nodeName = currentElement.nodeName;
22800 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22802 if (nodeName == '#text') {
22804 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22809 if (nodeName != 'BODY') {
22812 // Prints the node tagName, such as <A>, <IMG>, etc
22815 for(i = 0; i < currentElement.attributes.length;i++) {
22817 var aname = currentElement.attributes.item(i).name;
22818 if (!currentElement.attributes.item(i).value.length) {
22821 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22824 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22833 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22836 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22841 // Traverse the tree
22843 var currentElementChild = currentElement.childNodes.item(i);
22844 var allText = true;
22845 var innerHTML = '';
22847 while (currentElementChild) {
22848 // Formatting code (indent the tree so it looks nice on the screen)
22849 var nopad = nopadtext;
22850 if (lastnode == 'SPAN') {
22854 if (currentElementChild.nodeName == '#text') {
22855 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22856 toadd = nopadtext ? toadd : toadd.trim();
22857 if (!nopad && toadd.length > 80) {
22858 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22860 innerHTML += toadd;
22863 currentElementChild = currentElement.childNodes.item(i);
22869 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22871 // Recursively traverse the tree structure of the child node
22872 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22873 lastnode = currentElementChild.nodeName;
22875 currentElementChild=currentElement.childNodes.item(i);
22881 // The remaining code is mostly for formatting the tree
22882 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22887 ret+= "</"+tagName+">";
22893 applyBlacklists : function()
22895 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22896 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22900 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22901 if (b.indexOf(tag) > -1) {
22904 this.white.push(tag);
22908 Roo.each(w, function(tag) {
22909 if (b.indexOf(tag) > -1) {
22912 if (this.white.indexOf(tag) > -1) {
22915 this.white.push(tag);
22920 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22921 if (w.indexOf(tag) > -1) {
22924 this.black.push(tag);
22928 Roo.each(b, function(tag) {
22929 if (w.indexOf(tag) > -1) {
22932 if (this.black.indexOf(tag) > -1) {
22935 this.black.push(tag);
22940 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22941 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22945 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22946 if (b.indexOf(tag) > -1) {
22949 this.cwhite.push(tag);
22953 Roo.each(w, function(tag) {
22954 if (b.indexOf(tag) > -1) {
22957 if (this.cwhite.indexOf(tag) > -1) {
22960 this.cwhite.push(tag);
22965 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22966 if (w.indexOf(tag) > -1) {
22969 this.cblack.push(tag);
22973 Roo.each(b, function(tag) {
22974 if (w.indexOf(tag) > -1) {
22977 if (this.cblack.indexOf(tag) > -1) {
22980 this.cblack.push(tag);
22985 setStylesheets : function(stylesheets)
22987 if(typeof(stylesheets) == 'string'){
22988 Roo.get(this.iframe.contentDocument.head).createChild({
22990 rel : 'stylesheet',
22999 Roo.each(stylesheets, function(s) {
23004 Roo.get(_this.iframe.contentDocument.head).createChild({
23006 rel : 'stylesheet',
23015 removeStylesheets : function()
23019 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23024 setStyle : function(style)
23026 Roo.get(this.iframe.contentDocument.head).createChild({
23035 // hide stuff that is not compatible
23049 * @event specialkey
23053 * @cfg {String} fieldClass @hide
23056 * @cfg {String} focusClass @hide
23059 * @cfg {String} autoCreate @hide
23062 * @cfg {String} inputType @hide
23065 * @cfg {String} invalidClass @hide
23068 * @cfg {String} invalidText @hide
23071 * @cfg {String} msgFx @hide
23074 * @cfg {String} validateOnBlur @hide
23078 Roo.HtmlEditorCore.white = [
23079 'area', 'br', 'img', 'input', 'hr', 'wbr',
23081 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23082 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23083 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23084 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23085 'table', 'ul', 'xmp',
23087 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23090 'dir', 'menu', 'ol', 'ul', 'dl',
23096 Roo.HtmlEditorCore.black = [
23097 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23099 'base', 'basefont', 'bgsound', 'blink', 'body',
23100 'frame', 'frameset', 'head', 'html', 'ilayer',
23101 'iframe', 'layer', 'link', 'meta', 'object',
23102 'script', 'style' ,'title', 'xml' // clean later..
23104 Roo.HtmlEditorCore.clean = [
23105 'script', 'style', 'title', 'xml'
23107 Roo.HtmlEditorCore.remove = [
23112 Roo.HtmlEditorCore.ablack = [
23116 Roo.HtmlEditorCore.aclean = [
23117 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23121 Roo.HtmlEditorCore.pwhite= [
23122 'http', 'https', 'mailto'
23125 // white listed style attributes.
23126 Roo.HtmlEditorCore.cwhite= [
23127 // 'text-align', /// default is to allow most things..
23133 // black listed style attributes.
23134 Roo.HtmlEditorCore.cblack= [
23135 // 'font-size' -- this can be set by the project
23139 Roo.HtmlEditorCore.swapCodes =[
23158 * @class Roo.bootstrap.HtmlEditor
23159 * @extends Roo.bootstrap.TextArea
23160 * Bootstrap HtmlEditor class
23163 * Create a new HtmlEditor
23164 * @param {Object} config The config object
23167 Roo.bootstrap.HtmlEditor = function(config){
23168 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23169 if (!this.toolbars) {
23170 this.toolbars = [];
23173 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23176 * @event initialize
23177 * Fires when the editor is fully initialized (including the iframe)
23178 * @param {HtmlEditor} this
23183 * Fires when the editor is first receives the focus. Any insertion must wait
23184 * until after this event.
23185 * @param {HtmlEditor} this
23189 * @event beforesync
23190 * Fires before the textarea is updated with content from the editor iframe. Return false
23191 * to cancel the sync.
23192 * @param {HtmlEditor} this
23193 * @param {String} html
23197 * @event beforepush
23198 * Fires before the iframe editor is updated with content from the textarea. Return false
23199 * to cancel the push.
23200 * @param {HtmlEditor} this
23201 * @param {String} html
23206 * Fires when the textarea is updated with content from the editor iframe.
23207 * @param {HtmlEditor} this
23208 * @param {String} html
23213 * Fires when the iframe editor is updated with content from the textarea.
23214 * @param {HtmlEditor} this
23215 * @param {String} html
23219 * @event editmodechange
23220 * Fires when the editor switches edit modes
23221 * @param {HtmlEditor} this
23222 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23224 editmodechange: true,
23226 * @event editorevent
23227 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23228 * @param {HtmlEditor} this
23232 * @event firstfocus
23233 * Fires when on first focus - needed by toolbars..
23234 * @param {HtmlEditor} this
23239 * Auto save the htmlEditor value as a file into Events
23240 * @param {HtmlEditor} this
23244 * @event savedpreview
23245 * preview the saved version of htmlEditor
23246 * @param {HtmlEditor} this
23253 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23257 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23262 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23267 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23272 * @cfg {Number} height (in pixels)
23276 * @cfg {Number} width (in pixels)
23281 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23284 stylesheets: false,
23289 // private properties
23290 validationEvent : false,
23292 initialized : false,
23295 onFocus : Roo.emptyFn,
23297 hideMode:'offsets',
23299 tbContainer : false,
23303 toolbarContainer :function() {
23304 return this.wrap.select('.x-html-editor-tb',true).first();
23308 * Protected method that will not generally be called directly. It
23309 * is called when the editor creates its toolbar. Override this method if you need to
23310 * add custom toolbar buttons.
23311 * @param {HtmlEditor} editor
23313 createToolbar : function(){
23314 Roo.log('renewing');
23315 Roo.log("create toolbars");
23317 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23318 this.toolbars[0].render(this.toolbarContainer());
23322 // if (!editor.toolbars || !editor.toolbars.length) {
23323 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23326 // for (var i =0 ; i < editor.toolbars.length;i++) {
23327 // editor.toolbars[i] = Roo.factory(
23328 // typeof(editor.toolbars[i]) == 'string' ?
23329 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23330 // Roo.bootstrap.HtmlEditor);
23331 // editor.toolbars[i].init(editor);
23337 onRender : function(ct, position)
23339 // Roo.log("Call onRender: " + this.xtype);
23341 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23343 this.wrap = this.inputEl().wrap({
23344 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23347 this.editorcore.onRender(ct, position);
23349 if (this.resizable) {
23350 this.resizeEl = new Roo.Resizable(this.wrap, {
23354 minHeight : this.height,
23355 height: this.height,
23356 handles : this.resizable,
23359 resize : function(r, w, h) {
23360 _t.onResize(w,h); // -something
23366 this.createToolbar(this);
23369 if(!this.width && this.resizable){
23370 this.setSize(this.wrap.getSize());
23372 if (this.resizeEl) {
23373 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23374 // should trigger onReize..
23380 onResize : function(w, h)
23382 Roo.log('resize: ' +w + ',' + h );
23383 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23387 if(this.inputEl() ){
23388 if(typeof w == 'number'){
23389 var aw = w - this.wrap.getFrameWidth('lr');
23390 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23393 if(typeof h == 'number'){
23394 var tbh = -11; // fixme it needs to tool bar size!
23395 for (var i =0; i < this.toolbars.length;i++) {
23396 // fixme - ask toolbars for heights?
23397 tbh += this.toolbars[i].el.getHeight();
23398 //if (this.toolbars[i].footer) {
23399 // tbh += this.toolbars[i].footer.el.getHeight();
23407 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23408 ah -= 5; // knock a few pixes off for look..
23409 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23413 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23414 this.editorcore.onResize(ew,eh);
23419 * Toggles the editor between standard and source edit mode.
23420 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23422 toggleSourceEdit : function(sourceEditMode)
23424 this.editorcore.toggleSourceEdit(sourceEditMode);
23426 if(this.editorcore.sourceEditMode){
23427 Roo.log('editor - showing textarea');
23430 // Roo.log(this.syncValue());
23432 this.inputEl().removeClass(['hide', 'x-hidden']);
23433 this.inputEl().dom.removeAttribute('tabIndex');
23434 this.inputEl().focus();
23436 Roo.log('editor - hiding textarea');
23438 // Roo.log(this.pushValue());
23441 this.inputEl().addClass(['hide', 'x-hidden']);
23442 this.inputEl().dom.setAttribute('tabIndex', -1);
23443 //this.deferFocus();
23446 if(this.resizable){
23447 this.setSize(this.wrap.getSize());
23450 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23453 // private (for BoxComponent)
23454 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23456 // private (for BoxComponent)
23457 getResizeEl : function(){
23461 // private (for BoxComponent)
23462 getPositionEl : function(){
23467 initEvents : function(){
23468 this.originalValue = this.getValue();
23472 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23475 // markInvalid : Roo.emptyFn,
23477 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23480 // clearInvalid : Roo.emptyFn,
23482 setValue : function(v){
23483 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23484 this.editorcore.pushValue();
23489 deferFocus : function(){
23490 this.focus.defer(10, this);
23494 focus : function(){
23495 this.editorcore.focus();
23501 onDestroy : function(){
23507 for (var i =0; i < this.toolbars.length;i++) {
23508 // fixme - ask toolbars for heights?
23509 this.toolbars[i].onDestroy();
23512 this.wrap.dom.innerHTML = '';
23513 this.wrap.remove();
23518 onFirstFocus : function(){
23519 //Roo.log("onFirstFocus");
23520 this.editorcore.onFirstFocus();
23521 for (var i =0; i < this.toolbars.length;i++) {
23522 this.toolbars[i].onFirstFocus();
23528 syncValue : function()
23530 this.editorcore.syncValue();
23533 pushValue : function()
23535 this.editorcore.pushValue();
23539 // hide stuff that is not compatible
23553 * @event specialkey
23557 * @cfg {String} fieldClass @hide
23560 * @cfg {String} focusClass @hide
23563 * @cfg {String} autoCreate @hide
23566 * @cfg {String} inputType @hide
23569 * @cfg {String} invalidClass @hide
23572 * @cfg {String} invalidText @hide
23575 * @cfg {String} msgFx @hide
23578 * @cfg {String} validateOnBlur @hide
23587 Roo.namespace('Roo.bootstrap.htmleditor');
23589 * @class Roo.bootstrap.HtmlEditorToolbar1
23594 new Roo.bootstrap.HtmlEditor({
23597 new Roo.bootstrap.HtmlEditorToolbar1({
23598 disable : { fonts: 1 , format: 1, ..., ... , ...],
23604 * @cfg {Object} disable List of elements to disable..
23605 * @cfg {Array} btns List of additional buttons.
23609 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23612 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23615 Roo.apply(this, config);
23617 // default disabled, based on 'good practice'..
23618 this.disable = this.disable || {};
23619 Roo.applyIf(this.disable, {
23622 specialElements : true
23624 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23626 this.editor = config.editor;
23627 this.editorcore = config.editor.editorcore;
23629 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23631 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23632 // dont call parent... till later.
23634 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23639 editorcore : false,
23644 "h1","h2","h3","h4","h5","h6",
23646 "abbr", "acronym", "address", "cite", "samp", "var",
23650 onRender : function(ct, position)
23652 // Roo.log("Call onRender: " + this.xtype);
23654 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23656 this.el.dom.style.marginBottom = '0';
23658 var editorcore = this.editorcore;
23659 var editor= this.editor;
23662 var btn = function(id,cmd , toggle, handler, html){
23664 var event = toggle ? 'toggle' : 'click';
23669 xns: Roo.bootstrap,
23672 enableToggle:toggle !== false,
23674 pressed : toggle ? false : null,
23677 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23678 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23684 // var cb_box = function...
23689 xns: Roo.bootstrap,
23690 glyphicon : 'font',
23694 xns: Roo.bootstrap,
23698 Roo.each(this.formats, function(f) {
23699 style.menu.items.push({
23701 xns: Roo.bootstrap,
23702 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23707 editorcore.insertTag(this.tagname);
23714 children.push(style);
23716 btn('bold',false,true);
23717 btn('italic',false,true);
23718 btn('align-left', 'justifyleft',true);
23719 btn('align-center', 'justifycenter',true);
23720 btn('align-right' , 'justifyright',true);
23721 btn('link', false, false, function(btn) {
23722 //Roo.log("create link?");
23723 var url = prompt(this.createLinkText, this.defaultLinkValue);
23724 if(url && url != 'http:/'+'/'){
23725 this.editorcore.relayCmd('createlink', url);
23728 btn('list','insertunorderedlist',true);
23729 btn('pencil', false,true, function(btn){
23731 this.toggleSourceEdit(btn.pressed);
23734 if (this.editor.btns.length > 0) {
23735 for (var i = 0; i<this.editor.btns.length; i++) {
23736 children.push(this.editor.btns[i]);
23744 xns: Roo.bootstrap,
23749 xns: Roo.bootstrap,
23754 cog.menu.items.push({
23756 xns: Roo.bootstrap,
23757 html : Clean styles,
23762 editorcore.insertTag(this.tagname);
23771 this.xtype = 'NavSimplebar';
23773 for(var i=0;i< children.length;i++) {
23775 this.buttons.add(this.addxtypeChild(children[i]));
23779 editor.on('editorevent', this.updateToolbar, this);
23781 onBtnClick : function(id)
23783 this.editorcore.relayCmd(id);
23784 this.editorcore.focus();
23788 * Protected method that will not generally be called directly. It triggers
23789 * a toolbar update by reading the markup state of the current selection in the editor.
23791 updateToolbar: function(){
23793 if(!this.editorcore.activated){
23794 this.editor.onFirstFocus(); // is this neeed?
23798 var btns = this.buttons;
23799 var doc = this.editorcore.doc;
23800 btns.get('bold').setActive(doc.queryCommandState('bold'));
23801 btns.get('italic').setActive(doc.queryCommandState('italic'));
23802 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23804 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23805 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23806 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23808 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23809 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23812 var ans = this.editorcore.getAllAncestors();
23813 if (this.formatCombo) {
23816 var store = this.formatCombo.store;
23817 this.formatCombo.setValue("");
23818 for (var i =0; i < ans.length;i++) {
23819 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23821 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23829 // hides menus... - so this cant be on a menu...
23830 Roo.bootstrap.MenuMgr.hideAll();
23832 Roo.bootstrap.MenuMgr.hideAll();
23833 //this.editorsyncValue();
23835 onFirstFocus: function() {
23836 this.buttons.each(function(item){
23840 toggleSourceEdit : function(sourceEditMode){
23843 if(sourceEditMode){
23844 Roo.log("disabling buttons");
23845 this.buttons.each( function(item){
23846 if(item.cmd != 'pencil'){
23852 Roo.log("enabling buttons");
23853 if(this.editorcore.initialized){
23854 this.buttons.each( function(item){
23860 Roo.log("calling toggole on editor");
23861 // tell the editor that it's been pressed..
23862 this.editor.toggleSourceEdit(sourceEditMode);
23872 * @class Roo.bootstrap.Table.AbstractSelectionModel
23873 * @extends Roo.util.Observable
23874 * Abstract base class for grid SelectionModels. It provides the interface that should be
23875 * implemented by descendant classes. This class should not be directly instantiated.
23878 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23879 this.locked = false;
23880 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23884 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23885 /** @ignore Called by the grid automatically. Do not call directly. */
23886 init : function(grid){
23892 * Locks the selections.
23895 this.locked = true;
23899 * Unlocks the selections.
23901 unlock : function(){
23902 this.locked = false;
23906 * Returns true if the selections are locked.
23907 * @return {Boolean}
23909 isLocked : function(){
23910 return this.locked;
23914 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23915 * @class Roo.bootstrap.Table.RowSelectionModel
23916 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23917 * It supports multiple selections and keyboard selection/navigation.
23919 * @param {Object} config
23922 Roo.bootstrap.Table.RowSelectionModel = function(config){
23923 Roo.apply(this, config);
23924 this.selections = new Roo.util.MixedCollection(false, function(o){
23929 this.lastActive = false;
23933 * @event selectionchange
23934 * Fires when the selection changes
23935 * @param {SelectionModel} this
23937 "selectionchange" : true,
23939 * @event afterselectionchange
23940 * Fires after the selection changes (eg. by key press or clicking)
23941 * @param {SelectionModel} this
23943 "afterselectionchange" : true,
23945 * @event beforerowselect
23946 * Fires when a row is selected being selected, return false to cancel.
23947 * @param {SelectionModel} this
23948 * @param {Number} rowIndex The selected index
23949 * @param {Boolean} keepExisting False if other selections will be cleared
23951 "beforerowselect" : true,
23954 * Fires when a row is selected.
23955 * @param {SelectionModel} this
23956 * @param {Number} rowIndex The selected index
23957 * @param {Roo.data.Record} r The record
23959 "rowselect" : true,
23961 * @event rowdeselect
23962 * Fires when a row is deselected.
23963 * @param {SelectionModel} this
23964 * @param {Number} rowIndex The selected index
23966 "rowdeselect" : true
23968 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23969 this.locked = false;
23972 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23974 * @cfg {Boolean} singleSelect
23975 * True to allow selection of only one row at a time (defaults to false)
23977 singleSelect : false,
23980 initEvents : function()
23983 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23984 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23985 //}else{ // allow click to work like normal
23986 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23988 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23989 this.grid.on("rowclick", this.handleMouseDown, this);
23991 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23992 "up" : function(e){
23994 this.selectPrevious(e.shiftKey);
23995 }else if(this.last !== false && this.lastActive !== false){
23996 var last = this.last;
23997 this.selectRange(this.last, this.lastActive-1);
23998 this.grid.getView().focusRow(this.lastActive);
23999 if(last !== false){
24003 this.selectFirstRow();
24005 this.fireEvent("afterselectionchange", this);
24007 "down" : function(e){
24009 this.selectNext(e.shiftKey);
24010 }else if(this.last !== false && this.lastActive !== false){
24011 var last = this.last;
24012 this.selectRange(this.last, this.lastActive+1);
24013 this.grid.getView().focusRow(this.lastActive);
24014 if(last !== false){
24018 this.selectFirstRow();
24020 this.fireEvent("afterselectionchange", this);
24024 this.grid.store.on('load', function(){
24025 this.selections.clear();
24028 var view = this.grid.view;
24029 view.on("refresh", this.onRefresh, this);
24030 view.on("rowupdated", this.onRowUpdated, this);
24031 view.on("rowremoved", this.onRemove, this);
24036 onRefresh : function()
24038 var ds = this.grid.store, i, v = this.grid.view;
24039 var s = this.selections;
24040 s.each(function(r){
24041 if((i = ds.indexOfId(r.id)) != -1){
24050 onRemove : function(v, index, r){
24051 this.selections.remove(r);
24055 onRowUpdated : function(v, index, r){
24056 if(this.isSelected(r)){
24057 v.onRowSelect(index);
24063 * @param {Array} records The records to select
24064 * @param {Boolean} keepExisting (optional) True to keep existing selections
24066 selectRecords : function(records, keepExisting)
24069 this.clearSelections();
24071 var ds = this.grid.store;
24072 for(var i = 0, len = records.length; i < len; i++){
24073 this.selectRow(ds.indexOf(records[i]), true);
24078 * Gets the number of selected rows.
24081 getCount : function(){
24082 return this.selections.length;
24086 * Selects the first row in the grid.
24088 selectFirstRow : function(){
24093 * Select the last row.
24094 * @param {Boolean} keepExisting (optional) True to keep existing selections
24096 selectLastRow : function(keepExisting){
24097 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24098 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24102 * Selects the row immediately following the last selected row.
24103 * @param {Boolean} keepExisting (optional) True to keep existing selections
24105 selectNext : function(keepExisting)
24107 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24108 this.selectRow(this.last+1, keepExisting);
24109 this.grid.getView().focusRow(this.last);
24114 * Selects the row that precedes the last selected row.
24115 * @param {Boolean} keepExisting (optional) True to keep existing selections
24117 selectPrevious : function(keepExisting){
24119 this.selectRow(this.last-1, keepExisting);
24120 this.grid.getView().focusRow(this.last);
24125 * Returns the selected records
24126 * @return {Array} Array of selected records
24128 getSelections : function(){
24129 return [].concat(this.selections.items);
24133 * Returns the first selected record.
24136 getSelected : function(){
24137 return this.selections.itemAt(0);
24142 * Clears all selections.
24144 clearSelections : function(fast)
24150 var ds = this.grid.store;
24151 var s = this.selections;
24152 s.each(function(r){
24153 this.deselectRow(ds.indexOfId(r.id));
24157 this.selections.clear();
24164 * Selects all rows.
24166 selectAll : function(){
24170 this.selections.clear();
24171 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24172 this.selectRow(i, true);
24177 * Returns True if there is a selection.
24178 * @return {Boolean}
24180 hasSelection : function(){
24181 return this.selections.length > 0;
24185 * Returns True if the specified row is selected.
24186 * @param {Number/Record} record The record or index of the record to check
24187 * @return {Boolean}
24189 isSelected : function(index){
24190 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24191 return (r && this.selections.key(r.id) ? true : false);
24195 * Returns True if the specified record id is selected.
24196 * @param {String} id The id of record to check
24197 * @return {Boolean}
24199 isIdSelected : function(id){
24200 return (this.selections.key(id) ? true : false);
24205 handleMouseDBClick : function(e, t){
24209 handleMouseDown : function(e, t)
24211 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24212 if(this.isLocked() || rowIndex < 0 ){
24215 if(e.shiftKey && this.last !== false){
24216 var last = this.last;
24217 this.selectRange(last, rowIndex, e.ctrlKey);
24218 this.last = last; // reset the last
24222 var isSelected = this.isSelected(rowIndex);
24223 //Roo.log("select row:" + rowIndex);
24225 this.deselectRow(rowIndex);
24227 this.selectRow(rowIndex, true);
24231 if(e.button !== 0 && isSelected){
24232 alert('rowIndex 2: ' + rowIndex);
24233 view.focusRow(rowIndex);
24234 }else if(e.ctrlKey && isSelected){
24235 this.deselectRow(rowIndex);
24236 }else if(!isSelected){
24237 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24238 view.focusRow(rowIndex);
24242 this.fireEvent("afterselectionchange", this);
24245 handleDragableRowClick : function(grid, rowIndex, e)
24247 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24248 this.selectRow(rowIndex, false);
24249 grid.view.focusRow(rowIndex);
24250 this.fireEvent("afterselectionchange", this);
24255 * Selects multiple rows.
24256 * @param {Array} rows Array of the indexes of the row to select
24257 * @param {Boolean} keepExisting (optional) True to keep existing selections
24259 selectRows : function(rows, keepExisting){
24261 this.clearSelections();
24263 for(var i = 0, len = rows.length; i < len; i++){
24264 this.selectRow(rows[i], true);
24269 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24270 * @param {Number} startRow The index of the first row in the range
24271 * @param {Number} endRow The index of the last row in the range
24272 * @param {Boolean} keepExisting (optional) True to retain existing selections
24274 selectRange : function(startRow, endRow, keepExisting){
24279 this.clearSelections();
24281 if(startRow <= endRow){
24282 for(var i = startRow; i <= endRow; i++){
24283 this.selectRow(i, true);
24286 for(var i = startRow; i >= endRow; i--){
24287 this.selectRow(i, true);
24293 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24294 * @param {Number} startRow The index of the first row in the range
24295 * @param {Number} endRow The index of the last row in the range
24297 deselectRange : function(startRow, endRow, preventViewNotify){
24301 for(var i = startRow; i <= endRow; i++){
24302 this.deselectRow(i, preventViewNotify);
24308 * @param {Number} row The index of the row to select
24309 * @param {Boolean} keepExisting (optional) True to keep existing selections
24311 selectRow : function(index, keepExisting, preventViewNotify)
24313 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24316 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24317 if(!keepExisting || this.singleSelect){
24318 this.clearSelections();
24321 var r = this.grid.store.getAt(index);
24322 //console.log('selectRow - record id :' + r.id);
24324 this.selections.add(r);
24325 this.last = this.lastActive = index;
24326 if(!preventViewNotify){
24327 var proxy = new Roo.Element(
24328 this.grid.getRowDom(index)
24330 proxy.addClass('bg-info info');
24332 this.fireEvent("rowselect", this, index, r);
24333 this.fireEvent("selectionchange", this);
24339 * @param {Number} row The index of the row to deselect
24341 deselectRow : function(index, preventViewNotify)
24346 if(this.last == index){
24349 if(this.lastActive == index){
24350 this.lastActive = false;
24353 var r = this.grid.store.getAt(index);
24358 this.selections.remove(r);
24359 //.console.log('deselectRow - record id :' + r.id);
24360 if(!preventViewNotify){
24362 var proxy = new Roo.Element(
24363 this.grid.getRowDom(index)
24365 proxy.removeClass('bg-info info');
24367 this.fireEvent("rowdeselect", this, index);
24368 this.fireEvent("selectionchange", this);
24372 restoreLast : function(){
24374 this.last = this._last;
24379 acceptsNav : function(row, col, cm){
24380 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24384 onEditorKey : function(field, e){
24385 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24390 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24392 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24394 }else if(k == e.ENTER && !e.ctrlKey){
24398 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24400 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24402 }else if(k == e.ESC){
24406 g.startEditing(newCell[0], newCell[1]);
24412 * Ext JS Library 1.1.1
24413 * Copyright(c) 2006-2007, Ext JS, LLC.
24415 * Originally Released Under LGPL - original licence link has changed is not relivant.
24418 * <script type="text/javascript">
24422 * @class Roo.bootstrap.PagingToolbar
24423 * @extends Roo.bootstrap.NavSimplebar
24424 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24426 * Create a new PagingToolbar
24427 * @param {Object} config The config object
24428 * @param {Roo.data.Store} store
24430 Roo.bootstrap.PagingToolbar = function(config)
24432 // old args format still supported... - xtype is prefered..
24433 // created from xtype...
24435 this.ds = config.dataSource;
24437 if (config.store && !this.ds) {
24438 this.store= Roo.factory(config.store, Roo.data);
24439 this.ds = this.store;
24440 this.ds.xmodule = this.xmodule || false;
24443 this.toolbarItems = [];
24444 if (config.items) {
24445 this.toolbarItems = config.items;
24448 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24453 this.bind(this.ds);
24456 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24460 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24462 * @cfg {Roo.data.Store} dataSource
24463 * The underlying data store providing the paged data
24466 * @cfg {String/HTMLElement/Element} container
24467 * container The id or element that will contain the toolbar
24470 * @cfg {Boolean} displayInfo
24471 * True to display the displayMsg (defaults to false)
24474 * @cfg {Number} pageSize
24475 * The number of records to display per page (defaults to 20)
24479 * @cfg {String} displayMsg
24480 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24482 displayMsg : 'Displaying {0} - {1} of {2}',
24484 * @cfg {String} emptyMsg
24485 * The message to display when no records are found (defaults to "No data to display")
24487 emptyMsg : 'No data to display',
24489 * Customizable piece of the default paging text (defaults to "Page")
24492 beforePageText : "Page",
24494 * Customizable piece of the default paging text (defaults to "of %0")
24497 afterPageText : "of {0}",
24499 * Customizable piece of the default paging text (defaults to "First Page")
24502 firstText : "First Page",
24504 * Customizable piece of the default paging text (defaults to "Previous Page")
24507 prevText : "Previous Page",
24509 * Customizable piece of the default paging text (defaults to "Next Page")
24512 nextText : "Next Page",
24514 * Customizable piece of the default paging text (defaults to "Last Page")
24517 lastText : "Last Page",
24519 * Customizable piece of the default paging text (defaults to "Refresh")
24522 refreshText : "Refresh",
24526 onRender : function(ct, position)
24528 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24529 this.navgroup.parentId = this.id;
24530 this.navgroup.onRender(this.el, null);
24531 // add the buttons to the navgroup
24533 if(this.displayInfo){
24534 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24535 this.displayEl = this.el.select('.x-paging-info', true).first();
24536 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24537 // this.displayEl = navel.el.select('span',true).first();
24543 Roo.each(_this.buttons, function(e){ // this might need to use render????
24544 Roo.factory(e).render(_this.el);
24548 Roo.each(_this.toolbarItems, function(e) {
24549 _this.navgroup.addItem(e);
24553 this.first = this.navgroup.addItem({
24554 tooltip: this.firstText,
24556 icon : 'fa fa-backward',
24558 preventDefault: true,
24559 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24562 this.prev = this.navgroup.addItem({
24563 tooltip: this.prevText,
24565 icon : 'fa fa-step-backward',
24567 preventDefault: true,
24568 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24570 //this.addSeparator();
24573 var field = this.navgroup.addItem( {
24575 cls : 'x-paging-position',
24577 html : this.beforePageText +
24578 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24579 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24582 this.field = field.el.select('input', true).first();
24583 this.field.on("keydown", this.onPagingKeydown, this);
24584 this.field.on("focus", function(){this.dom.select();});
24587 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24588 //this.field.setHeight(18);
24589 //this.addSeparator();
24590 this.next = this.navgroup.addItem({
24591 tooltip: this.nextText,
24593 html : ' <i class="fa fa-step-forward">',
24595 preventDefault: true,
24596 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24598 this.last = this.navgroup.addItem({
24599 tooltip: this.lastText,
24600 icon : 'fa fa-forward',
24603 preventDefault: true,
24604 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24606 //this.addSeparator();
24607 this.loading = this.navgroup.addItem({
24608 tooltip: this.refreshText,
24609 icon: 'fa fa-refresh',
24610 preventDefault: true,
24611 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24617 updateInfo : function(){
24618 if(this.displayEl){
24619 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24620 var msg = count == 0 ?
24624 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24626 this.displayEl.update(msg);
24631 onLoad : function(ds, r, o)
24633 this.cursor = o.params.start ? o.params.start : 0;
24635 var d = this.getPageData(),
24640 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24641 this.field.dom.value = ap;
24642 this.first.setDisabled(ap == 1);
24643 this.prev.setDisabled(ap == 1);
24644 this.next.setDisabled(ap == ps);
24645 this.last.setDisabled(ap == ps);
24646 this.loading.enable();
24651 getPageData : function(){
24652 var total = this.ds.getTotalCount();
24655 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24656 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24661 onLoadError : function(){
24662 this.loading.enable();
24666 onPagingKeydown : function(e){
24667 var k = e.getKey();
24668 var d = this.getPageData();
24670 var v = this.field.dom.value, pageNum;
24671 if(!v || isNaN(pageNum = parseInt(v, 10))){
24672 this.field.dom.value = d.activePage;
24675 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24676 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24679 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))
24681 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24682 this.field.dom.value = pageNum;
24683 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24686 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24688 var v = this.field.dom.value, pageNum;
24689 var increment = (e.shiftKey) ? 10 : 1;
24690 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24693 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24694 this.field.dom.value = d.activePage;
24697 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24699 this.field.dom.value = parseInt(v, 10) + increment;
24700 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24701 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24708 beforeLoad : function(){
24710 this.loading.disable();
24715 onClick : function(which){
24724 ds.load({params:{start: 0, limit: this.pageSize}});
24727 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24730 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24733 var total = ds.getTotalCount();
24734 var extra = total % this.pageSize;
24735 var lastStart = extra ? (total - extra) : total-this.pageSize;
24736 ds.load({params:{start: lastStart, limit: this.pageSize}});
24739 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24745 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24746 * @param {Roo.data.Store} store The data store to unbind
24748 unbind : function(ds){
24749 ds.un("beforeload", this.beforeLoad, this);
24750 ds.un("load", this.onLoad, this);
24751 ds.un("loadexception", this.onLoadError, this);
24752 ds.un("remove", this.updateInfo, this);
24753 ds.un("add", this.updateInfo, this);
24754 this.ds = undefined;
24758 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24759 * @param {Roo.data.Store} store The data store to bind
24761 bind : function(ds){
24762 ds.on("beforeload", this.beforeLoad, this);
24763 ds.on("load", this.onLoad, this);
24764 ds.on("loadexception", this.onLoadError, this);
24765 ds.on("remove", this.updateInfo, this);
24766 ds.on("add", this.updateInfo, this);
24777 * @class Roo.bootstrap.MessageBar
24778 * @extends Roo.bootstrap.Component
24779 * Bootstrap MessageBar class
24780 * @cfg {String} html contents of the MessageBar
24781 * @cfg {String} weight (info | success | warning | danger) default info
24782 * @cfg {String} beforeClass insert the bar before the given class
24783 * @cfg {Boolean} closable (true | false) default false
24784 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24787 * Create a new Element
24788 * @param {Object} config The config object
24791 Roo.bootstrap.MessageBar = function(config){
24792 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24795 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24801 beforeClass: 'bootstrap-sticky-wrap',
24803 getAutoCreate : function(){
24807 cls: 'alert alert-dismissable alert-' + this.weight,
24812 html: this.html || ''
24818 cfg.cls += ' alert-messages-fixed';
24832 onRender : function(ct, position)
24834 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24837 var cfg = Roo.apply({}, this.getAutoCreate());
24841 cfg.cls += ' ' + this.cls;
24844 cfg.style = this.style;
24846 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24848 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24851 this.el.select('>button.close').on('click', this.hide, this);
24857 if (!this.rendered) {
24863 this.fireEvent('show', this);
24869 if (!this.rendered) {
24875 this.fireEvent('hide', this);
24878 update : function()
24880 // var e = this.el.dom.firstChild;
24882 // if(this.closable){
24883 // e = e.nextSibling;
24886 // e.data = this.html || '';
24888 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24904 * @class Roo.bootstrap.Graph
24905 * @extends Roo.bootstrap.Component
24906 * Bootstrap Graph class
24910 @cfg {String} graphtype bar | vbar | pie
24911 @cfg {number} g_x coodinator | centre x (pie)
24912 @cfg {number} g_y coodinator | centre y (pie)
24913 @cfg {number} g_r radius (pie)
24914 @cfg {number} g_height height of the chart (respected by all elements in the set)
24915 @cfg {number} g_width width of the chart (respected by all elements in the set)
24916 @cfg {Object} title The title of the chart
24919 -opts (object) options for the chart
24921 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24922 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24924 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.
24925 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24927 o stretch (boolean)
24929 -opts (object) options for the pie
24932 o startAngle (number)
24933 o endAngle (number)
24937 * Create a new Input
24938 * @param {Object} config The config object
24941 Roo.bootstrap.Graph = function(config){
24942 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24948 * The img click event for the img.
24949 * @param {Roo.EventObject} e
24955 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24966 //g_colors: this.colors,
24973 getAutoCreate : function(){
24984 onRender : function(ct,position){
24987 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24989 if (typeof(Raphael) == 'undefined') {
24990 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24994 this.raphael = Raphael(this.el.dom);
24996 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24997 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24998 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24999 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25001 r.text(160, 10, "Single Series Chart").attr(txtattr);
25002 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25003 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25004 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25006 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25007 r.barchart(330, 10, 300, 220, data1);
25008 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25009 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25012 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25013 // r.barchart(30, 30, 560, 250, xdata, {
25014 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25015 // axis : "0 0 1 1",
25016 // axisxlabels : xdata
25017 // //yvalues : cols,
25020 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25022 // this.load(null,xdata,{
25023 // axis : "0 0 1 1",
25024 // axisxlabels : xdata
25029 load : function(graphtype,xdata,opts)
25031 this.raphael.clear();
25033 graphtype = this.graphtype;
25038 var r = this.raphael,
25039 fin = function () {
25040 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25042 fout = function () {
25043 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25045 pfin = function() {
25046 this.sector.stop();
25047 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25050 this.label[0].stop();
25051 this.label[0].attr({ r: 7.5 });
25052 this.label[1].attr({ "font-weight": 800 });
25055 pfout = function() {
25056 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25059 this.label[0].animate({ r: 5 }, 500, "bounce");
25060 this.label[1].attr({ "font-weight": 400 });
25066 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25069 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25072 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25073 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25075 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25082 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25087 setTitle: function(o)
25092 initEvents: function() {
25095 this.el.on('click', this.onClick, this);
25099 onClick : function(e)
25101 Roo.log('img onclick');
25102 this.fireEvent('click', this, e);
25114 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25117 * @class Roo.bootstrap.dash.NumberBox
25118 * @extends Roo.bootstrap.Component
25119 * Bootstrap NumberBox class
25120 * @cfg {String} headline Box headline
25121 * @cfg {String} content Box content
25122 * @cfg {String} icon Box icon
25123 * @cfg {String} footer Footer text
25124 * @cfg {String} fhref Footer href
25127 * Create a new NumberBox
25128 * @param {Object} config The config object
25132 Roo.bootstrap.dash.NumberBox = function(config){
25133 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25137 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25146 getAutoCreate : function(){
25150 cls : 'small-box ',
25158 cls : 'roo-headline',
25159 html : this.headline
25163 cls : 'roo-content',
25164 html : this.content
25178 cls : 'ion ' + this.icon
25187 cls : 'small-box-footer',
25188 href : this.fhref || '#',
25192 cfg.cn.push(footer);
25199 onRender : function(ct,position){
25200 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25207 setHeadline: function (value)
25209 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25212 setFooter: function (value, href)
25214 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25217 this.el.select('a.small-box-footer',true).first().attr('href', href);
25222 setContent: function (value)
25224 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25227 initEvents: function()
25241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25244 * @class Roo.bootstrap.dash.TabBox
25245 * @extends Roo.bootstrap.Component
25246 * Bootstrap TabBox class
25247 * @cfg {String} title Title of the TabBox
25248 * @cfg {String} icon Icon of the TabBox
25249 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25250 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25253 * Create a new TabBox
25254 * @param {Object} config The config object
25258 Roo.bootstrap.dash.TabBox = function(config){
25259 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25264 * When a pane is added
25265 * @param {Roo.bootstrap.dash.TabPane} pane
25269 * @event activatepane
25270 * When a pane is activated
25271 * @param {Roo.bootstrap.dash.TabPane} pane
25273 "activatepane" : true
25281 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25286 tabScrollable : false,
25288 getChildContainer : function()
25290 return this.el.select('.tab-content', true).first();
25293 getAutoCreate : function(){
25297 cls: 'pull-left header',
25305 cls: 'fa ' + this.icon
25311 cls: 'nav nav-tabs pull-right',
25317 if(this.tabScrollable){
25324 cls: 'nav nav-tabs pull-right',
25335 cls: 'nav-tabs-custom',
25340 cls: 'tab-content no-padding',
25348 initEvents : function()
25350 //Roo.log('add add pane handler');
25351 this.on('addpane', this.onAddPane, this);
25354 * Updates the box title
25355 * @param {String} html to set the title to.
25357 setTitle : function(value)
25359 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25361 onAddPane : function(pane)
25363 this.panes.push(pane);
25364 //Roo.log('addpane');
25366 // tabs are rendere left to right..
25367 if(!this.showtabs){
25371 var ctr = this.el.select('.nav-tabs', true).first();
25374 var existing = ctr.select('.nav-tab',true);
25375 var qty = existing.getCount();;
25378 var tab = ctr.createChild({
25380 cls : 'nav-tab' + (qty ? '' : ' active'),
25388 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25391 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25393 pane.el.addClass('active');
25398 onTabClick : function(ev,un,ob,pane)
25400 //Roo.log('tab - prev default');
25401 ev.preventDefault();
25404 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25405 pane.tab.addClass('active');
25406 //Roo.log(pane.title);
25407 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25408 // technically we should have a deactivate event.. but maybe add later.
25409 // and it should not de-activate the selected tab...
25410 this.fireEvent('activatepane', pane);
25411 pane.el.addClass('active');
25412 pane.fireEvent('activate');
25417 getActivePane : function()
25420 Roo.each(this.panes, function(p) {
25421 if(p.el.hasClass('active')){
25442 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25444 * @class Roo.bootstrap.TabPane
25445 * @extends Roo.bootstrap.Component
25446 * Bootstrap TabPane class
25447 * @cfg {Boolean} active (false | true) Default false
25448 * @cfg {String} title title of panel
25452 * Create a new TabPane
25453 * @param {Object} config The config object
25456 Roo.bootstrap.dash.TabPane = function(config){
25457 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25463 * When a pane is activated
25464 * @param {Roo.bootstrap.dash.TabPane} pane
25471 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25476 // the tabBox that this is attached to.
25479 getAutoCreate : function()
25487 cfg.cls += ' active';
25492 initEvents : function()
25494 //Roo.log('trigger add pane handler');
25495 this.parent().fireEvent('addpane', this)
25499 * Updates the tab title
25500 * @param {String} html to set the title to.
25502 setTitle: function(str)
25508 this.tab.select('a', true).first().dom.innerHTML = str;
25525 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25528 * @class Roo.bootstrap.menu.Menu
25529 * @extends Roo.bootstrap.Component
25530 * Bootstrap Menu class - container for Menu
25531 * @cfg {String} html Text of the menu
25532 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25533 * @cfg {String} icon Font awesome icon
25534 * @cfg {String} pos Menu align to (top | bottom) default bottom
25538 * Create a new Menu
25539 * @param {Object} config The config object
25543 Roo.bootstrap.menu.Menu = function(config){
25544 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25548 * @event beforeshow
25549 * Fires before this menu is displayed
25550 * @param {Roo.bootstrap.menu.Menu} this
25554 * @event beforehide
25555 * Fires before this menu is hidden
25556 * @param {Roo.bootstrap.menu.Menu} this
25561 * Fires after this menu is displayed
25562 * @param {Roo.bootstrap.menu.Menu} this
25567 * Fires after this menu is hidden
25568 * @param {Roo.bootstrap.menu.Menu} this
25573 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25574 * @param {Roo.bootstrap.menu.Menu} this
25575 * @param {Roo.EventObject} e
25582 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25586 weight : 'default',
25591 getChildContainer : function() {
25592 if(this.isSubMenu){
25596 return this.el.select('ul.dropdown-menu', true).first();
25599 getAutoCreate : function()
25604 cls : 'roo-menu-text',
25612 cls : 'fa ' + this.icon
25623 cls : 'dropdown-button btn btn-' + this.weight,
25628 cls : 'dropdown-toggle btn btn-' + this.weight,
25638 cls : 'dropdown-menu'
25644 if(this.pos == 'top'){
25645 cfg.cls += ' dropup';
25648 if(this.isSubMenu){
25651 cls : 'dropdown-menu'
25658 onRender : function(ct, position)
25660 this.isSubMenu = ct.hasClass('dropdown-submenu');
25662 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25665 initEvents : function()
25667 if(this.isSubMenu){
25671 this.hidden = true;
25673 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25674 this.triggerEl.on('click', this.onTriggerPress, this);
25676 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25677 this.buttonEl.on('click', this.onClick, this);
25683 if(this.isSubMenu){
25687 return this.el.select('ul.dropdown-menu', true).first();
25690 onClick : function(e)
25692 this.fireEvent("click", this, e);
25695 onTriggerPress : function(e)
25697 if (this.isVisible()) {
25704 isVisible : function(){
25705 return !this.hidden;
25710 this.fireEvent("beforeshow", this);
25712 this.hidden = false;
25713 this.el.addClass('open');
25715 Roo.get(document).on("mouseup", this.onMouseUp, this);
25717 this.fireEvent("show", this);
25724 this.fireEvent("beforehide", this);
25726 this.hidden = true;
25727 this.el.removeClass('open');
25729 Roo.get(document).un("mouseup", this.onMouseUp);
25731 this.fireEvent("hide", this);
25734 onMouseUp : function()
25748 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25751 * @class Roo.bootstrap.menu.Item
25752 * @extends Roo.bootstrap.Component
25753 * Bootstrap MenuItem class
25754 * @cfg {Boolean} submenu (true | false) default false
25755 * @cfg {String} html text of the item
25756 * @cfg {String} href the link
25757 * @cfg {Boolean} disable (true | false) default false
25758 * @cfg {Boolean} preventDefault (true | false) default true
25759 * @cfg {String} icon Font awesome icon
25760 * @cfg {String} pos Submenu align to (left | right) default right
25764 * Create a new Item
25765 * @param {Object} config The config object
25769 Roo.bootstrap.menu.Item = function(config){
25770 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25774 * Fires when the mouse is hovering over this menu
25775 * @param {Roo.bootstrap.menu.Item} this
25776 * @param {Roo.EventObject} e
25781 * Fires when the mouse exits this menu
25782 * @param {Roo.bootstrap.menu.Item} this
25783 * @param {Roo.EventObject} e
25789 * The raw click event for the entire grid.
25790 * @param {Roo.EventObject} e
25796 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25801 preventDefault: true,
25806 getAutoCreate : function()
25811 cls : 'roo-menu-item-text',
25819 cls : 'fa ' + this.icon
25828 href : this.href || '#',
25835 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25839 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25841 if(this.pos == 'left'){
25842 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25849 initEvents : function()
25851 this.el.on('mouseover', this.onMouseOver, this);
25852 this.el.on('mouseout', this.onMouseOut, this);
25854 this.el.select('a', true).first().on('click', this.onClick, this);
25858 onClick : function(e)
25860 if(this.preventDefault){
25861 e.preventDefault();
25864 this.fireEvent("click", this, e);
25867 onMouseOver : function(e)
25869 if(this.submenu && this.pos == 'left'){
25870 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25873 this.fireEvent("mouseover", this, e);
25876 onMouseOut : function(e)
25878 this.fireEvent("mouseout", this, e);
25890 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25893 * @class Roo.bootstrap.menu.Separator
25894 * @extends Roo.bootstrap.Component
25895 * Bootstrap Separator class
25898 * Create a new Separator
25899 * @param {Object} config The config object
25903 Roo.bootstrap.menu.Separator = function(config){
25904 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25907 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25909 getAutoCreate : function(){
25930 * @class Roo.bootstrap.Tooltip
25931 * Bootstrap Tooltip class
25932 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25933 * to determine which dom element triggers the tooltip.
25935 * It needs to add support for additional attributes like tooltip-position
25938 * Create a new Toolti
25939 * @param {Object} config The config object
25942 Roo.bootstrap.Tooltip = function(config){
25943 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25945 this.alignment = Roo.bootstrap.Tooltip.alignment;
25947 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25948 this.alignment = config.alignment;
25953 Roo.apply(Roo.bootstrap.Tooltip, {
25955 * @function init initialize tooltip monitoring.
25959 currentTip : false,
25960 currentRegion : false,
25966 Roo.get(document).on('mouseover', this.enter ,this);
25967 Roo.get(document).on('mouseout', this.leave, this);
25970 this.currentTip = new Roo.bootstrap.Tooltip();
25973 enter : function(ev)
25975 var dom = ev.getTarget();
25977 //Roo.log(['enter',dom]);
25978 var el = Roo.fly(dom);
25979 if (this.currentEl) {
25981 //Roo.log(this.currentEl);
25982 //Roo.log(this.currentEl.contains(dom));
25983 if (this.currentEl == el) {
25986 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25992 if (this.currentTip.el) {
25993 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25997 if(!el || el.dom == document){
26003 // you can not look for children, as if el is the body.. then everythign is the child..
26004 if (!el.attr('tooltip')) { //
26005 if (!el.select("[tooltip]").elements.length) {
26008 // is the mouse over this child...?
26009 bindEl = el.select("[tooltip]").first();
26010 var xy = ev.getXY();
26011 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26012 //Roo.log("not in region.");
26015 //Roo.log("child element over..");
26018 this.currentEl = bindEl;
26019 this.currentTip.bind(bindEl);
26020 this.currentRegion = Roo.lib.Region.getRegion(dom);
26021 this.currentTip.enter();
26024 leave : function(ev)
26026 var dom = ev.getTarget();
26027 //Roo.log(['leave',dom]);
26028 if (!this.currentEl) {
26033 if (dom != this.currentEl.dom) {
26036 var xy = ev.getXY();
26037 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26040 // only activate leave if mouse cursor is outside... bounding box..
26045 if (this.currentTip) {
26046 this.currentTip.leave();
26048 //Roo.log('clear currentEl');
26049 this.currentEl = false;
26054 'left' : ['r-l', [-2,0], 'right'],
26055 'right' : ['l-r', [2,0], 'left'],
26056 'bottom' : ['t-b', [0,2], 'top'],
26057 'top' : [ 'b-t', [0,-2], 'bottom']
26063 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26068 delay : null, // can be { show : 300 , hide: 500}
26072 hoverState : null, //???
26074 placement : 'bottom',
26078 getAutoCreate : function(){
26085 cls : 'tooltip-arrow'
26088 cls : 'tooltip-inner'
26095 bind : function(el)
26101 enter : function () {
26103 if (this.timeout != null) {
26104 clearTimeout(this.timeout);
26107 this.hoverState = 'in';
26108 //Roo.log("enter - show");
26109 if (!this.delay || !this.delay.show) {
26114 this.timeout = setTimeout(function () {
26115 if (_t.hoverState == 'in') {
26118 }, this.delay.show);
26122 clearTimeout(this.timeout);
26124 this.hoverState = 'out';
26125 if (!this.delay || !this.delay.hide) {
26131 this.timeout = setTimeout(function () {
26132 //Roo.log("leave - timeout");
26134 if (_t.hoverState == 'out') {
26136 Roo.bootstrap.Tooltip.currentEl = false;
26141 show : function (msg)
26144 this.render(document.body);
26147 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26149 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26151 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26153 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26155 var placement = typeof this.placement == 'function' ?
26156 this.placement.call(this, this.el, on_el) :
26159 var autoToken = /\s?auto?\s?/i;
26160 var autoPlace = autoToken.test(placement);
26162 placement = placement.replace(autoToken, '') || 'top';
26166 //this.el.setXY([0,0]);
26168 //this.el.dom.style.display='block';
26170 //this.el.appendTo(on_el);
26172 var p = this.getPosition();
26173 var box = this.el.getBox();
26179 var align = this.alignment[placement];
26181 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26183 if(placement == 'top' || placement == 'bottom'){
26185 placement = 'right';
26188 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26189 placement = 'left';
26192 var scroll = Roo.select('body', true).first().getScroll();
26194 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26200 this.el.alignTo(this.bindEl, align[0],align[1]);
26201 //var arrow = this.el.select('.arrow',true).first();
26202 //arrow.set(align[2],
26204 this.el.addClass(placement);
26206 this.el.addClass('in fade');
26208 this.hoverState = null;
26210 if (this.el.hasClass('fade')) {
26221 //this.el.setXY([0,0]);
26222 this.el.removeClass('in');
26238 * @class Roo.bootstrap.LocationPicker
26239 * @extends Roo.bootstrap.Component
26240 * Bootstrap LocationPicker class
26241 * @cfg {Number} latitude Position when init default 0
26242 * @cfg {Number} longitude Position when init default 0
26243 * @cfg {Number} zoom default 15
26244 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26245 * @cfg {Boolean} mapTypeControl default false
26246 * @cfg {Boolean} disableDoubleClickZoom default false
26247 * @cfg {Boolean} scrollwheel default true
26248 * @cfg {Boolean} streetViewControl default false
26249 * @cfg {Number} radius default 0
26250 * @cfg {String} locationName
26251 * @cfg {Boolean} draggable default true
26252 * @cfg {Boolean} enableAutocomplete default false
26253 * @cfg {Boolean} enableReverseGeocode default true
26254 * @cfg {String} markerTitle
26257 * Create a new LocationPicker
26258 * @param {Object} config The config object
26262 Roo.bootstrap.LocationPicker = function(config){
26264 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26269 * Fires when the picker initialized.
26270 * @param {Roo.bootstrap.LocationPicker} this
26271 * @param {Google Location} location
26275 * @event positionchanged
26276 * Fires when the picker position changed.
26277 * @param {Roo.bootstrap.LocationPicker} this
26278 * @param {Google Location} location
26280 positionchanged : true,
26283 * Fires when the map resize.
26284 * @param {Roo.bootstrap.LocationPicker} this
26289 * Fires when the map show.
26290 * @param {Roo.bootstrap.LocationPicker} this
26295 * Fires when the map hide.
26296 * @param {Roo.bootstrap.LocationPicker} this
26301 * Fires when click the map.
26302 * @param {Roo.bootstrap.LocationPicker} this
26303 * @param {Map event} e
26307 * @event mapRightClick
26308 * Fires when right click the map.
26309 * @param {Roo.bootstrap.LocationPicker} this
26310 * @param {Map event} e
26312 mapRightClick : true,
26314 * @event markerClick
26315 * Fires when click the marker.
26316 * @param {Roo.bootstrap.LocationPicker} this
26317 * @param {Map event} e
26319 markerClick : true,
26321 * @event markerRightClick
26322 * Fires when right click the marker.
26323 * @param {Roo.bootstrap.LocationPicker} this
26324 * @param {Map event} e
26326 markerRightClick : true,
26328 * @event OverlayViewDraw
26329 * Fires when OverlayView Draw
26330 * @param {Roo.bootstrap.LocationPicker} this
26332 OverlayViewDraw : true,
26334 * @event OverlayViewOnAdd
26335 * Fires when OverlayView Draw
26336 * @param {Roo.bootstrap.LocationPicker} this
26338 OverlayViewOnAdd : true,
26340 * @event OverlayViewOnRemove
26341 * Fires when OverlayView Draw
26342 * @param {Roo.bootstrap.LocationPicker} this
26344 OverlayViewOnRemove : true,
26346 * @event OverlayViewShow
26347 * Fires when OverlayView Draw
26348 * @param {Roo.bootstrap.LocationPicker} this
26349 * @param {Pixel} cpx
26351 OverlayViewShow : true,
26353 * @event OverlayViewHide
26354 * Fires when OverlayView Draw
26355 * @param {Roo.bootstrap.LocationPicker} this
26357 OverlayViewHide : true,
26359 * @event loadexception
26360 * Fires when load google lib failed.
26361 * @param {Roo.bootstrap.LocationPicker} this
26363 loadexception : true
26368 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26370 gMapContext: false,
26376 mapTypeControl: false,
26377 disableDoubleClickZoom: false,
26379 streetViewControl: false,
26383 enableAutocomplete: false,
26384 enableReverseGeocode: true,
26387 getAutoCreate: function()
26392 cls: 'roo-location-picker'
26398 initEvents: function(ct, position)
26400 if(!this.el.getWidth() || this.isApplied()){
26404 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26409 initial: function()
26411 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26412 this.fireEvent('loadexception', this);
26416 if(!this.mapTypeId){
26417 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26420 this.gMapContext = this.GMapContext();
26422 this.initOverlayView();
26424 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26428 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26429 _this.setPosition(_this.gMapContext.marker.position);
26432 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26433 _this.fireEvent('mapClick', this, event);
26437 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26438 _this.fireEvent('mapRightClick', this, event);
26442 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26443 _this.fireEvent('markerClick', this, event);
26447 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26448 _this.fireEvent('markerRightClick', this, event);
26452 this.setPosition(this.gMapContext.location);
26454 this.fireEvent('initial', this, this.gMapContext.location);
26457 initOverlayView: function()
26461 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26465 _this.fireEvent('OverlayViewDraw', _this);
26470 _this.fireEvent('OverlayViewOnAdd', _this);
26473 onRemove: function()
26475 _this.fireEvent('OverlayViewOnRemove', _this);
26478 show: function(cpx)
26480 _this.fireEvent('OverlayViewShow', _this, cpx);
26485 _this.fireEvent('OverlayViewHide', _this);
26491 fromLatLngToContainerPixel: function(event)
26493 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26496 isApplied: function()
26498 return this.getGmapContext() == false ? false : true;
26501 getGmapContext: function()
26503 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26506 GMapContext: function()
26508 var position = new google.maps.LatLng(this.latitude, this.longitude);
26510 var _map = new google.maps.Map(this.el.dom, {
26513 mapTypeId: this.mapTypeId,
26514 mapTypeControl: this.mapTypeControl,
26515 disableDoubleClickZoom: this.disableDoubleClickZoom,
26516 scrollwheel: this.scrollwheel,
26517 streetViewControl: this.streetViewControl,
26518 locationName: this.locationName,
26519 draggable: this.draggable,
26520 enableAutocomplete: this.enableAutocomplete,
26521 enableReverseGeocode: this.enableReverseGeocode
26524 var _marker = new google.maps.Marker({
26525 position: position,
26527 title: this.markerTitle,
26528 draggable: this.draggable
26535 location: position,
26536 radius: this.radius,
26537 locationName: this.locationName,
26538 addressComponents: {
26539 formatted_address: null,
26540 addressLine1: null,
26541 addressLine2: null,
26543 streetNumber: null,
26547 stateOrProvince: null
26550 domContainer: this.el.dom,
26551 geodecoder: new google.maps.Geocoder()
26555 drawCircle: function(center, radius, options)
26557 if (this.gMapContext.circle != null) {
26558 this.gMapContext.circle.setMap(null);
26562 options = Roo.apply({}, options, {
26563 strokeColor: "#0000FF",
26564 strokeOpacity: .35,
26566 fillColor: "#0000FF",
26570 options.map = this.gMapContext.map;
26571 options.radius = radius;
26572 options.center = center;
26573 this.gMapContext.circle = new google.maps.Circle(options);
26574 return this.gMapContext.circle;
26580 setPosition: function(location)
26582 this.gMapContext.location = location;
26583 this.gMapContext.marker.setPosition(location);
26584 this.gMapContext.map.panTo(location);
26585 this.drawCircle(location, this.gMapContext.radius, {});
26589 if (this.gMapContext.settings.enableReverseGeocode) {
26590 this.gMapContext.geodecoder.geocode({
26591 latLng: this.gMapContext.location
26592 }, function(results, status) {
26594 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26595 _this.gMapContext.locationName = results[0].formatted_address;
26596 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26598 _this.fireEvent('positionchanged', this, location);
26605 this.fireEvent('positionchanged', this, location);
26610 google.maps.event.trigger(this.gMapContext.map, "resize");
26612 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26614 this.fireEvent('resize', this);
26617 setPositionByLatLng: function(latitude, longitude)
26619 this.setPosition(new google.maps.LatLng(latitude, longitude));
26622 getCurrentPosition: function()
26625 latitude: this.gMapContext.location.lat(),
26626 longitude: this.gMapContext.location.lng()
26630 getAddressName: function()
26632 return this.gMapContext.locationName;
26635 getAddressComponents: function()
26637 return this.gMapContext.addressComponents;
26640 address_component_from_google_geocode: function(address_components)
26644 for (var i = 0; i < address_components.length; i++) {
26645 var component = address_components[i];
26646 if (component.types.indexOf("postal_code") >= 0) {
26647 result.postalCode = component.short_name;
26648 } else if (component.types.indexOf("street_number") >= 0) {
26649 result.streetNumber = component.short_name;
26650 } else if (component.types.indexOf("route") >= 0) {
26651 result.streetName = component.short_name;
26652 } else if (component.types.indexOf("neighborhood") >= 0) {
26653 result.city = component.short_name;
26654 } else if (component.types.indexOf("locality") >= 0) {
26655 result.city = component.short_name;
26656 } else if (component.types.indexOf("sublocality") >= 0) {
26657 result.district = component.short_name;
26658 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26659 result.stateOrProvince = component.short_name;
26660 } else if (component.types.indexOf("country") >= 0) {
26661 result.country = component.short_name;
26665 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26666 result.addressLine2 = "";
26670 setZoomLevel: function(zoom)
26672 this.gMapContext.map.setZoom(zoom);
26685 this.fireEvent('show', this);
26696 this.fireEvent('hide', this);
26701 Roo.apply(Roo.bootstrap.LocationPicker, {
26703 OverlayView : function(map, options)
26705 options = options || {};
26719 * @class Roo.bootstrap.Alert
26720 * @extends Roo.bootstrap.Component
26721 * Bootstrap Alert class
26722 * @cfg {String} title The title of alert
26723 * @cfg {String} html The content of alert
26724 * @cfg {String} weight ( success | info | warning | danger )
26725 * @cfg {String} faicon font-awesomeicon
26728 * Create a new alert
26729 * @param {Object} config The config object
26733 Roo.bootstrap.Alert = function(config){
26734 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26738 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26745 getAutoCreate : function()
26754 cls : 'roo-alert-icon'
26759 cls : 'roo-alert-title',
26764 cls : 'roo-alert-text',
26771 cfg.cn[0].cls += ' fa ' + this.faicon;
26775 cfg.cls += ' alert-' + this.weight;
26781 initEvents: function()
26783 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26786 setTitle : function(str)
26788 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26791 setText : function(str)
26793 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26796 setWeight : function(weight)
26799 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26802 this.weight = weight;
26804 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26807 setIcon : function(icon)
26810 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26813 this.faicon = icon;
26815 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26836 * @class Roo.bootstrap.UploadCropbox
26837 * @extends Roo.bootstrap.Component
26838 * Bootstrap UploadCropbox class
26839 * @cfg {String} emptyText show when image has been loaded
26840 * @cfg {String} rotateNotify show when image too small to rotate
26841 * @cfg {Number} errorTimeout default 3000
26842 * @cfg {Number} minWidth default 300
26843 * @cfg {Number} minHeight default 300
26844 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26845 * @cfg {Boolean} isDocument (true|false) default false
26846 * @cfg {String} url action url
26847 * @cfg {String} paramName default 'imageUpload'
26848 * @cfg {String} method default POST
26849 * @cfg {Boolean} loadMask (true|false) default true
26850 * @cfg {Boolean} loadingText default 'Loading...'
26853 * Create a new UploadCropbox
26854 * @param {Object} config The config object
26857 Roo.bootstrap.UploadCropbox = function(config){
26858 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26862 * @event beforeselectfile
26863 * Fire before select file
26864 * @param {Roo.bootstrap.UploadCropbox} this
26866 "beforeselectfile" : true,
26869 * Fire after initEvent
26870 * @param {Roo.bootstrap.UploadCropbox} this
26875 * Fire after initEvent
26876 * @param {Roo.bootstrap.UploadCropbox} this
26877 * @param {String} data
26882 * Fire when preparing the file data
26883 * @param {Roo.bootstrap.UploadCropbox} this
26884 * @param {Object} file
26889 * Fire when get exception
26890 * @param {Roo.bootstrap.UploadCropbox} this
26891 * @param {XMLHttpRequest} xhr
26893 "exception" : true,
26895 * @event beforeloadcanvas
26896 * Fire before load the canvas
26897 * @param {Roo.bootstrap.UploadCropbox} this
26898 * @param {String} src
26900 "beforeloadcanvas" : true,
26903 * Fire when trash image
26904 * @param {Roo.bootstrap.UploadCropbox} this
26909 * Fire when download the image
26910 * @param {Roo.bootstrap.UploadCropbox} this
26914 * @event footerbuttonclick
26915 * Fire when footerbuttonclick
26916 * @param {Roo.bootstrap.UploadCropbox} this
26917 * @param {String} type
26919 "footerbuttonclick" : true,
26923 * @param {Roo.bootstrap.UploadCropbox} this
26928 * Fire when rotate the image
26929 * @param {Roo.bootstrap.UploadCropbox} this
26930 * @param {String} pos
26935 * Fire when inspect the file
26936 * @param {Roo.bootstrap.UploadCropbox} this
26937 * @param {Object} file
26942 * Fire when xhr upload the file
26943 * @param {Roo.bootstrap.UploadCropbox} this
26944 * @param {Object} data
26949 * Fire when arrange the file data
26950 * @param {Roo.bootstrap.UploadCropbox} this
26951 * @param {Object} formData
26956 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26959 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26961 emptyText : 'Click to upload image',
26962 rotateNotify : 'Image is too small to rotate',
26963 errorTimeout : 3000,
26977 cropType : 'image/jpeg',
26979 canvasLoaded : false,
26980 isDocument : false,
26982 paramName : 'imageUpload',
26984 loadingText : 'Loading...',
26987 getAutoCreate : function()
26991 cls : 'roo-upload-cropbox',
26995 cls : 'roo-upload-cropbox-selector',
27000 cls : 'roo-upload-cropbox-body',
27001 style : 'cursor:pointer',
27005 cls : 'roo-upload-cropbox-preview'
27009 cls : 'roo-upload-cropbox-thumb'
27013 cls : 'roo-upload-cropbox-empty-notify',
27014 html : this.emptyText
27018 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27019 html : this.rotateNotify
27025 cls : 'roo-upload-cropbox-footer',
27028 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27038 onRender : function(ct, position)
27040 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27042 if (this.buttons.length) {
27044 Roo.each(this.buttons, function(bb) {
27046 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27048 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27054 this.maskEl = this.el;
27058 initEvents : function()
27060 this.urlAPI = (window.createObjectURL && window) ||
27061 (window.URL && URL.revokeObjectURL && URL) ||
27062 (window.webkitURL && webkitURL);
27064 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27065 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27068 this.selectorEl.hide();
27070 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27071 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27074 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075 this.thumbEl.hide();
27077 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27078 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27080 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27081 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082 this.errorEl.hide();
27084 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27085 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27086 this.footerEl.hide();
27088 this.setThumbBoxSize();
27094 this.fireEvent('initial', this);
27101 window.addEventListener("resize", function() { _this.resize(); } );
27103 this.bodyEl.on('click', this.beforeSelectFile, this);
27106 this.bodyEl.on('touchstart', this.onTouchStart, this);
27107 this.bodyEl.on('touchmove', this.onTouchMove, this);
27108 this.bodyEl.on('touchend', this.onTouchEnd, this);
27112 this.bodyEl.on('mousedown', this.onMouseDown, this);
27113 this.bodyEl.on('mousemove', this.onMouseMove, this);
27114 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27115 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27116 Roo.get(document).on('mouseup', this.onMouseUp, this);
27119 this.selectorEl.on('change', this.onFileSelected, this);
27125 this.baseScale = 1;
27127 this.baseRotate = 1;
27128 this.dragable = false;
27129 this.pinching = false;
27132 this.cropData = false;
27133 this.notifyEl.dom.innerHTML = this.emptyText;
27135 this.selectorEl.dom.value = '';
27139 resize : function()
27141 if(this.fireEvent('resize', this) != false){
27142 this.setThumbBoxPosition();
27143 this.setCanvasPosition();
27147 onFooterButtonClick : function(e, el, o, type)
27150 case 'rotate-left' :
27151 this.onRotateLeft(e);
27153 case 'rotate-right' :
27154 this.onRotateRight(e);
27157 this.beforeSelectFile(e);
27172 this.fireEvent('footerbuttonclick', this, type);
27175 beforeSelectFile : function(e)
27177 e.preventDefault();
27179 if(this.fireEvent('beforeselectfile', this) != false){
27180 this.selectorEl.dom.click();
27184 onFileSelected : function(e)
27186 e.preventDefault();
27188 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27192 var file = this.selectorEl.dom.files[0];
27194 if(this.fireEvent('inspect', this, file) != false){
27195 this.prepare(file);
27200 trash : function(e)
27202 this.fireEvent('trash', this);
27205 download : function(e)
27207 this.fireEvent('download', this);
27210 loadCanvas : function(src)
27212 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27216 this.imageEl = document.createElement('img');
27220 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27222 this.imageEl.src = src;
27226 onLoadCanvas : function()
27228 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27229 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27231 this.bodyEl.un('click', this.beforeSelectFile, this);
27233 this.notifyEl.hide();
27234 this.thumbEl.show();
27235 this.footerEl.show();
27237 this.baseRotateLevel();
27239 if(this.isDocument){
27240 this.setThumbBoxSize();
27243 this.setThumbBoxPosition();
27245 this.baseScaleLevel();
27251 this.canvasLoaded = true;
27254 this.maskEl.unmask();
27259 setCanvasPosition : function()
27261 if(!this.canvasEl){
27265 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27266 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27268 this.previewEl.setLeft(pw);
27269 this.previewEl.setTop(ph);
27273 onMouseDown : function(e)
27277 this.dragable = true;
27278 this.pinching = false;
27280 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27281 this.dragable = false;
27285 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27286 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27290 onMouseMove : function(e)
27294 if(!this.canvasLoaded){
27298 if (!this.dragable){
27302 var minX = Math.ceil(this.thumbEl.getLeft(true));
27303 var minY = Math.ceil(this.thumbEl.getTop(true));
27305 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27306 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27308 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27309 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27311 x = x - this.mouseX;
27312 y = y - this.mouseY;
27314 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27315 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27317 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27318 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27320 this.previewEl.setLeft(bgX);
27321 this.previewEl.setTop(bgY);
27323 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27324 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27327 onMouseUp : function(e)
27331 this.dragable = false;
27334 onMouseWheel : function(e)
27338 this.startScale = this.scale;
27340 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27342 if(!this.zoomable()){
27343 this.scale = this.startScale;
27352 zoomable : function()
27354 var minScale = this.thumbEl.getWidth() / this.minWidth;
27356 if(this.minWidth < this.minHeight){
27357 minScale = this.thumbEl.getHeight() / this.minHeight;
27360 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27361 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27365 (this.rotate == 0 || this.rotate == 180) &&
27367 width > this.imageEl.OriginWidth ||
27368 height > this.imageEl.OriginHeight ||
27369 (width < this.minWidth && height < this.minHeight)
27377 (this.rotate == 90 || this.rotate == 270) &&
27379 width > this.imageEl.OriginWidth ||
27380 height > this.imageEl.OriginHeight ||
27381 (width < this.minHeight && height < this.minWidth)
27388 !this.isDocument &&
27389 (this.rotate == 0 || this.rotate == 180) &&
27391 width < this.minWidth ||
27392 width > this.imageEl.OriginWidth ||
27393 height < this.minHeight ||
27394 height > this.imageEl.OriginHeight
27401 !this.isDocument &&
27402 (this.rotate == 90 || this.rotate == 270) &&
27404 width < this.minHeight ||
27405 width > this.imageEl.OriginWidth ||
27406 height < this.minWidth ||
27407 height > this.imageEl.OriginHeight
27417 onRotateLeft : function(e)
27419 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27421 var minScale = this.thumbEl.getWidth() / this.minWidth;
27423 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27424 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27426 this.startScale = this.scale;
27428 while (this.getScaleLevel() < minScale){
27430 this.scale = this.scale + 1;
27432 if(!this.zoomable()){
27437 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27438 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27443 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27450 this.scale = this.startScale;
27452 this.onRotateFail();
27457 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27459 if(this.isDocument){
27460 this.setThumbBoxSize();
27461 this.setThumbBoxPosition();
27462 this.setCanvasPosition();
27467 this.fireEvent('rotate', this, 'left');
27471 onRotateRight : function(e)
27473 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27475 var minScale = this.thumbEl.getWidth() / this.minWidth;
27477 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27478 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27480 this.startScale = this.scale;
27482 while (this.getScaleLevel() < minScale){
27484 this.scale = this.scale + 1;
27486 if(!this.zoomable()){
27491 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27492 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27497 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27504 this.scale = this.startScale;
27506 this.onRotateFail();
27511 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27513 if(this.isDocument){
27514 this.setThumbBoxSize();
27515 this.setThumbBoxPosition();
27516 this.setCanvasPosition();
27521 this.fireEvent('rotate', this, 'right');
27524 onRotateFail : function()
27526 this.errorEl.show(true);
27530 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27535 this.previewEl.dom.innerHTML = '';
27537 var canvasEl = document.createElement("canvas");
27539 var contextEl = canvasEl.getContext("2d");
27541 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27542 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27543 var center = this.imageEl.OriginWidth / 2;
27545 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27546 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27547 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27548 center = this.imageEl.OriginHeight / 2;
27551 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27553 contextEl.translate(center, center);
27554 contextEl.rotate(this.rotate * Math.PI / 180);
27556 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27558 this.canvasEl = document.createElement("canvas");
27560 this.contextEl = this.canvasEl.getContext("2d");
27562 switch (this.rotate) {
27565 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27566 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27568 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27573 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27574 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27576 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27577 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);
27581 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27586 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27587 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27589 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27590 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);
27594 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);
27599 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27600 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27602 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27603 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27607 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27614 this.previewEl.appendChild(this.canvasEl);
27616 this.setCanvasPosition();
27621 if(!this.canvasLoaded){
27625 var imageCanvas = document.createElement("canvas");
27627 var imageContext = imageCanvas.getContext("2d");
27629 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27630 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27632 var center = imageCanvas.width / 2;
27634 imageContext.translate(center, center);
27636 imageContext.rotate(this.rotate * Math.PI / 180);
27638 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27640 var canvas = document.createElement("canvas");
27642 var context = canvas.getContext("2d");
27644 canvas.width = this.minWidth;
27645 canvas.height = this.minHeight;
27647 switch (this.rotate) {
27650 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27651 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27653 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27654 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27656 var targetWidth = this.minWidth - 2 * x;
27657 var targetHeight = this.minHeight - 2 * y;
27661 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27662 scale = targetWidth / width;
27665 if(x > 0 && y == 0){
27666 scale = targetHeight / height;
27669 if(x > 0 && y > 0){
27670 scale = targetWidth / width;
27672 if(width < height){
27673 scale = targetHeight / height;
27677 context.scale(scale, scale);
27679 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27680 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27682 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27683 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27685 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27690 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27691 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27693 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27694 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27696 var targetWidth = this.minWidth - 2 * x;
27697 var targetHeight = this.minHeight - 2 * y;
27701 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27702 scale = targetWidth / width;
27705 if(x > 0 && y == 0){
27706 scale = targetHeight / height;
27709 if(x > 0 && y > 0){
27710 scale = targetWidth / width;
27712 if(width < height){
27713 scale = targetHeight / height;
27717 context.scale(scale, scale);
27719 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27720 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27722 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27723 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27725 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27727 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27732 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27733 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27735 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27736 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27738 var targetWidth = this.minWidth - 2 * x;
27739 var targetHeight = this.minHeight - 2 * y;
27743 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27744 scale = targetWidth / width;
27747 if(x > 0 && y == 0){
27748 scale = targetHeight / height;
27751 if(x > 0 && y > 0){
27752 scale = targetWidth / width;
27754 if(width < height){
27755 scale = targetHeight / height;
27759 context.scale(scale, scale);
27761 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27762 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27764 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27765 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27767 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27768 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27770 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27775 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27776 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27778 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27779 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27781 var targetWidth = this.minWidth - 2 * x;
27782 var targetHeight = this.minHeight - 2 * y;
27786 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27787 scale = targetWidth / width;
27790 if(x > 0 && y == 0){
27791 scale = targetHeight / height;
27794 if(x > 0 && y > 0){
27795 scale = targetWidth / width;
27797 if(width < height){
27798 scale = targetHeight / height;
27802 context.scale(scale, scale);
27804 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27805 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27807 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27808 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27810 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27812 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27819 this.cropData = canvas.toDataURL(this.cropType);
27821 if(this.fireEvent('crop', this, this.cropData) !== false){
27822 this.process(this.file, this.cropData);
27829 setThumbBoxSize : function()
27833 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27834 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27835 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27837 this.minWidth = width;
27838 this.minHeight = height;
27840 if(this.rotate == 90 || this.rotate == 270){
27841 this.minWidth = height;
27842 this.minHeight = width;
27847 width = Math.ceil(this.minWidth * height / this.minHeight);
27849 if(this.minWidth > this.minHeight){
27851 height = Math.ceil(this.minHeight * width / this.minWidth);
27854 this.thumbEl.setStyle({
27855 width : width + 'px',
27856 height : height + 'px'
27863 setThumbBoxPosition : function()
27865 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27866 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27868 this.thumbEl.setLeft(x);
27869 this.thumbEl.setTop(y);
27873 baseRotateLevel : function()
27875 this.baseRotate = 1;
27878 typeof(this.exif) != 'undefined' &&
27879 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27880 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27882 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27885 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27889 baseScaleLevel : function()
27893 if(this.isDocument){
27895 if(this.baseRotate == 6 || this.baseRotate == 8){
27897 height = this.thumbEl.getHeight();
27898 this.baseScale = height / this.imageEl.OriginWidth;
27900 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27901 width = this.thumbEl.getWidth();
27902 this.baseScale = width / this.imageEl.OriginHeight;
27908 height = this.thumbEl.getHeight();
27909 this.baseScale = height / this.imageEl.OriginHeight;
27911 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27912 width = this.thumbEl.getWidth();
27913 this.baseScale = width / this.imageEl.OriginWidth;
27919 if(this.baseRotate == 6 || this.baseRotate == 8){
27921 width = this.thumbEl.getHeight();
27922 this.baseScale = width / this.imageEl.OriginHeight;
27924 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27925 height = this.thumbEl.getWidth();
27926 this.baseScale = height / this.imageEl.OriginHeight;
27929 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27930 height = this.thumbEl.getWidth();
27931 this.baseScale = height / this.imageEl.OriginHeight;
27933 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27934 width = this.thumbEl.getHeight();
27935 this.baseScale = width / this.imageEl.OriginWidth;
27942 width = this.thumbEl.getWidth();
27943 this.baseScale = width / this.imageEl.OriginWidth;
27945 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27946 height = this.thumbEl.getHeight();
27947 this.baseScale = height / this.imageEl.OriginHeight;
27950 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27952 height = this.thumbEl.getHeight();
27953 this.baseScale = height / this.imageEl.OriginHeight;
27955 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27956 width = this.thumbEl.getWidth();
27957 this.baseScale = width / this.imageEl.OriginWidth;
27965 getScaleLevel : function()
27967 return this.baseScale * Math.pow(1.1, this.scale);
27970 onTouchStart : function(e)
27972 if(!this.canvasLoaded){
27973 this.beforeSelectFile(e);
27977 var touches = e.browserEvent.touches;
27983 if(touches.length == 1){
27984 this.onMouseDown(e);
27988 if(touches.length != 2){
27994 for(var i = 0, finger; finger = touches[i]; i++){
27995 coords.push(finger.pageX, finger.pageY);
27998 var x = Math.pow(coords[0] - coords[2], 2);
27999 var y = Math.pow(coords[1] - coords[3], 2);
28001 this.startDistance = Math.sqrt(x + y);
28003 this.startScale = this.scale;
28005 this.pinching = true;
28006 this.dragable = false;
28010 onTouchMove : function(e)
28012 if(!this.pinching && !this.dragable){
28016 var touches = e.browserEvent.touches;
28023 this.onMouseMove(e);
28029 for(var i = 0, finger; finger = touches[i]; i++){
28030 coords.push(finger.pageX, finger.pageY);
28033 var x = Math.pow(coords[0] - coords[2], 2);
28034 var y = Math.pow(coords[1] - coords[3], 2);
28036 this.endDistance = Math.sqrt(x + y);
28038 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28040 if(!this.zoomable()){
28041 this.scale = this.startScale;
28049 onTouchEnd : function(e)
28051 this.pinching = false;
28052 this.dragable = false;
28056 process : function(file, crop)
28059 this.maskEl.mask(this.loadingText);
28062 this.xhr = new XMLHttpRequest();
28064 file.xhr = this.xhr;
28066 this.xhr.open(this.method, this.url, true);
28069 "Accept": "application/json",
28070 "Cache-Control": "no-cache",
28071 "X-Requested-With": "XMLHttpRequest"
28074 for (var headerName in headers) {
28075 var headerValue = headers[headerName];
28077 this.xhr.setRequestHeader(headerName, headerValue);
28083 this.xhr.onload = function()
28085 _this.xhrOnLoad(_this.xhr);
28088 this.xhr.onerror = function()
28090 _this.xhrOnError(_this.xhr);
28093 var formData = new FormData();
28095 formData.append('returnHTML', 'NO');
28098 formData.append('crop', crop);
28101 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28102 formData.append(this.paramName, file, file.name);
28105 if(typeof(file.filename) != 'undefined'){
28106 formData.append('filename', file.filename);
28109 if(typeof(file.mimetype) != 'undefined'){
28110 formData.append('mimetype', file.mimetype);
28113 if(this.fireEvent('arrange', this, formData) != false){
28114 this.xhr.send(formData);
28118 xhrOnLoad : function(xhr)
28121 this.maskEl.unmask();
28124 if (xhr.readyState !== 4) {
28125 this.fireEvent('exception', this, xhr);
28129 var response = Roo.decode(xhr.responseText);
28131 if(!response.success){
28132 this.fireEvent('exception', this, xhr);
28136 var response = Roo.decode(xhr.responseText);
28138 this.fireEvent('upload', this, response);
28142 xhrOnError : function()
28145 this.maskEl.unmask();
28148 Roo.log('xhr on error');
28150 var response = Roo.decode(xhr.responseText);
28156 prepare : function(file)
28159 this.maskEl.mask(this.loadingText);
28165 if(typeof(file) === 'string'){
28166 this.loadCanvas(file);
28170 if(!file || !this.urlAPI){
28175 this.cropType = file.type;
28179 if(this.fireEvent('prepare', this, this.file) != false){
28181 var reader = new FileReader();
28183 reader.onload = function (e) {
28184 if (e.target.error) {
28185 Roo.log(e.target.error);
28189 var buffer = e.target.result,
28190 dataView = new DataView(buffer),
28192 maxOffset = dataView.byteLength - 4,
28196 if (dataView.getUint16(0) === 0xffd8) {
28197 while (offset < maxOffset) {
28198 markerBytes = dataView.getUint16(offset);
28200 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28201 markerLength = dataView.getUint16(offset + 2) + 2;
28202 if (offset + markerLength > dataView.byteLength) {
28203 Roo.log('Invalid meta data: Invalid segment size.');
28207 if(markerBytes == 0xffe1){
28208 _this.parseExifData(
28215 offset += markerLength;
28225 var url = _this.urlAPI.createObjectURL(_this.file);
28227 _this.loadCanvas(url);
28232 reader.readAsArrayBuffer(this.file);
28238 parseExifData : function(dataView, offset, length)
28240 var tiffOffset = offset + 10,
28244 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28245 // No Exif data, might be XMP data instead
28249 // Check for the ASCII code for "Exif" (0x45786966):
28250 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28251 // No Exif data, might be XMP data instead
28254 if (tiffOffset + 8 > dataView.byteLength) {
28255 Roo.log('Invalid Exif data: Invalid segment size.');
28258 // Check for the two null bytes:
28259 if (dataView.getUint16(offset + 8) !== 0x0000) {
28260 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28263 // Check the byte alignment:
28264 switch (dataView.getUint16(tiffOffset)) {
28266 littleEndian = true;
28269 littleEndian = false;
28272 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28275 // Check for the TIFF tag marker (0x002A):
28276 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28277 Roo.log('Invalid Exif data: Missing TIFF marker.');
28280 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28281 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28283 this.parseExifTags(
28286 tiffOffset + dirOffset,
28291 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28296 if (dirOffset + 6 > dataView.byteLength) {
28297 Roo.log('Invalid Exif data: Invalid directory offset.');
28300 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28301 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28302 if (dirEndOffset + 4 > dataView.byteLength) {
28303 Roo.log('Invalid Exif data: Invalid directory size.');
28306 for (i = 0; i < tagsNumber; i += 1) {
28310 dirOffset + 2 + 12 * i, // tag offset
28314 // Return the offset to the next directory:
28315 return dataView.getUint32(dirEndOffset, littleEndian);
28318 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28320 var tag = dataView.getUint16(offset, littleEndian);
28322 this.exif[tag] = this.getExifValue(
28326 dataView.getUint16(offset + 2, littleEndian), // tag type
28327 dataView.getUint32(offset + 4, littleEndian), // tag length
28332 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28334 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28343 Roo.log('Invalid Exif data: Invalid tag type.');
28347 tagSize = tagType.size * length;
28348 // Determine if the value is contained in the dataOffset bytes,
28349 // or if the value at the dataOffset is a pointer to the actual data:
28350 dataOffset = tagSize > 4 ?
28351 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28352 if (dataOffset + tagSize > dataView.byteLength) {
28353 Roo.log('Invalid Exif data: Invalid data offset.');
28356 if (length === 1) {
28357 return tagType.getValue(dataView, dataOffset, littleEndian);
28360 for (i = 0; i < length; i += 1) {
28361 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28364 if (tagType.ascii) {
28366 // Concatenate the chars:
28367 for (i = 0; i < values.length; i += 1) {
28369 // Ignore the terminating NULL byte(s):
28370 if (c === '\u0000') {
28382 Roo.apply(Roo.bootstrap.UploadCropbox, {
28384 'Orientation': 0x0112
28388 1: 0, //'top-left',
28390 3: 180, //'bottom-right',
28391 // 4: 'bottom-left',
28393 6: 90, //'right-top',
28394 // 7: 'right-bottom',
28395 8: 270 //'left-bottom'
28399 // byte, 8-bit unsigned int:
28401 getValue: function (dataView, dataOffset) {
28402 return dataView.getUint8(dataOffset);
28406 // ascii, 8-bit byte:
28408 getValue: function (dataView, dataOffset) {
28409 return String.fromCharCode(dataView.getUint8(dataOffset));
28414 // short, 16 bit int:
28416 getValue: function (dataView, dataOffset, littleEndian) {
28417 return dataView.getUint16(dataOffset, littleEndian);
28421 // long, 32 bit int:
28423 getValue: function (dataView, dataOffset, littleEndian) {
28424 return dataView.getUint32(dataOffset, littleEndian);
28428 // rational = two long values, first is numerator, second is denominator:
28430 getValue: function (dataView, dataOffset, littleEndian) {
28431 return dataView.getUint32(dataOffset, littleEndian) /
28432 dataView.getUint32(dataOffset + 4, littleEndian);
28436 // slong, 32 bit signed int:
28438 getValue: function (dataView, dataOffset, littleEndian) {
28439 return dataView.getInt32(dataOffset, littleEndian);
28443 // srational, two slongs, first is numerator, second is denominator:
28445 getValue: function (dataView, dataOffset, littleEndian) {
28446 return dataView.getInt32(dataOffset, littleEndian) /
28447 dataView.getInt32(dataOffset + 4, littleEndian);
28457 cls : 'btn-group roo-upload-cropbox-rotate-left',
28458 action : 'rotate-left',
28462 cls : 'btn btn-default',
28463 html : '<i class="fa fa-undo"></i>'
28469 cls : 'btn-group roo-upload-cropbox-picture',
28470 action : 'picture',
28474 cls : 'btn btn-default',
28475 html : '<i class="fa fa-picture-o"></i>'
28481 cls : 'btn-group roo-upload-cropbox-rotate-right',
28482 action : 'rotate-right',
28486 cls : 'btn btn-default',
28487 html : '<i class="fa fa-repeat"></i>'
28495 cls : 'btn-group roo-upload-cropbox-rotate-left',
28496 action : 'rotate-left',
28500 cls : 'btn btn-default',
28501 html : '<i class="fa fa-undo"></i>'
28507 cls : 'btn-group roo-upload-cropbox-download',
28508 action : 'download',
28512 cls : 'btn btn-default',
28513 html : '<i class="fa fa-download"></i>'
28519 cls : 'btn-group roo-upload-cropbox-crop',
28524 cls : 'btn btn-default',
28525 html : '<i class="fa fa-crop"></i>'
28531 cls : 'btn-group roo-upload-cropbox-trash',
28536 cls : 'btn btn-default',
28537 html : '<i class="fa fa-trash"></i>'
28543 cls : 'btn-group roo-upload-cropbox-rotate-right',
28544 action : 'rotate-right',
28548 cls : 'btn btn-default',
28549 html : '<i class="fa fa-repeat"></i>'
28557 cls : 'btn-group roo-upload-cropbox-rotate-left',
28558 action : 'rotate-left',
28562 cls : 'btn btn-default',
28563 html : '<i class="fa fa-undo"></i>'
28569 cls : 'btn-group roo-upload-cropbox-rotate-right',
28570 action : 'rotate-right',
28574 cls : 'btn btn-default',
28575 html : '<i class="fa fa-repeat"></i>'
28588 * @class Roo.bootstrap.DocumentManager
28589 * @extends Roo.bootstrap.Component
28590 * Bootstrap DocumentManager class
28591 * @cfg {String} paramName default 'imageUpload'
28592 * @cfg {String} toolTipName default 'filename'
28593 * @cfg {String} method default POST
28594 * @cfg {String} url action url
28595 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28596 * @cfg {Boolean} multiple multiple upload default true
28597 * @cfg {Number} thumbSize default 300
28598 * @cfg {String} fieldLabel
28599 * @cfg {Number} labelWidth default 4
28600 * @cfg {String} labelAlign (left|top) default left
28601 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28602 * @cfg {Number} labellg set the width of label (1-12)
28603 * @cfg {Number} labelmd set the width of label (1-12)
28604 * @cfg {Number} labelsm set the width of label (1-12)
28605 * @cfg {Number} labelxs set the width of label (1-12)
28608 * Create a new DocumentManager
28609 * @param {Object} config The config object
28612 Roo.bootstrap.DocumentManager = function(config){
28613 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28616 this.delegates = [];
28621 * Fire when initial the DocumentManager
28622 * @param {Roo.bootstrap.DocumentManager} this
28627 * inspect selected file
28628 * @param {Roo.bootstrap.DocumentManager} this
28629 * @param {File} file
28634 * Fire when xhr load exception
28635 * @param {Roo.bootstrap.DocumentManager} this
28636 * @param {XMLHttpRequest} xhr
28638 "exception" : true,
28640 * @event afterupload
28641 * Fire when xhr load exception
28642 * @param {Roo.bootstrap.DocumentManager} this
28643 * @param {XMLHttpRequest} xhr
28645 "afterupload" : true,
28648 * prepare the form data
28649 * @param {Roo.bootstrap.DocumentManager} this
28650 * @param {Object} formData
28655 * Fire when remove the file
28656 * @param {Roo.bootstrap.DocumentManager} this
28657 * @param {Object} file
28662 * Fire after refresh the file
28663 * @param {Roo.bootstrap.DocumentManager} this
28668 * Fire after click the image
28669 * @param {Roo.bootstrap.DocumentManager} this
28670 * @param {Object} file
28675 * Fire when upload a image and editable set to true
28676 * @param {Roo.bootstrap.DocumentManager} this
28677 * @param {Object} file
28681 * @event beforeselectfile
28682 * Fire before select file
28683 * @param {Roo.bootstrap.DocumentManager} this
28685 "beforeselectfile" : true,
28688 * Fire before process file
28689 * @param {Roo.bootstrap.DocumentManager} this
28690 * @param {Object} file
28694 * @event previewrendered
28695 * Fire when preview rendered
28696 * @param {Roo.bootstrap.DocumentManager} this
28697 * @param {Object} file
28699 "previewrendered" : true
28704 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28713 paramName : 'imageUpload',
28714 toolTipName : 'filename',
28717 labelAlign : 'left',
28727 getAutoCreate : function()
28729 var managerWidget = {
28731 cls : 'roo-document-manager',
28735 cls : 'roo-document-manager-selector',
28740 cls : 'roo-document-manager-uploader',
28744 cls : 'roo-document-manager-upload-btn',
28745 html : '<i class="fa fa-plus"></i>'
28756 cls : 'column col-md-12',
28761 if(this.fieldLabel.length){
28766 cls : 'column col-md-12',
28767 html : this.fieldLabel
28771 cls : 'column col-md-12',
28776 if(this.labelAlign == 'left'){
28781 html : this.fieldLabel
28790 if(this.labelWidth > 12){
28791 content[0].style = "width: " + this.labelWidth + 'px';
28794 if(this.labelWidth < 13 && this.labelmd == 0){
28795 this.labelmd = this.labelWidth;
28798 if(this.labellg > 0){
28799 content[0].cls += ' col-lg-' + this.labellg;
28800 content[1].cls += ' col-lg-' + (12 - this.labellg);
28803 if(this.labelmd > 0){
28804 content[0].cls += ' col-md-' + this.labelmd;
28805 content[1].cls += ' col-md-' + (12 - this.labelmd);
28808 if(this.labelsm > 0){
28809 content[0].cls += ' col-sm-' + this.labelsm;
28810 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28813 if(this.labelxs > 0){
28814 content[0].cls += ' col-xs-' + this.labelxs;
28815 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28823 cls : 'row clearfix',
28831 initEvents : function()
28833 this.managerEl = this.el.select('.roo-document-manager', true).first();
28834 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28836 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28837 this.selectorEl.hide();
28840 this.selectorEl.attr('multiple', 'multiple');
28843 this.selectorEl.on('change', this.onFileSelected, this);
28845 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28846 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28848 this.uploader.on('click', this.onUploaderClick, this);
28850 this.renderProgressDialog();
28854 window.addEventListener("resize", function() { _this.refresh(); } );
28856 this.fireEvent('initial', this);
28859 renderProgressDialog : function()
28863 this.progressDialog = new Roo.bootstrap.Modal({
28864 cls : 'roo-document-manager-progress-dialog',
28865 allow_close : false,
28875 btnclick : function() {
28876 _this.uploadCancel();
28882 this.progressDialog.render(Roo.get(document.body));
28884 this.progress = new Roo.bootstrap.Progress({
28885 cls : 'roo-document-manager-progress',
28890 this.progress.render(this.progressDialog.getChildContainer());
28892 this.progressBar = new Roo.bootstrap.ProgressBar({
28893 cls : 'roo-document-manager-progress-bar',
28896 aria_valuemax : 12,
28900 this.progressBar.render(this.progress.getChildContainer());
28903 onUploaderClick : function(e)
28905 e.preventDefault();
28907 if(this.fireEvent('beforeselectfile', this) != false){
28908 this.selectorEl.dom.click();
28913 onFileSelected : function(e)
28915 e.preventDefault();
28917 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28921 Roo.each(this.selectorEl.dom.files, function(file){
28922 if(this.fireEvent('inspect', this, file) != false){
28923 this.files.push(file);
28933 this.selectorEl.dom.value = '';
28935 if(!this.files || !this.files.length){
28939 if(this.boxes > 0 && this.files.length > this.boxes){
28940 this.files = this.files.slice(0, this.boxes);
28943 this.uploader.show();
28945 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28946 this.uploader.hide();
28955 Roo.each(this.files, function(file){
28957 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28958 var f = this.renderPreview(file);
28963 if(file.type.indexOf('image') != -1){
28964 this.delegates.push(
28966 _this.process(file);
28967 }).createDelegate(this)
28975 _this.process(file);
28976 }).createDelegate(this)
28981 this.files = files;
28983 this.delegates = this.delegates.concat(docs);
28985 if(!this.delegates.length){
28990 this.progressBar.aria_valuemax = this.delegates.length;
28997 arrange : function()
28999 if(!this.delegates.length){
29000 this.progressDialog.hide();
29005 var delegate = this.delegates.shift();
29007 this.progressDialog.show();
29009 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29011 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29016 refresh : function()
29018 this.uploader.show();
29020 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29021 this.uploader.hide();
29024 Roo.isTouch ? this.closable(false) : this.closable(true);
29026 this.fireEvent('refresh', this);
29029 onRemove : function(e, el, o)
29031 e.preventDefault();
29033 this.fireEvent('remove', this, o);
29037 remove : function(o)
29041 Roo.each(this.files, function(file){
29042 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29051 this.files = files;
29058 Roo.each(this.files, function(file){
29063 file.target.remove();
29072 onClick : function(e, el, o)
29074 e.preventDefault();
29076 this.fireEvent('click', this, o);
29080 closable : function(closable)
29082 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29084 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29096 xhrOnLoad : function(xhr)
29098 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29102 if (xhr.readyState !== 4) {
29104 this.fireEvent('exception', this, xhr);
29108 var response = Roo.decode(xhr.responseText);
29110 if(!response.success){
29112 this.fireEvent('exception', this, xhr);
29116 var file = this.renderPreview(response.data);
29118 this.files.push(file);
29122 this.fireEvent('afterupload', this, xhr);
29126 xhrOnError : function(xhr)
29128 Roo.log('xhr on error');
29130 var response = Roo.decode(xhr.responseText);
29137 process : function(file)
29139 if(this.fireEvent('process', this, file) !== false){
29140 if(this.editable && file.type.indexOf('image') != -1){
29141 this.fireEvent('edit', this, file);
29145 this.uploadStart(file, false);
29152 uploadStart : function(file, crop)
29154 this.xhr = new XMLHttpRequest();
29156 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29161 file.xhr = this.xhr;
29163 this.managerEl.createChild({
29165 cls : 'roo-document-manager-loading',
29169 tooltip : file.name,
29170 cls : 'roo-document-manager-thumb',
29171 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29177 this.xhr.open(this.method, this.url, true);
29180 "Accept": "application/json",
29181 "Cache-Control": "no-cache",
29182 "X-Requested-With": "XMLHttpRequest"
29185 for (var headerName in headers) {
29186 var headerValue = headers[headerName];
29188 this.xhr.setRequestHeader(headerName, headerValue);
29194 this.xhr.onload = function()
29196 _this.xhrOnLoad(_this.xhr);
29199 this.xhr.onerror = function()
29201 _this.xhrOnError(_this.xhr);
29204 var formData = new FormData();
29206 formData.append('returnHTML', 'NO');
29209 formData.append('crop', crop);
29212 formData.append(this.paramName, file, file.name);
29219 if(this.fireEvent('prepare', this, formData, options) != false){
29221 if(options.manually){
29225 this.xhr.send(formData);
29229 this.uploadCancel();
29232 uploadCancel : function()
29238 this.delegates = [];
29240 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29247 renderPreview : function(file)
29249 if(typeof(file.target) != 'undefined' && file.target){
29253 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29255 var previewEl = this.managerEl.createChild({
29257 cls : 'roo-document-manager-preview',
29261 tooltip : file[this.toolTipName],
29262 cls : 'roo-document-manager-thumb',
29263 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29268 html : '<i class="fa fa-times-circle"></i>'
29273 var close = previewEl.select('button.close', true).first();
29275 close.on('click', this.onRemove, this, file);
29277 file.target = previewEl;
29279 var image = previewEl.select('img', true).first();
29283 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29285 image.on('click', this.onClick, this, file);
29287 this.fireEvent('previewrendered', this, file);
29293 onPreviewLoad : function(file, image)
29295 if(typeof(file.target) == 'undefined' || !file.target){
29299 var width = image.dom.naturalWidth || image.dom.width;
29300 var height = image.dom.naturalHeight || image.dom.height;
29302 if(width > height){
29303 file.target.addClass('wide');
29307 file.target.addClass('tall');
29312 uploadFromSource : function(file, crop)
29314 this.xhr = new XMLHttpRequest();
29316 this.managerEl.createChild({
29318 cls : 'roo-document-manager-loading',
29322 tooltip : file.name,
29323 cls : 'roo-document-manager-thumb',
29324 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29330 this.xhr.open(this.method, this.url, true);
29333 "Accept": "application/json",
29334 "Cache-Control": "no-cache",
29335 "X-Requested-With": "XMLHttpRequest"
29338 for (var headerName in headers) {
29339 var headerValue = headers[headerName];
29341 this.xhr.setRequestHeader(headerName, headerValue);
29347 this.xhr.onload = function()
29349 _this.xhrOnLoad(_this.xhr);
29352 this.xhr.onerror = function()
29354 _this.xhrOnError(_this.xhr);
29357 var formData = new FormData();
29359 formData.append('returnHTML', 'NO');
29361 formData.append('crop', crop);
29363 if(typeof(file.filename) != 'undefined'){
29364 formData.append('filename', file.filename);
29367 if(typeof(file.mimetype) != 'undefined'){
29368 formData.append('mimetype', file.mimetype);
29373 if(this.fireEvent('prepare', this, formData) != false){
29374 this.xhr.send(formData);
29384 * @class Roo.bootstrap.DocumentViewer
29385 * @extends Roo.bootstrap.Component
29386 * Bootstrap DocumentViewer class
29387 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29388 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29391 * Create a new DocumentViewer
29392 * @param {Object} config The config object
29395 Roo.bootstrap.DocumentViewer = function(config){
29396 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29401 * Fire after initEvent
29402 * @param {Roo.bootstrap.DocumentViewer} this
29408 * @param {Roo.bootstrap.DocumentViewer} this
29413 * Fire after download button
29414 * @param {Roo.bootstrap.DocumentViewer} this
29419 * Fire after trash button
29420 * @param {Roo.bootstrap.DocumentViewer} this
29427 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29429 showDownload : true,
29433 getAutoCreate : function()
29437 cls : 'roo-document-viewer',
29441 cls : 'roo-document-viewer-body',
29445 cls : 'roo-document-viewer-thumb',
29449 cls : 'roo-document-viewer-image'
29457 cls : 'roo-document-viewer-footer',
29460 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29464 cls : 'btn-group roo-document-viewer-download',
29468 cls : 'btn btn-default',
29469 html : '<i class="fa fa-download"></i>'
29475 cls : 'btn-group roo-document-viewer-trash',
29479 cls : 'btn btn-default',
29480 html : '<i class="fa fa-trash"></i>'
29493 initEvents : function()
29495 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29496 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29498 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29499 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29501 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29502 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29504 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29505 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29507 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29508 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29510 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29511 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29513 this.bodyEl.on('click', this.onClick, this);
29514 this.downloadBtn.on('click', this.onDownload, this);
29515 this.trashBtn.on('click', this.onTrash, this);
29517 this.downloadBtn.hide();
29518 this.trashBtn.hide();
29520 if(this.showDownload){
29521 this.downloadBtn.show();
29524 if(this.showTrash){
29525 this.trashBtn.show();
29528 if(!this.showDownload && !this.showTrash) {
29529 this.footerEl.hide();
29534 initial : function()
29536 this.fireEvent('initial', this);
29540 onClick : function(e)
29542 e.preventDefault();
29544 this.fireEvent('click', this);
29547 onDownload : function(e)
29549 e.preventDefault();
29551 this.fireEvent('download', this);
29554 onTrash : function(e)
29556 e.preventDefault();
29558 this.fireEvent('trash', this);
29570 * @class Roo.bootstrap.NavProgressBar
29571 * @extends Roo.bootstrap.Component
29572 * Bootstrap NavProgressBar class
29575 * Create a new nav progress bar
29576 * @param {Object} config The config object
29579 Roo.bootstrap.NavProgressBar = function(config){
29580 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29582 this.bullets = this.bullets || [];
29584 // Roo.bootstrap.NavProgressBar.register(this);
29588 * Fires when the active item changes
29589 * @param {Roo.bootstrap.NavProgressBar} this
29590 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29591 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29598 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29603 getAutoCreate : function()
29605 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29609 cls : 'roo-navigation-bar-group',
29613 cls : 'roo-navigation-top-bar'
29617 cls : 'roo-navigation-bullets-bar',
29621 cls : 'roo-navigation-bar'
29628 cls : 'roo-navigation-bottom-bar'
29638 initEvents: function()
29643 onRender : function(ct, position)
29645 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29647 if(this.bullets.length){
29648 Roo.each(this.bullets, function(b){
29657 addItem : function(cfg)
29659 var item = new Roo.bootstrap.NavProgressItem(cfg);
29661 item.parentId = this.id;
29662 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29665 var top = new Roo.bootstrap.Element({
29667 cls : 'roo-navigation-bar-text'
29670 var bottom = new Roo.bootstrap.Element({
29672 cls : 'roo-navigation-bar-text'
29675 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29676 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29678 var topText = new Roo.bootstrap.Element({
29680 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29683 var bottomText = new Roo.bootstrap.Element({
29685 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29688 topText.onRender(top.el, null);
29689 bottomText.onRender(bottom.el, null);
29692 item.bottomEl = bottom;
29695 this.barItems.push(item);
29700 getActive : function()
29702 var active = false;
29704 Roo.each(this.barItems, function(v){
29706 if (!v.isActive()) {
29718 setActiveItem : function(item)
29722 Roo.each(this.barItems, function(v){
29723 if (v.rid == item.rid) {
29727 if (v.isActive()) {
29728 v.setActive(false);
29733 item.setActive(true);
29735 this.fireEvent('changed', this, item, prev);
29738 getBarItem: function(rid)
29742 Roo.each(this.barItems, function(e) {
29743 if (e.rid != rid) {
29754 indexOfItem : function(item)
29758 Roo.each(this.barItems, function(v, i){
29760 if (v.rid != item.rid) {
29771 setActiveNext : function()
29773 var i = this.indexOfItem(this.getActive());
29775 if (i > this.barItems.length) {
29779 this.setActiveItem(this.barItems[i+1]);
29782 setActivePrev : function()
29784 var i = this.indexOfItem(this.getActive());
29790 this.setActiveItem(this.barItems[i-1]);
29793 format : function()
29795 if(!this.barItems.length){
29799 var width = 100 / this.barItems.length;
29801 Roo.each(this.barItems, function(i){
29802 i.el.setStyle('width', width + '%');
29803 i.topEl.el.setStyle('width', width + '%');
29804 i.bottomEl.el.setStyle('width', width + '%');
29813 * Nav Progress Item
29818 * @class Roo.bootstrap.NavProgressItem
29819 * @extends Roo.bootstrap.Component
29820 * Bootstrap NavProgressItem class
29821 * @cfg {String} rid the reference id
29822 * @cfg {Boolean} active (true|false) Is item active default false
29823 * @cfg {Boolean} disabled (true|false) Is item active default false
29824 * @cfg {String} html
29825 * @cfg {String} position (top|bottom) text position default bottom
29826 * @cfg {String} icon show icon instead of number
29829 * Create a new NavProgressItem
29830 * @param {Object} config The config object
29832 Roo.bootstrap.NavProgressItem = function(config){
29833 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29838 * The raw click event for the entire grid.
29839 * @param {Roo.bootstrap.NavProgressItem} this
29840 * @param {Roo.EventObject} e
29847 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29853 position : 'bottom',
29856 getAutoCreate : function()
29858 var iconCls = 'roo-navigation-bar-item-icon';
29860 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29864 cls: 'roo-navigation-bar-item',
29874 cfg.cls += ' active';
29877 cfg.cls += ' disabled';
29883 disable : function()
29885 this.setDisabled(true);
29888 enable : function()
29890 this.setDisabled(false);
29893 initEvents: function()
29895 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29897 this.iconEl.on('click', this.onClick, this);
29900 onClick : function(e)
29902 e.preventDefault();
29908 if(this.fireEvent('click', this, e) === false){
29912 this.parent().setActiveItem(this);
29915 isActive: function ()
29917 return this.active;
29920 setActive : function(state)
29922 if(this.active == state){
29926 this.active = state;
29929 this.el.addClass('active');
29933 this.el.removeClass('active');
29938 setDisabled : function(state)
29940 if(this.disabled == state){
29944 this.disabled = state;
29947 this.el.addClass('disabled');
29951 this.el.removeClass('disabled');
29954 tooltipEl : function()
29956 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29969 * @class Roo.bootstrap.FieldLabel
29970 * @extends Roo.bootstrap.Component
29971 * Bootstrap FieldLabel class
29972 * @cfg {String} html contents of the element
29973 * @cfg {String} tag tag of the element default label
29974 * @cfg {String} cls class of the element
29975 * @cfg {String} target label target
29976 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29977 * @cfg {String} invalidClass default "text-warning"
29978 * @cfg {String} validClass default "text-success"
29979 * @cfg {String} iconTooltip default "This field is required"
29980 * @cfg {String} indicatorpos (left|right) default left
29983 * Create a new FieldLabel
29984 * @param {Object} config The config object
29987 Roo.bootstrap.FieldLabel = function(config){
29988 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29993 * Fires after the field has been marked as invalid.
29994 * @param {Roo.form.FieldLabel} this
29995 * @param {String} msg The validation message
30000 * Fires after the field has been validated with no errors.
30001 * @param {Roo.form.FieldLabel} this
30007 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30014 invalidClass : 'has-warning',
30015 validClass : 'has-success',
30016 iconTooltip : 'This field is required',
30017 indicatorpos : 'left',
30019 getAutoCreate : function(){
30022 if (!this.allowBlank) {
30028 cls : 'roo-bootstrap-field-label ' + this.cls,
30033 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30034 tooltip : this.iconTooltip
30043 if(this.indicatorpos == 'right'){
30046 cls : 'roo-bootstrap-field-label ' + this.cls,
30055 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30056 tooltip : this.iconTooltip
30065 initEvents: function()
30067 Roo.bootstrap.Element.superclass.initEvents.call(this);
30069 this.indicator = this.indicatorEl();
30071 if(this.indicator){
30072 this.indicator.removeClass('visible');
30073 this.indicator.addClass('invisible');
30076 Roo.bootstrap.FieldLabel.register(this);
30079 indicatorEl : function()
30081 var indicator = this.el.select('i.roo-required-indicator',true).first();
30092 * Mark this field as valid
30094 markValid : function()
30096 if(this.indicator){
30097 this.indicator.removeClass('visible');
30098 this.indicator.addClass('invisible');
30101 this.el.removeClass(this.invalidClass);
30103 this.el.addClass(this.validClass);
30105 this.fireEvent('valid', this);
30109 * Mark this field as invalid
30110 * @param {String} msg The validation message
30112 markInvalid : function(msg)
30114 if(this.indicator){
30115 this.indicator.removeClass('invisible');
30116 this.indicator.addClass('visible');
30119 this.el.removeClass(this.validClass);
30121 this.el.addClass(this.invalidClass);
30123 this.fireEvent('invalid', this, msg);
30129 Roo.apply(Roo.bootstrap.FieldLabel, {
30134 * register a FieldLabel Group
30135 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30137 register : function(label)
30139 if(this.groups.hasOwnProperty(label.target)){
30143 this.groups[label.target] = label;
30147 * fetch a FieldLabel Group based on the target
30148 * @param {string} target
30149 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30151 get: function(target) {
30152 if (typeof(this.groups[target]) == 'undefined') {
30156 return this.groups[target] ;
30165 * page DateSplitField.
30171 * @class Roo.bootstrap.DateSplitField
30172 * @extends Roo.bootstrap.Component
30173 * Bootstrap DateSplitField class
30174 * @cfg {string} fieldLabel - the label associated
30175 * @cfg {Number} labelWidth set the width of label (0-12)
30176 * @cfg {String} labelAlign (top|left)
30177 * @cfg {Boolean} dayAllowBlank (true|false) default false
30178 * @cfg {Boolean} monthAllowBlank (true|false) default false
30179 * @cfg {Boolean} yearAllowBlank (true|false) default false
30180 * @cfg {string} dayPlaceholder
30181 * @cfg {string} monthPlaceholder
30182 * @cfg {string} yearPlaceholder
30183 * @cfg {string} dayFormat default 'd'
30184 * @cfg {string} monthFormat default 'm'
30185 * @cfg {string} yearFormat default 'Y'
30186 * @cfg {Number} labellg set the width of label (1-12)
30187 * @cfg {Number} labelmd set the width of label (1-12)
30188 * @cfg {Number} labelsm set the width of label (1-12)
30189 * @cfg {Number} labelxs set the width of label (1-12)
30193 * Create a new DateSplitField
30194 * @param {Object} config The config object
30197 Roo.bootstrap.DateSplitField = function(config){
30198 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30204 * getting the data of years
30205 * @param {Roo.bootstrap.DateSplitField} this
30206 * @param {Object} years
30211 * getting the data of days
30212 * @param {Roo.bootstrap.DateSplitField} this
30213 * @param {Object} days
30218 * Fires after the field has been marked as invalid.
30219 * @param {Roo.form.Field} this
30220 * @param {String} msg The validation message
30225 * Fires after the field has been validated with no errors.
30226 * @param {Roo.form.Field} this
30232 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30235 labelAlign : 'top',
30237 dayAllowBlank : false,
30238 monthAllowBlank : false,
30239 yearAllowBlank : false,
30240 dayPlaceholder : '',
30241 monthPlaceholder : '',
30242 yearPlaceholder : '',
30246 isFormField : true,
30252 getAutoCreate : function()
30256 cls : 'row roo-date-split-field-group',
30261 cls : 'form-hidden-field roo-date-split-field-group-value',
30267 var labelCls = 'col-md-12';
30268 var contentCls = 'col-md-4';
30270 if(this.fieldLabel){
30274 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30278 html : this.fieldLabel
30283 if(this.labelAlign == 'left'){
30285 if(this.labelWidth > 12){
30286 label.style = "width: " + this.labelWidth + 'px';
30289 if(this.labelWidth < 13 && this.labelmd == 0){
30290 this.labelmd = this.labelWidth;
30293 if(this.labellg > 0){
30294 labelCls = ' col-lg-' + this.labellg;
30295 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30298 if(this.labelmd > 0){
30299 labelCls = ' col-md-' + this.labelmd;
30300 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30303 if(this.labelsm > 0){
30304 labelCls = ' col-sm-' + this.labelsm;
30305 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30308 if(this.labelxs > 0){
30309 labelCls = ' col-xs-' + this.labelxs;
30310 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30314 label.cls += ' ' + labelCls;
30316 cfg.cn.push(label);
30319 Roo.each(['day', 'month', 'year'], function(t){
30322 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30329 inputEl: function ()
30331 return this.el.select('.roo-date-split-field-group-value', true).first();
30334 onRender : function(ct, position)
30338 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30340 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30342 this.dayField = new Roo.bootstrap.ComboBox({
30343 allowBlank : this.dayAllowBlank,
30344 alwaysQuery : true,
30345 displayField : 'value',
30348 forceSelection : true,
30350 placeholder : this.dayPlaceholder,
30351 selectOnFocus : true,
30352 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30353 triggerAction : 'all',
30355 valueField : 'value',
30356 store : new Roo.data.SimpleStore({
30357 data : (function() {
30359 _this.fireEvent('days', _this, days);
30362 fields : [ 'value' ]
30365 select : function (_self, record, index)
30367 _this.setValue(_this.getValue());
30372 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30374 this.monthField = new Roo.bootstrap.MonthField({
30375 after : '<i class=\"fa fa-calendar\"></i>',
30376 allowBlank : this.monthAllowBlank,
30377 placeholder : this.monthPlaceholder,
30380 render : function (_self)
30382 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30383 e.preventDefault();
30387 select : function (_self, oldvalue, newvalue)
30389 _this.setValue(_this.getValue());
30394 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30396 this.yearField = new Roo.bootstrap.ComboBox({
30397 allowBlank : this.yearAllowBlank,
30398 alwaysQuery : true,
30399 displayField : 'value',
30402 forceSelection : true,
30404 placeholder : this.yearPlaceholder,
30405 selectOnFocus : true,
30406 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30407 triggerAction : 'all',
30409 valueField : 'value',
30410 store : new Roo.data.SimpleStore({
30411 data : (function() {
30413 _this.fireEvent('years', _this, years);
30416 fields : [ 'value' ]
30419 select : function (_self, record, index)
30421 _this.setValue(_this.getValue());
30426 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30429 setValue : function(v, format)
30431 this.inputEl.dom.value = v;
30433 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30435 var d = Date.parseDate(v, f);
30442 this.setDay(d.format(this.dayFormat));
30443 this.setMonth(d.format(this.monthFormat));
30444 this.setYear(d.format(this.yearFormat));
30451 setDay : function(v)
30453 this.dayField.setValue(v);
30454 this.inputEl.dom.value = this.getValue();
30459 setMonth : function(v)
30461 this.monthField.setValue(v, true);
30462 this.inputEl.dom.value = this.getValue();
30467 setYear : function(v)
30469 this.yearField.setValue(v);
30470 this.inputEl.dom.value = this.getValue();
30475 getDay : function()
30477 return this.dayField.getValue();
30480 getMonth : function()
30482 return this.monthField.getValue();
30485 getYear : function()
30487 return this.yearField.getValue();
30490 getValue : function()
30492 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30494 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30504 this.inputEl.dom.value = '';
30509 validate : function()
30511 var d = this.dayField.validate();
30512 var m = this.monthField.validate();
30513 var y = this.yearField.validate();
30518 (!this.dayAllowBlank && !d) ||
30519 (!this.monthAllowBlank && !m) ||
30520 (!this.yearAllowBlank && !y)
30525 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30534 this.markInvalid();
30539 markValid : function()
30542 var label = this.el.select('label', true).first();
30543 var icon = this.el.select('i.fa-star', true).first();
30549 this.fireEvent('valid', this);
30553 * Mark this field as invalid
30554 * @param {String} msg The validation message
30556 markInvalid : function(msg)
30559 var label = this.el.select('label', true).first();
30560 var icon = this.el.select('i.fa-star', true).first();
30562 if(label && !icon){
30563 this.el.select('.roo-date-split-field-label', true).createChild({
30565 cls : 'text-danger fa fa-lg fa-star',
30566 tooltip : 'This field is required',
30567 style : 'margin-right:5px;'
30571 this.fireEvent('invalid', this, msg);
30574 clearInvalid : function()
30576 var label = this.el.select('label', true).first();
30577 var icon = this.el.select('i.fa-star', true).first();
30583 this.fireEvent('valid', this);
30586 getName: function()
30596 * http://masonry.desandro.com
30598 * The idea is to render all the bricks based on vertical width...
30600 * The original code extends 'outlayer' - we might need to use that....
30606 * @class Roo.bootstrap.LayoutMasonry
30607 * @extends Roo.bootstrap.Component
30608 * Bootstrap Layout Masonry class
30611 * Create a new Element
30612 * @param {Object} config The config object
30615 Roo.bootstrap.LayoutMasonry = function(config){
30617 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30621 Roo.bootstrap.LayoutMasonry.register(this);
30627 * Fire after layout the items
30628 * @param {Roo.bootstrap.LayoutMasonry} this
30629 * @param {Roo.EventObject} e
30636 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30639 * @cfg {Boolean} isLayoutInstant = no animation?
30641 isLayoutInstant : false, // needed?
30644 * @cfg {Number} boxWidth width of the columns
30649 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30654 * @cfg {Number} padWidth padding below box..
30659 * @cfg {Number} gutter gutter width..
30664 * @cfg {Number} maxCols maximum number of columns
30670 * @cfg {Boolean} isAutoInitial defalut true
30672 isAutoInitial : true,
30677 * @cfg {Boolean} isHorizontal defalut false
30679 isHorizontal : false,
30681 currentSize : null,
30687 bricks: null, //CompositeElement
30691 _isLayoutInited : false,
30693 // isAlternative : false, // only use for vertical layout...
30696 * @cfg {Number} alternativePadWidth padding below box..
30698 alternativePadWidth : 50,
30700 selectedBrick : [],
30702 getAutoCreate : function(){
30704 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30708 cls: 'blog-masonary-wrapper ' + this.cls,
30710 cls : 'mas-boxes masonary'
30717 getChildContainer: function( )
30719 if (this.boxesEl) {
30720 return this.boxesEl;
30723 this.boxesEl = this.el.select('.mas-boxes').first();
30725 return this.boxesEl;
30729 initEvents : function()
30733 if(this.isAutoInitial){
30734 Roo.log('hook children rendered');
30735 this.on('childrenrendered', function() {
30736 Roo.log('children rendered');
30742 initial : function()
30744 this.selectedBrick = [];
30746 this.currentSize = this.el.getBox(true);
30748 Roo.EventManager.onWindowResize(this.resize, this);
30750 if(!this.isAutoInitial){
30758 //this.layout.defer(500,this);
30762 resize : function()
30764 var cs = this.el.getBox(true);
30767 this.currentSize.width == cs.width &&
30768 this.currentSize.x == cs.x &&
30769 this.currentSize.height == cs.height &&
30770 this.currentSize.y == cs.y
30772 Roo.log("no change in with or X or Y");
30776 this.currentSize = cs;
30782 layout : function()
30784 this._resetLayout();
30786 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30788 this.layoutItems( isInstant );
30790 this._isLayoutInited = true;
30792 this.fireEvent('layout', this);
30796 _resetLayout : function()
30798 if(this.isHorizontal){
30799 this.horizontalMeasureColumns();
30803 this.verticalMeasureColumns();
30807 verticalMeasureColumns : function()
30809 this.getContainerWidth();
30811 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30812 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30816 var boxWidth = this.boxWidth + this.padWidth;
30818 if(this.containerWidth < this.boxWidth){
30819 boxWidth = this.containerWidth
30822 var containerWidth = this.containerWidth;
30824 var cols = Math.floor(containerWidth / boxWidth);
30826 this.cols = Math.max( cols, 1 );
30828 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30830 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30832 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30834 this.colWidth = boxWidth + avail - this.padWidth;
30836 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30837 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30840 horizontalMeasureColumns : function()
30842 this.getContainerWidth();
30844 var boxWidth = this.boxWidth;
30846 if(this.containerWidth < boxWidth){
30847 boxWidth = this.containerWidth;
30850 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30852 this.el.setHeight(boxWidth);
30856 getContainerWidth : function()
30858 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30861 layoutItems : function( isInstant )
30863 Roo.log(this.bricks);
30865 var items = Roo.apply([], this.bricks);
30867 if(this.isHorizontal){
30868 this._horizontalLayoutItems( items , isInstant );
30872 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30873 // this._verticalAlternativeLayoutItems( items , isInstant );
30877 this._verticalLayoutItems( items , isInstant );
30881 _verticalLayoutItems : function ( items , isInstant)
30883 if ( !items || !items.length ) {
30888 ['xs', 'xs', 'xs', 'tall'],
30889 ['xs', 'xs', 'tall'],
30890 ['xs', 'xs', 'sm'],
30891 ['xs', 'xs', 'xs'],
30897 ['sm', 'xs', 'xs'],
30901 ['tall', 'xs', 'xs', 'xs'],
30902 ['tall', 'xs', 'xs'],
30914 Roo.each(items, function(item, k){
30916 switch (item.size) {
30917 // these layouts take up a full box,
30928 boxes.push([item]);
30951 var filterPattern = function(box, length)
30959 var pattern = box.slice(0, length);
30963 Roo.each(pattern, function(i){
30964 format.push(i.size);
30967 Roo.each(standard, function(s){
30969 if(String(s) != String(format)){
30978 if(!match && length == 1){
30983 filterPattern(box, length - 1);
30987 queue.push(pattern);
30989 box = box.slice(length, box.length);
30991 filterPattern(box, 4);
30997 Roo.each(boxes, function(box, k){
31003 if(box.length == 1){
31008 filterPattern(box, 4);
31012 this._processVerticalLayoutQueue( queue, isInstant );
31016 // _verticalAlternativeLayoutItems : function( items , isInstant )
31018 // if ( !items || !items.length ) {
31022 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31026 _horizontalLayoutItems : function ( items , isInstant)
31028 if ( !items || !items.length || items.length < 3) {
31034 var eItems = items.slice(0, 3);
31036 items = items.slice(3, items.length);
31039 ['xs', 'xs', 'xs', 'wide'],
31040 ['xs', 'xs', 'wide'],
31041 ['xs', 'xs', 'sm'],
31042 ['xs', 'xs', 'xs'],
31048 ['sm', 'xs', 'xs'],
31052 ['wide', 'xs', 'xs', 'xs'],
31053 ['wide', 'xs', 'xs'],
31066 Roo.each(items, function(item, k){
31068 switch (item.size) {
31079 boxes.push([item]);
31103 var filterPattern = function(box, length)
31111 var pattern = box.slice(0, length);
31115 Roo.each(pattern, function(i){
31116 format.push(i.size);
31119 Roo.each(standard, function(s){
31121 if(String(s) != String(format)){
31130 if(!match && length == 1){
31135 filterPattern(box, length - 1);
31139 queue.push(pattern);
31141 box = box.slice(length, box.length);
31143 filterPattern(box, 4);
31149 Roo.each(boxes, function(box, k){
31155 if(box.length == 1){
31160 filterPattern(box, 4);
31167 var pos = this.el.getBox(true);
31171 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31173 var hit_end = false;
31175 Roo.each(queue, function(box){
31179 Roo.each(box, function(b){
31181 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31191 Roo.each(box, function(b){
31193 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31196 mx = Math.max(mx, b.x);
31200 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31204 Roo.each(box, function(b){
31206 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31220 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31223 /** Sets position of item in DOM
31224 * @param {Element} item
31225 * @param {Number} x - horizontal position
31226 * @param {Number} y - vertical position
31227 * @param {Boolean} isInstant - disables transitions
31229 _processVerticalLayoutQueue : function( queue, isInstant )
31231 var pos = this.el.getBox(true);
31236 for (var i = 0; i < this.cols; i++){
31240 Roo.each(queue, function(box, k){
31242 var col = k % this.cols;
31244 Roo.each(box, function(b,kk){
31246 b.el.position('absolute');
31248 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31249 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31251 if(b.size == 'md-left' || b.size == 'md-right'){
31252 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31253 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31256 b.el.setWidth(width);
31257 b.el.setHeight(height);
31259 b.el.select('iframe',true).setSize(width,height);
31263 for (var i = 0; i < this.cols; i++){
31265 if(maxY[i] < maxY[col]){
31270 col = Math.min(col, i);
31274 x = pos.x + col * (this.colWidth + this.padWidth);
31278 var positions = [];
31280 switch (box.length){
31282 positions = this.getVerticalOneBoxColPositions(x, y, box);
31285 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31288 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31291 positions = this.getVerticalFourBoxColPositions(x, y, box);
31297 Roo.each(box, function(b,kk){
31299 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31301 var sz = b.el.getSize();
31303 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31311 for (var i = 0; i < this.cols; i++){
31312 mY = Math.max(mY, maxY[i]);
31315 this.el.setHeight(mY - pos.y);
31319 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31321 // var pos = this.el.getBox(true);
31324 // var maxX = pos.right;
31326 // var maxHeight = 0;
31328 // Roo.each(items, function(item, k){
31332 // item.el.position('absolute');
31334 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31336 // item.el.setWidth(width);
31338 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31340 // item.el.setHeight(height);
31343 // item.el.setXY([x, y], isInstant ? false : true);
31345 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31348 // y = y + height + this.alternativePadWidth;
31350 // maxHeight = maxHeight + height + this.alternativePadWidth;
31354 // this.el.setHeight(maxHeight);
31358 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31360 var pos = this.el.getBox(true);
31365 var maxX = pos.right;
31367 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31369 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31371 Roo.each(queue, function(box, k){
31373 Roo.each(box, function(b, kk){
31375 b.el.position('absolute');
31377 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31378 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31380 if(b.size == 'md-left' || b.size == 'md-right'){
31381 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31382 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31385 b.el.setWidth(width);
31386 b.el.setHeight(height);
31394 var positions = [];
31396 switch (box.length){
31398 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31401 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31404 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31407 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31413 Roo.each(box, function(b,kk){
31415 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31417 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31425 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31427 Roo.each(eItems, function(b,k){
31429 b.size = (k == 0) ? 'sm' : 'xs';
31430 b.x = (k == 0) ? 2 : 1;
31431 b.y = (k == 0) ? 2 : 1;
31433 b.el.position('absolute');
31435 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31437 b.el.setWidth(width);
31439 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31441 b.el.setHeight(height);
31445 var positions = [];
31448 x : maxX - this.unitWidth * 2 - this.gutter,
31453 x : maxX - this.unitWidth,
31454 y : minY + (this.unitWidth + this.gutter) * 2
31458 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31462 Roo.each(eItems, function(b,k){
31464 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31470 getVerticalOneBoxColPositions : function(x, y, box)
31474 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31476 if(box[0].size == 'md-left'){
31480 if(box[0].size == 'md-right'){
31485 x : x + (this.unitWidth + this.gutter) * rand,
31492 getVerticalTwoBoxColPositions : function(x, y, box)
31496 if(box[0].size == 'xs'){
31500 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31504 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31518 x : x + (this.unitWidth + this.gutter) * 2,
31519 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31526 getVerticalThreeBoxColPositions : function(x, y, box)
31530 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31538 x : x + (this.unitWidth + this.gutter) * 1,
31543 x : x + (this.unitWidth + this.gutter) * 2,
31551 if(box[0].size == 'xs' && box[1].size == 'xs'){
31560 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31564 x : x + (this.unitWidth + this.gutter) * 1,
31578 x : x + (this.unitWidth + this.gutter) * 2,
31583 x : x + (this.unitWidth + this.gutter) * 2,
31584 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31591 getVerticalFourBoxColPositions : function(x, y, box)
31595 if(box[0].size == 'xs'){
31604 y : y + (this.unitHeight + this.gutter) * 1
31609 y : y + (this.unitHeight + this.gutter) * 2
31613 x : x + (this.unitWidth + this.gutter) * 1,
31627 x : x + (this.unitWidth + this.gutter) * 2,
31632 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31633 y : y + (this.unitHeight + this.gutter) * 1
31637 x : x + (this.unitWidth + this.gutter) * 2,
31638 y : y + (this.unitWidth + this.gutter) * 2
31645 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31649 if(box[0].size == 'md-left'){
31651 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31658 if(box[0].size == 'md-right'){
31660 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31661 y : minY + (this.unitWidth + this.gutter) * 1
31667 var rand = Math.floor(Math.random() * (4 - box[0].y));
31670 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31671 y : minY + (this.unitWidth + this.gutter) * rand
31678 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31682 if(box[0].size == 'xs'){
31685 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31690 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31691 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31699 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31704 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31705 y : minY + (this.unitWidth + this.gutter) * 2
31712 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31716 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31719 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31724 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31725 y : minY + (this.unitWidth + this.gutter) * 1
31729 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31730 y : minY + (this.unitWidth + this.gutter) * 2
31737 if(box[0].size == 'xs' && box[1].size == 'xs'){
31740 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31745 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31750 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31751 y : minY + (this.unitWidth + this.gutter) * 1
31759 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31764 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31765 y : minY + (this.unitWidth + this.gutter) * 2
31769 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31770 y : minY + (this.unitWidth + this.gutter) * 2
31777 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31781 if(box[0].size == 'xs'){
31784 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31789 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31794 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),
31799 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31800 y : minY + (this.unitWidth + this.gutter) * 1
31808 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31813 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31814 y : minY + (this.unitWidth + this.gutter) * 2
31818 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31819 y : minY + (this.unitWidth + this.gutter) * 2
31823 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),
31824 y : minY + (this.unitWidth + this.gutter) * 2
31832 * remove a Masonry Brick
31833 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31835 removeBrick : function(brick_id)
31841 for (var i = 0; i<this.bricks.length; i++) {
31842 if (this.bricks[i].id == brick_id) {
31843 this.bricks.splice(i,1);
31844 this.el.dom.removeChild(Roo.get(brick_id).dom);
31851 * adds a Masonry Brick
31852 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31854 addBrick : function(cfg)
31856 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31857 //this.register(cn);
31858 cn.parentId = this.id;
31859 cn.onRender(this.el, null);
31864 * register a Masonry Brick
31865 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31868 register : function(brick)
31870 this.bricks.push(brick);
31871 brick.masonryId = this.id;
31875 * clear all the Masonry Brick
31877 clearAll : function()
31880 //this.getChildContainer().dom.innerHTML = "";
31881 this.el.dom.innerHTML = '';
31884 getSelected : function()
31886 if (!this.selectedBrick) {
31890 return this.selectedBrick;
31894 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31898 * register a Masonry Layout
31899 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31902 register : function(layout)
31904 this.groups[layout.id] = layout;
31907 * fetch a Masonry Layout based on the masonry layout ID
31908 * @param {string} the masonry layout to add
31909 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31912 get: function(layout_id) {
31913 if (typeof(this.groups[layout_id]) == 'undefined') {
31916 return this.groups[layout_id] ;
31928 * http://masonry.desandro.com
31930 * The idea is to render all the bricks based on vertical width...
31932 * The original code extends 'outlayer' - we might need to use that....
31938 * @class Roo.bootstrap.LayoutMasonryAuto
31939 * @extends Roo.bootstrap.Component
31940 * Bootstrap Layout Masonry class
31943 * Create a new Element
31944 * @param {Object} config The config object
31947 Roo.bootstrap.LayoutMasonryAuto = function(config){
31948 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31951 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31954 * @cfg {Boolean} isFitWidth - resize the width..
31956 isFitWidth : false, // options..
31958 * @cfg {Boolean} isOriginLeft = left align?
31960 isOriginLeft : true,
31962 * @cfg {Boolean} isOriginTop = top align?
31964 isOriginTop : false,
31966 * @cfg {Boolean} isLayoutInstant = no animation?
31968 isLayoutInstant : false, // needed?
31970 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31972 isResizingContainer : true,
31974 * @cfg {Number} columnWidth width of the columns
31980 * @cfg {Number} maxCols maximum number of columns
31985 * @cfg {Number} padHeight padding below box..
31991 * @cfg {Boolean} isAutoInitial defalut true
31994 isAutoInitial : true,
32000 initialColumnWidth : 0,
32001 currentSize : null,
32003 colYs : null, // array.
32010 bricks: null, //CompositeElement
32011 cols : 0, // array?
32012 // element : null, // wrapped now this.el
32013 _isLayoutInited : null,
32016 getAutoCreate : function(){
32020 cls: 'blog-masonary-wrapper ' + this.cls,
32022 cls : 'mas-boxes masonary'
32029 getChildContainer: function( )
32031 if (this.boxesEl) {
32032 return this.boxesEl;
32035 this.boxesEl = this.el.select('.mas-boxes').first();
32037 return this.boxesEl;
32041 initEvents : function()
32045 if(this.isAutoInitial){
32046 Roo.log('hook children rendered');
32047 this.on('childrenrendered', function() {
32048 Roo.log('children rendered');
32055 initial : function()
32057 this.reloadItems();
32059 this.currentSize = this.el.getBox(true);
32061 /// was window resize... - let's see if this works..
32062 Roo.EventManager.onWindowResize(this.resize, this);
32064 if(!this.isAutoInitial){
32069 this.layout.defer(500,this);
32072 reloadItems: function()
32074 this.bricks = this.el.select('.masonry-brick', true);
32076 this.bricks.each(function(b) {
32077 //Roo.log(b.getSize());
32078 if (!b.attr('originalwidth')) {
32079 b.attr('originalwidth', b.getSize().width);
32084 Roo.log(this.bricks.elements.length);
32087 resize : function()
32090 var cs = this.el.getBox(true);
32092 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32093 Roo.log("no change in with or X");
32096 this.currentSize = cs;
32100 layout : function()
32103 this._resetLayout();
32104 //this._manageStamps();
32106 // don't animate first layout
32107 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32108 this.layoutItems( isInstant );
32110 // flag for initalized
32111 this._isLayoutInited = true;
32114 layoutItems : function( isInstant )
32116 //var items = this._getItemsForLayout( this.items );
32117 // original code supports filtering layout items.. we just ignore it..
32119 this._layoutItems( this.bricks , isInstant );
32121 this._postLayout();
32123 _layoutItems : function ( items , isInstant)
32125 //this.fireEvent( 'layout', this, items );
32128 if ( !items || !items.elements.length ) {
32129 // no items, emit event with empty array
32134 items.each(function(item) {
32135 Roo.log("layout item");
32137 // get x/y object from method
32138 var position = this._getItemLayoutPosition( item );
32140 position.item = item;
32141 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32142 queue.push( position );
32145 this._processLayoutQueue( queue );
32147 /** Sets position of item in DOM
32148 * @param {Element} item
32149 * @param {Number} x - horizontal position
32150 * @param {Number} y - vertical position
32151 * @param {Boolean} isInstant - disables transitions
32153 _processLayoutQueue : function( queue )
32155 for ( var i=0, len = queue.length; i < len; i++ ) {
32156 var obj = queue[i];
32157 obj.item.position('absolute');
32158 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32164 * Any logic you want to do after each layout,
32165 * i.e. size the container
32167 _postLayout : function()
32169 this.resizeContainer();
32172 resizeContainer : function()
32174 if ( !this.isResizingContainer ) {
32177 var size = this._getContainerSize();
32179 this.el.setSize(size.width,size.height);
32180 this.boxesEl.setSize(size.width,size.height);
32186 _resetLayout : function()
32188 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32189 this.colWidth = this.el.getWidth();
32190 //this.gutter = this.el.getWidth();
32192 this.measureColumns();
32198 this.colYs.push( 0 );
32204 measureColumns : function()
32206 this.getContainerWidth();
32207 // if columnWidth is 0, default to outerWidth of first item
32208 if ( !this.columnWidth ) {
32209 var firstItem = this.bricks.first();
32210 Roo.log(firstItem);
32211 this.columnWidth = this.containerWidth;
32212 if (firstItem && firstItem.attr('originalwidth') ) {
32213 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32215 // columnWidth fall back to item of first element
32216 Roo.log("set column width?");
32217 this.initialColumnWidth = this.columnWidth ;
32219 // if first elem has no width, default to size of container
32224 if (this.initialColumnWidth) {
32225 this.columnWidth = this.initialColumnWidth;
32230 // column width is fixed at the top - however if container width get's smaller we should
32233 // this bit calcs how man columns..
32235 var columnWidth = this.columnWidth += this.gutter;
32237 // calculate columns
32238 var containerWidth = this.containerWidth + this.gutter;
32240 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32241 // fix rounding errors, typically with gutters
32242 var excess = columnWidth - containerWidth % columnWidth;
32245 // if overshoot is less than a pixel, round up, otherwise floor it
32246 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32247 cols = Math[ mathMethod ]( cols );
32248 this.cols = Math.max( cols, 1 );
32249 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32251 // padding positioning..
32252 var totalColWidth = this.cols * this.columnWidth;
32253 var padavail = this.containerWidth - totalColWidth;
32254 // so for 2 columns - we need 3 'pads'
32256 var padNeeded = (1+this.cols) * this.padWidth;
32258 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32260 this.columnWidth += padExtra
32261 //this.padWidth = Math.floor(padavail / ( this.cols));
32263 // adjust colum width so that padding is fixed??
32265 // we have 3 columns ... total = width * 3
32266 // we have X left over... that should be used by
32268 //if (this.expandC) {
32276 getContainerWidth : function()
32278 /* // container is parent if fit width
32279 var container = this.isFitWidth ? this.element.parentNode : this.element;
32280 // check that this.size and size are there
32281 // IE8 triggers resize on body size change, so they might not be
32283 var size = getSize( container ); //FIXME
32284 this.containerWidth = size && size.innerWidth; //FIXME
32287 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32291 _getItemLayoutPosition : function( item ) // what is item?
32293 // we resize the item to our columnWidth..
32295 item.setWidth(this.columnWidth);
32296 item.autoBoxAdjust = false;
32298 var sz = item.getSize();
32300 // how many columns does this brick span
32301 var remainder = this.containerWidth % this.columnWidth;
32303 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32304 // round if off by 1 pixel, otherwise use ceil
32305 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32306 colSpan = Math.min( colSpan, this.cols );
32308 // normally this should be '1' as we dont' currently allow multi width columns..
32310 var colGroup = this._getColGroup( colSpan );
32311 // get the minimum Y value from the columns
32312 var minimumY = Math.min.apply( Math, colGroup );
32313 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32315 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32317 // position the brick
32319 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32320 y: this.currentSize.y + minimumY + this.padHeight
32324 // apply setHeight to necessary columns
32325 var setHeight = minimumY + sz.height + this.padHeight;
32326 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32328 var setSpan = this.cols + 1 - colGroup.length;
32329 for ( var i = 0; i < setSpan; i++ ) {
32330 this.colYs[ shortColIndex + i ] = setHeight ;
32337 * @param {Number} colSpan - number of columns the element spans
32338 * @returns {Array} colGroup
32340 _getColGroup : function( colSpan )
32342 if ( colSpan < 2 ) {
32343 // if brick spans only one column, use all the column Ys
32348 // how many different places could this brick fit horizontally
32349 var groupCount = this.cols + 1 - colSpan;
32350 // for each group potential horizontal position
32351 for ( var i = 0; i < groupCount; i++ ) {
32352 // make an array of colY values for that one group
32353 var groupColYs = this.colYs.slice( i, i + colSpan );
32354 // and get the max value of the array
32355 colGroup[i] = Math.max.apply( Math, groupColYs );
32360 _manageStamp : function( stamp )
32362 var stampSize = stamp.getSize();
32363 var offset = stamp.getBox();
32364 // get the columns that this stamp affects
32365 var firstX = this.isOriginLeft ? offset.x : offset.right;
32366 var lastX = firstX + stampSize.width;
32367 var firstCol = Math.floor( firstX / this.columnWidth );
32368 firstCol = Math.max( 0, firstCol );
32370 var lastCol = Math.floor( lastX / this.columnWidth );
32371 // lastCol should not go over if multiple of columnWidth #425
32372 lastCol -= lastX % this.columnWidth ? 0 : 1;
32373 lastCol = Math.min( this.cols - 1, lastCol );
32375 // set colYs to bottom of the stamp
32376 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32379 for ( var i = firstCol; i <= lastCol; i++ ) {
32380 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32385 _getContainerSize : function()
32387 this.maxY = Math.max.apply( Math, this.colYs );
32392 if ( this.isFitWidth ) {
32393 size.width = this._getContainerFitWidth();
32399 _getContainerFitWidth : function()
32401 var unusedCols = 0;
32402 // count unused columns
32405 if ( this.colYs[i] !== 0 ) {
32410 // fit container to columns that have been used
32411 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32414 needsResizeLayout : function()
32416 var previousWidth = this.containerWidth;
32417 this.getContainerWidth();
32418 return previousWidth !== this.containerWidth;
32433 * @class Roo.bootstrap.MasonryBrick
32434 * @extends Roo.bootstrap.Component
32435 * Bootstrap MasonryBrick class
32438 * Create a new MasonryBrick
32439 * @param {Object} config The config object
32442 Roo.bootstrap.MasonryBrick = function(config){
32444 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32446 Roo.bootstrap.MasonryBrick.register(this);
32452 * When a MasonryBrick is clcik
32453 * @param {Roo.bootstrap.MasonryBrick} this
32454 * @param {Roo.EventObject} e
32460 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32463 * @cfg {String} title
32467 * @cfg {String} html
32471 * @cfg {String} bgimage
32475 * @cfg {String} videourl
32479 * @cfg {String} cls
32483 * @cfg {String} href
32487 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32492 * @cfg {String} placetitle (center|bottom)
32497 * @cfg {Boolean} isFitContainer defalut true
32499 isFitContainer : true,
32502 * @cfg {Boolean} preventDefault defalut false
32504 preventDefault : false,
32507 * @cfg {Boolean} inverse defalut false
32509 maskInverse : false,
32511 getAutoCreate : function()
32513 if(!this.isFitContainer){
32514 return this.getSplitAutoCreate();
32517 var cls = 'masonry-brick masonry-brick-full';
32519 if(this.href.length){
32520 cls += ' masonry-brick-link';
32523 if(this.bgimage.length){
32524 cls += ' masonry-brick-image';
32527 if(this.maskInverse){
32528 cls += ' mask-inverse';
32531 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32532 cls += ' enable-mask';
32536 cls += ' masonry-' + this.size + '-brick';
32539 if(this.placetitle.length){
32541 switch (this.placetitle) {
32543 cls += ' masonry-center-title';
32546 cls += ' masonry-bottom-title';
32553 if(!this.html.length && !this.bgimage.length){
32554 cls += ' masonry-center-title';
32557 if(!this.html.length && this.bgimage.length){
32558 cls += ' masonry-bottom-title';
32563 cls += ' ' + this.cls;
32567 tag: (this.href.length) ? 'a' : 'div',
32572 cls: 'masonry-brick-mask'
32576 cls: 'masonry-brick-paragraph',
32582 if(this.href.length){
32583 cfg.href = this.href;
32586 var cn = cfg.cn[1].cn;
32588 if(this.title.length){
32591 cls: 'masonry-brick-title',
32596 if(this.html.length){
32599 cls: 'masonry-brick-text',
32604 if (!this.title.length && !this.html.length) {
32605 cfg.cn[1].cls += ' hide';
32608 if(this.bgimage.length){
32611 cls: 'masonry-brick-image-view',
32616 if(this.videourl.length){
32617 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32618 // youtube support only?
32621 cls: 'masonry-brick-image-view',
32624 allowfullscreen : true
32632 getSplitAutoCreate : function()
32634 var cls = 'masonry-brick masonry-brick-split';
32636 if(this.href.length){
32637 cls += ' masonry-brick-link';
32640 if(this.bgimage.length){
32641 cls += ' masonry-brick-image';
32645 cls += ' masonry-' + this.size + '-brick';
32648 switch (this.placetitle) {
32650 cls += ' masonry-center-title';
32653 cls += ' masonry-bottom-title';
32656 if(!this.bgimage.length){
32657 cls += ' masonry-center-title';
32660 if(this.bgimage.length){
32661 cls += ' masonry-bottom-title';
32667 cls += ' ' + this.cls;
32671 tag: (this.href.length) ? 'a' : 'div',
32676 cls: 'masonry-brick-split-head',
32680 cls: 'masonry-brick-paragraph',
32687 cls: 'masonry-brick-split-body',
32693 if(this.href.length){
32694 cfg.href = this.href;
32697 if(this.title.length){
32698 cfg.cn[0].cn[0].cn.push({
32700 cls: 'masonry-brick-title',
32705 if(this.html.length){
32706 cfg.cn[1].cn.push({
32708 cls: 'masonry-brick-text',
32713 if(this.bgimage.length){
32714 cfg.cn[0].cn.push({
32716 cls: 'masonry-brick-image-view',
32721 if(this.videourl.length){
32722 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32723 // youtube support only?
32724 cfg.cn[0].cn.cn.push({
32726 cls: 'masonry-brick-image-view',
32729 allowfullscreen : true
32736 initEvents: function()
32738 switch (this.size) {
32771 this.el.on('touchstart', this.onTouchStart, this);
32772 this.el.on('touchmove', this.onTouchMove, this);
32773 this.el.on('touchend', this.onTouchEnd, this);
32774 this.el.on('contextmenu', this.onContextMenu, this);
32776 this.el.on('mouseenter' ,this.enter, this);
32777 this.el.on('mouseleave', this.leave, this);
32778 this.el.on('click', this.onClick, this);
32781 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32782 this.parent().bricks.push(this);
32787 onClick: function(e, el)
32789 var time = this.endTimer - this.startTimer;
32790 // Roo.log(e.preventDefault());
32793 e.preventDefault();
32798 if(!this.preventDefault){
32802 e.preventDefault();
32804 if (this.activeClass != '') {
32805 this.selectBrick();
32808 this.fireEvent('click', this, e);
32811 enter: function(e, el)
32813 e.preventDefault();
32815 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32819 if(this.bgimage.length && this.html.length){
32820 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32824 leave: function(e, el)
32826 e.preventDefault();
32828 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32832 if(this.bgimage.length && this.html.length){
32833 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32837 onTouchStart: function(e, el)
32839 // e.preventDefault();
32841 this.touchmoved = false;
32843 if(!this.isFitContainer){
32847 if(!this.bgimage.length || !this.html.length){
32851 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32853 this.timer = new Date().getTime();
32857 onTouchMove: function(e, el)
32859 this.touchmoved = true;
32862 onContextMenu : function(e,el)
32864 e.preventDefault();
32865 e.stopPropagation();
32869 onTouchEnd: function(e, el)
32871 // e.preventDefault();
32873 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32880 if(!this.bgimage.length || !this.html.length){
32882 if(this.href.length){
32883 window.location.href = this.href;
32889 if(!this.isFitContainer){
32893 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32895 window.location.href = this.href;
32898 //selection on single brick only
32899 selectBrick : function() {
32901 if (!this.parentId) {
32905 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32906 var index = m.selectedBrick.indexOf(this.id);
32909 m.selectedBrick.splice(index,1);
32910 this.el.removeClass(this.activeClass);
32914 for(var i = 0; i < m.selectedBrick.length; i++) {
32915 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32916 b.el.removeClass(b.activeClass);
32919 m.selectedBrick = [];
32921 m.selectedBrick.push(this.id);
32922 this.el.addClass(this.activeClass);
32926 isSelected : function(){
32927 return this.el.hasClass(this.activeClass);
32932 Roo.apply(Roo.bootstrap.MasonryBrick, {
32935 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32937 * register a Masonry Brick
32938 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32941 register : function(brick)
32943 //this.groups[brick.id] = brick;
32944 this.groups.add(brick.id, brick);
32947 * fetch a masonry brick based on the masonry brick ID
32948 * @param {string} the masonry brick to add
32949 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32952 get: function(brick_id)
32954 // if (typeof(this.groups[brick_id]) == 'undefined') {
32957 // return this.groups[brick_id] ;
32959 if(this.groups.key(brick_id)) {
32960 return this.groups.key(brick_id);
32978 * @class Roo.bootstrap.Brick
32979 * @extends Roo.bootstrap.Component
32980 * Bootstrap Brick class
32983 * Create a new Brick
32984 * @param {Object} config The config object
32987 Roo.bootstrap.Brick = function(config){
32988 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32994 * When a Brick is click
32995 * @param {Roo.bootstrap.Brick} this
32996 * @param {Roo.EventObject} e
33002 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33005 * @cfg {String} title
33009 * @cfg {String} html
33013 * @cfg {String} bgimage
33017 * @cfg {String} cls
33021 * @cfg {String} href
33025 * @cfg {String} video
33029 * @cfg {Boolean} square
33033 getAutoCreate : function()
33035 var cls = 'roo-brick';
33037 if(this.href.length){
33038 cls += ' roo-brick-link';
33041 if(this.bgimage.length){
33042 cls += ' roo-brick-image';
33045 if(!this.html.length && !this.bgimage.length){
33046 cls += ' roo-brick-center-title';
33049 if(!this.html.length && this.bgimage.length){
33050 cls += ' roo-brick-bottom-title';
33054 cls += ' ' + this.cls;
33058 tag: (this.href.length) ? 'a' : 'div',
33063 cls: 'roo-brick-paragraph',
33069 if(this.href.length){
33070 cfg.href = this.href;
33073 var cn = cfg.cn[0].cn;
33075 if(this.title.length){
33078 cls: 'roo-brick-title',
33083 if(this.html.length){
33086 cls: 'roo-brick-text',
33093 if(this.bgimage.length){
33096 cls: 'roo-brick-image-view',
33104 initEvents: function()
33106 if(this.title.length || this.html.length){
33107 this.el.on('mouseenter' ,this.enter, this);
33108 this.el.on('mouseleave', this.leave, this);
33111 Roo.EventManager.onWindowResize(this.resize, this);
33113 if(this.bgimage.length){
33114 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33115 this.imageEl.on('load', this.onImageLoad, this);
33122 onImageLoad : function()
33127 resize : function()
33129 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33131 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33133 if(this.bgimage.length){
33134 var image = this.el.select('.roo-brick-image-view', true).first();
33136 image.setWidth(paragraph.getWidth());
33139 image.setHeight(paragraph.getWidth());
33142 this.el.setHeight(image.getHeight());
33143 paragraph.setHeight(image.getHeight());
33149 enter: function(e, el)
33151 e.preventDefault();
33153 if(this.bgimage.length){
33154 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33155 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33159 leave: function(e, el)
33161 e.preventDefault();
33163 if(this.bgimage.length){
33164 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33165 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33180 * @class Roo.bootstrap.NumberField
33181 * @extends Roo.bootstrap.Input
33182 * Bootstrap NumberField class
33188 * Create a new NumberField
33189 * @param {Object} config The config object
33192 Roo.bootstrap.NumberField = function(config){
33193 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33196 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33199 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33201 allowDecimals : true,
33203 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33205 decimalSeparator : ".",
33207 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33209 decimalPrecision : 2,
33211 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33213 allowNegative : true,
33216 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33220 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33222 minValue : Number.NEGATIVE_INFINITY,
33224 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33226 maxValue : Number.MAX_VALUE,
33228 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33230 minText : "The minimum value for this field is {0}",
33232 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33234 maxText : "The maximum value for this field is {0}",
33236 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33237 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33239 nanText : "{0} is not a valid number",
33241 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33245 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33247 thousandsDelimiter : false,
33249 * @cfg {String} valueAlign alignment of value
33251 valueAlign : "left",
33253 getAutoCreate : function()
33255 var hiddenInput = {
33259 cls: 'hidden-number-input'
33263 hiddenInput.name = this.name;
33268 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33270 this.name = hiddenInput.name;
33272 if(cfg.cn.length > 0) {
33273 cfg.cn.push(hiddenInput);
33280 initEvents : function()
33282 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33284 var allowed = "0123456789";
33286 if(this.allowDecimals){
33287 allowed += this.decimalSeparator;
33290 if(this.allowNegative){
33294 if(this.thousandsDelimiter) {
33298 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33300 var keyPress = function(e){
33302 var k = e.getKey();
33304 var c = e.getCharCode();
33307 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33308 allowed.indexOf(String.fromCharCode(c)) === -1
33314 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33318 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33323 this.el.on("keypress", keyPress, this);
33326 validateValue : function(value)
33329 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33333 var num = this.parseValue(value);
33336 this.markInvalid(String.format(this.nanText, value));
33340 if(num < this.minValue){
33341 this.markInvalid(String.format(this.minText, this.minValue));
33345 if(num > this.maxValue){
33346 this.markInvalid(String.format(this.maxText, this.maxValue));
33353 getValue : function()
33355 var v = this.hiddenEl().getValue();
33357 return this.fixPrecision(this.parseValue(v));
33360 parseValue : function(value)
33362 if(this.thousandsDelimiter) {
33364 r = new RegExp(",", "g");
33365 value = value.replace(r, "");
33368 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33369 return isNaN(value) ? '' : value;
33372 fixPrecision : function(value)
33374 if(this.thousandsDelimiter) {
33376 r = new RegExp(",", "g");
33377 value = value.replace(r, "");
33380 var nan = isNaN(value);
33382 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33383 return nan ? '' : value;
33385 return parseFloat(value).toFixed(this.decimalPrecision);
33388 setValue : function(v)
33390 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33396 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33398 this.inputEl().dom.value = (v == '') ? '' :
33399 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33401 if(!this.allowZero && v === '0') {
33402 this.hiddenEl().dom.value = '';
33403 this.inputEl().dom.value = '';
33410 decimalPrecisionFcn : function(v)
33412 return Math.floor(v);
33415 beforeBlur : function()
33421 var v = this.parseValue(this.getRawValue());
33423 Roo.log("Number Field Before Blur");
33431 hiddenEl : function()
33433 return this.el.select('input.hidden-number-input',true).first();
33445 * @class Roo.bootstrap.DocumentSlider
33446 * @extends Roo.bootstrap.Component
33447 * Bootstrap DocumentSlider class
33450 * Create a new DocumentViewer
33451 * @param {Object} config The config object
33454 Roo.bootstrap.DocumentSlider = function(config){
33455 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33462 * Fire after initEvent
33463 * @param {Roo.bootstrap.DocumentSlider} this
33468 * Fire after update
33469 * @param {Roo.bootstrap.DocumentSlider} this
33475 * @param {Roo.bootstrap.DocumentSlider} this
33481 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33487 getAutoCreate : function()
33491 cls : 'roo-document-slider',
33495 cls : 'roo-document-slider-header',
33499 cls : 'roo-document-slider-header-title'
33505 cls : 'roo-document-slider-body',
33509 cls : 'roo-document-slider-prev',
33513 cls : 'fa fa-chevron-left'
33519 cls : 'roo-document-slider-thumb',
33523 cls : 'roo-document-slider-image'
33529 cls : 'roo-document-slider-next',
33533 cls : 'fa fa-chevron-right'
33545 initEvents : function()
33547 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33548 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33550 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33551 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33553 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33554 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33556 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33557 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33559 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33560 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33562 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33563 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33565 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33566 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33568 this.thumbEl.on('click', this.onClick, this);
33570 this.prevIndicator.on('click', this.prev, this);
33572 this.nextIndicator.on('click', this.next, this);
33576 initial : function()
33578 if(this.files.length){
33579 this.indicator = 1;
33583 this.fireEvent('initial', this);
33586 update : function()
33588 this.imageEl.attr('src', this.files[this.indicator - 1]);
33590 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33592 this.prevIndicator.show();
33594 if(this.indicator == 1){
33595 this.prevIndicator.hide();
33598 this.nextIndicator.show();
33600 if(this.indicator == this.files.length){
33601 this.nextIndicator.hide();
33604 this.thumbEl.scrollTo('top');
33606 this.fireEvent('update', this);
33609 onClick : function(e)
33611 e.preventDefault();
33613 this.fireEvent('click', this);
33618 e.preventDefault();
33620 this.indicator = Math.max(1, this.indicator - 1);
33627 e.preventDefault();
33629 this.indicator = Math.min(this.files.length, this.indicator + 1);
33643 * @class Roo.bootstrap.RadioSet
33644 * @extends Roo.bootstrap.Input
33645 * Bootstrap RadioSet class
33646 * @cfg {String} indicatorpos (left|right) default left
33647 * @cfg {Boolean} inline (true|false) inline the element (default true)
33648 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33650 * Create a new RadioSet
33651 * @param {Object} config The config object
33654 Roo.bootstrap.RadioSet = function(config){
33656 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33660 Roo.bootstrap.RadioSet.register(this);
33665 * Fires when the element is checked or unchecked.
33666 * @param {Roo.bootstrap.RadioSet} this This radio
33667 * @param {Roo.bootstrap.Radio} item The checked item
33672 * Fires when the element is click.
33673 * @param {Roo.bootstrap.RadioSet} this This radio set
33674 * @param {Roo.bootstrap.Radio} item The checked item
33675 * @param {Roo.EventObject} e The event object
33682 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33690 indicatorpos : 'left',
33692 getAutoCreate : function()
33696 cls : 'roo-radio-set-label',
33700 html : this.fieldLabel
33705 if(this.indicatorpos == 'left'){
33708 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33709 tooltip : 'This field is required'
33714 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33715 tooltip : 'This field is required'
33721 cls : 'roo-radio-set-items'
33724 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33726 if (align === 'left' && this.fieldLabel.length) {
33729 cls : "roo-radio-set-right",
33735 if(this.labelWidth > 12){
33736 label.style = "width: " + this.labelWidth + 'px';
33739 if(this.labelWidth < 13 && this.labelmd == 0){
33740 this.labelmd = this.labelWidth;
33743 if(this.labellg > 0){
33744 label.cls += ' col-lg-' + this.labellg;
33745 items.cls += ' col-lg-' + (12 - this.labellg);
33748 if(this.labelmd > 0){
33749 label.cls += ' col-md-' + this.labelmd;
33750 items.cls += ' col-md-' + (12 - this.labelmd);
33753 if(this.labelsm > 0){
33754 label.cls += ' col-sm-' + this.labelsm;
33755 items.cls += ' col-sm-' + (12 - this.labelsm);
33758 if(this.labelxs > 0){
33759 label.cls += ' col-xs-' + this.labelxs;
33760 items.cls += ' col-xs-' + (12 - this.labelxs);
33766 cls : 'roo-radio-set',
33770 cls : 'roo-radio-set-input',
33773 value : this.value ? this.value : ''
33780 if(this.weight.length){
33781 cfg.cls += ' roo-radio-' + this.weight;
33785 cfg.cls += ' roo-radio-set-inline';
33789 ['xs','sm','md','lg'].map(function(size){
33790 if (settings[size]) {
33791 cfg.cls += ' col-' + size + '-' + settings[size];
33799 initEvents : function()
33801 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33802 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33804 if(!this.fieldLabel.length){
33805 this.labelEl.hide();
33808 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33809 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33811 this.indicator = this.indicatorEl();
33813 if(this.indicator){
33814 this.indicator.addClass('invisible');
33817 this.originalValue = this.getValue();
33821 inputEl: function ()
33823 return this.el.select('.roo-radio-set-input', true).first();
33826 getChildContainer : function()
33828 return this.itemsEl;
33831 register : function(item)
33833 this.radioes.push(item);
33837 validate : function()
33839 if(this.getVisibilityEl().hasClass('hidden')){
33845 Roo.each(this.radioes, function(i){
33854 if(this.allowBlank) {
33858 if(this.disabled || valid){
33863 this.markInvalid();
33868 markValid : function()
33870 if(this.labelEl.isVisible(true)){
33871 this.indicatorEl().removeClass('visible');
33872 this.indicatorEl().addClass('invisible');
33875 this.el.removeClass([this.invalidClass, this.validClass]);
33876 this.el.addClass(this.validClass);
33878 this.fireEvent('valid', this);
33881 markInvalid : function(msg)
33883 if(this.allowBlank || this.disabled){
33887 if(this.labelEl.isVisible(true)){
33888 this.indicatorEl().removeClass('invisible');
33889 this.indicatorEl().addClass('visible');
33892 this.el.removeClass([this.invalidClass, this.validClass]);
33893 this.el.addClass(this.invalidClass);
33895 this.fireEvent('invalid', this, msg);
33899 setValue : function(v, suppressEvent)
33901 if(this.value === v){
33908 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33911 Roo.each(this.radioes, function(i){
33913 i.el.removeClass('checked');
33916 Roo.each(this.radioes, function(i){
33918 if(i.value === v || i.value.toString() === v.toString()){
33920 i.el.addClass('checked');
33922 if(suppressEvent !== true){
33923 this.fireEvent('check', this, i);
33934 clearInvalid : function(){
33936 if(!this.el || this.preventMark){
33940 this.el.removeClass([this.invalidClass]);
33942 this.fireEvent('valid', this);
33947 Roo.apply(Roo.bootstrap.RadioSet, {
33951 register : function(set)
33953 this.groups[set.name] = set;
33956 get: function(name)
33958 if (typeof(this.groups[name]) == 'undefined') {
33962 return this.groups[name] ;
33968 * Ext JS Library 1.1.1
33969 * Copyright(c) 2006-2007, Ext JS, LLC.
33971 * Originally Released Under LGPL - original licence link has changed is not relivant.
33974 * <script type="text/javascript">
33979 * @class Roo.bootstrap.SplitBar
33980 * @extends Roo.util.Observable
33981 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33985 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33986 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33987 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33988 split.minSize = 100;
33989 split.maxSize = 600;
33990 split.animate = true;
33991 split.on('moved', splitterMoved);
33994 * Create a new SplitBar
33995 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33996 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33997 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33998 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33999 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34000 position of the SplitBar).
34002 Roo.bootstrap.SplitBar = function(cfg){
34007 // dragElement : elm
34008 // resizingElement: el,
34010 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34011 // placement : Roo.bootstrap.SplitBar.LEFT ,
34012 // existingProxy ???
34015 this.el = Roo.get(cfg.dragElement, true);
34016 this.el.dom.unselectable = "on";
34018 this.resizingEl = Roo.get(cfg.resizingElement, true);
34022 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34023 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34026 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34029 * The minimum size of the resizing element. (Defaults to 0)
34035 * The maximum size of the resizing element. (Defaults to 2000)
34038 this.maxSize = 2000;
34041 * Whether to animate the transition to the new size
34044 this.animate = false;
34047 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34050 this.useShim = false;
34055 if(!cfg.existingProxy){
34057 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34059 this.proxy = Roo.get(cfg.existingProxy).dom;
34062 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34065 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34068 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34071 this.dragSpecs = {};
34074 * @private The adapter to use to positon and resize elements
34076 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34077 this.adapter.init(this);
34079 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34081 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34082 this.el.addClass("roo-splitbar-h");
34085 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34086 this.el.addClass("roo-splitbar-v");
34092 * Fires when the splitter is moved (alias for {@link #event-moved})
34093 * @param {Roo.bootstrap.SplitBar} this
34094 * @param {Number} newSize the new width or height
34099 * Fires when the splitter is moved
34100 * @param {Roo.bootstrap.SplitBar} this
34101 * @param {Number} newSize the new width or height
34105 * @event beforeresize
34106 * Fires before the splitter is dragged
34107 * @param {Roo.bootstrap.SplitBar} this
34109 "beforeresize" : true,
34111 "beforeapply" : true
34114 Roo.util.Observable.call(this);
34117 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34118 onStartProxyDrag : function(x, y){
34119 this.fireEvent("beforeresize", this);
34121 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34123 o.enableDisplayMode("block");
34124 // all splitbars share the same overlay
34125 Roo.bootstrap.SplitBar.prototype.overlay = o;
34127 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34128 this.overlay.show();
34129 Roo.get(this.proxy).setDisplayed("block");
34130 var size = this.adapter.getElementSize(this);
34131 this.activeMinSize = this.getMinimumSize();;
34132 this.activeMaxSize = this.getMaximumSize();;
34133 var c1 = size - this.activeMinSize;
34134 var c2 = Math.max(this.activeMaxSize - size, 0);
34135 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34136 this.dd.resetConstraints();
34137 this.dd.setXConstraint(
34138 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34139 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34141 this.dd.setYConstraint(0, 0);
34143 this.dd.resetConstraints();
34144 this.dd.setXConstraint(0, 0);
34145 this.dd.setYConstraint(
34146 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34147 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34150 this.dragSpecs.startSize = size;
34151 this.dragSpecs.startPoint = [x, y];
34152 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34156 * @private Called after the drag operation by the DDProxy
34158 onEndProxyDrag : function(e){
34159 Roo.get(this.proxy).setDisplayed(false);
34160 var endPoint = Roo.lib.Event.getXY(e);
34162 this.overlay.hide();
34165 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34166 newSize = this.dragSpecs.startSize +
34167 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34168 endPoint[0] - this.dragSpecs.startPoint[0] :
34169 this.dragSpecs.startPoint[0] - endPoint[0]
34172 newSize = this.dragSpecs.startSize +
34173 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34174 endPoint[1] - this.dragSpecs.startPoint[1] :
34175 this.dragSpecs.startPoint[1] - endPoint[1]
34178 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34179 if(newSize != this.dragSpecs.startSize){
34180 if(this.fireEvent('beforeapply', this, newSize) !== false){
34181 this.adapter.setElementSize(this, newSize);
34182 this.fireEvent("moved", this, newSize);
34183 this.fireEvent("resize", this, newSize);
34189 * Get the adapter this SplitBar uses
34190 * @return The adapter object
34192 getAdapter : function(){
34193 return this.adapter;
34197 * Set the adapter this SplitBar uses
34198 * @param {Object} adapter A SplitBar adapter object
34200 setAdapter : function(adapter){
34201 this.adapter = adapter;
34202 this.adapter.init(this);
34206 * Gets the minimum size for the resizing element
34207 * @return {Number} The minimum size
34209 getMinimumSize : function(){
34210 return this.minSize;
34214 * Sets the minimum size for the resizing element
34215 * @param {Number} minSize The minimum size
34217 setMinimumSize : function(minSize){
34218 this.minSize = minSize;
34222 * Gets the maximum size for the resizing element
34223 * @return {Number} The maximum size
34225 getMaximumSize : function(){
34226 return this.maxSize;
34230 * Sets the maximum size for the resizing element
34231 * @param {Number} maxSize The maximum size
34233 setMaximumSize : function(maxSize){
34234 this.maxSize = maxSize;
34238 * Sets the initialize size for the resizing element
34239 * @param {Number} size The initial size
34241 setCurrentSize : function(size){
34242 var oldAnimate = this.animate;
34243 this.animate = false;
34244 this.adapter.setElementSize(this, size);
34245 this.animate = oldAnimate;
34249 * Destroy this splitbar.
34250 * @param {Boolean} removeEl True to remove the element
34252 destroy : function(removeEl){
34254 this.shim.remove();
34257 this.proxy.parentNode.removeChild(this.proxy);
34265 * @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.
34267 Roo.bootstrap.SplitBar.createProxy = function(dir){
34268 var proxy = new Roo.Element(document.createElement("div"));
34269 proxy.unselectable();
34270 var cls = 'roo-splitbar-proxy';
34271 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34272 document.body.appendChild(proxy.dom);
34277 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34278 * Default Adapter. It assumes the splitter and resizing element are not positioned
34279 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34281 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34284 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34285 // do nothing for now
34286 init : function(s){
34290 * Called before drag operations to get the current size of the resizing element.
34291 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34293 getElementSize : function(s){
34294 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34295 return s.resizingEl.getWidth();
34297 return s.resizingEl.getHeight();
34302 * Called after drag operations to set the size of the resizing element.
34303 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34304 * @param {Number} newSize The new size to set
34305 * @param {Function} onComplete A function to be invoked when resizing is complete
34307 setElementSize : function(s, newSize, onComplete){
34308 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34310 s.resizingEl.setWidth(newSize);
34312 onComplete(s, newSize);
34315 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34320 s.resizingEl.setHeight(newSize);
34322 onComplete(s, newSize);
34325 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34332 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34333 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34334 * Adapter that moves the splitter element to align with the resized sizing element.
34335 * Used with an absolute positioned SplitBar.
34336 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34337 * document.body, make sure you assign an id to the body element.
34339 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34340 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34341 this.container = Roo.get(container);
34344 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34345 init : function(s){
34346 this.basic.init(s);
34349 getElementSize : function(s){
34350 return this.basic.getElementSize(s);
34353 setElementSize : function(s, newSize, onComplete){
34354 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34357 moveSplitter : function(s){
34358 var yes = Roo.bootstrap.SplitBar;
34359 switch(s.placement){
34361 s.el.setX(s.resizingEl.getRight());
34364 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34367 s.el.setY(s.resizingEl.getBottom());
34370 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34377 * Orientation constant - Create a vertical SplitBar
34381 Roo.bootstrap.SplitBar.VERTICAL = 1;
34384 * Orientation constant - Create a horizontal SplitBar
34388 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34391 * Placement constant - The resizing element is to the left of the splitter element
34395 Roo.bootstrap.SplitBar.LEFT = 1;
34398 * Placement constant - The resizing element is to the right of the splitter element
34402 Roo.bootstrap.SplitBar.RIGHT = 2;
34405 * Placement constant - The resizing element is positioned above the splitter element
34409 Roo.bootstrap.SplitBar.TOP = 3;
34412 * Placement constant - The resizing element is positioned under splitter element
34416 Roo.bootstrap.SplitBar.BOTTOM = 4;
34417 Roo.namespace("Roo.bootstrap.layout");/*
34419 * Ext JS Library 1.1.1
34420 * Copyright(c) 2006-2007, Ext JS, LLC.
34422 * Originally Released Under LGPL - original licence link has changed is not relivant.
34425 * <script type="text/javascript">
34429 * @class Roo.bootstrap.layout.Manager
34430 * @extends Roo.bootstrap.Component
34431 * Base class for layout managers.
34433 Roo.bootstrap.layout.Manager = function(config)
34435 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34441 /** false to disable window resize monitoring @type Boolean */
34442 this.monitorWindowResize = true;
34447 * Fires when a layout is performed.
34448 * @param {Roo.LayoutManager} this
34452 * @event regionresized
34453 * Fires when the user resizes a region.
34454 * @param {Roo.LayoutRegion} region The resized region
34455 * @param {Number} newSize The new size (width for east/west, height for north/south)
34457 "regionresized" : true,
34459 * @event regioncollapsed
34460 * Fires when a region is collapsed.
34461 * @param {Roo.LayoutRegion} region The collapsed region
34463 "regioncollapsed" : true,
34465 * @event regionexpanded
34466 * Fires when a region is expanded.
34467 * @param {Roo.LayoutRegion} region The expanded region
34469 "regionexpanded" : true
34471 this.updating = false;
34474 this.el = Roo.get(config.el);
34480 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34485 monitorWindowResize : true,
34491 onRender : function(ct, position)
34494 this.el = Roo.get(ct);
34497 //this.fireEvent('render',this);
34501 initEvents: function()
34505 // ie scrollbar fix
34506 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34507 document.body.scroll = "no";
34508 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34509 this.el.position('relative');
34511 this.id = this.el.id;
34512 this.el.addClass("roo-layout-container");
34513 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34514 if(this.el.dom != document.body ) {
34515 this.el.on('resize', this.layout,this);
34516 this.el.on('show', this.layout,this);
34522 * Returns true if this layout is currently being updated
34523 * @return {Boolean}
34525 isUpdating : function(){
34526 return this.updating;
34530 * Suspend the LayoutManager from doing auto-layouts while
34531 * making multiple add or remove calls
34533 beginUpdate : function(){
34534 this.updating = true;
34538 * Restore auto-layouts and optionally disable the manager from performing a layout
34539 * @param {Boolean} noLayout true to disable a layout update
34541 endUpdate : function(noLayout){
34542 this.updating = false;
34548 layout: function(){
34552 onRegionResized : function(region, newSize){
34553 this.fireEvent("regionresized", region, newSize);
34557 onRegionCollapsed : function(region){
34558 this.fireEvent("regioncollapsed", region);
34561 onRegionExpanded : function(region){
34562 this.fireEvent("regionexpanded", region);
34566 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34567 * performs box-model adjustments.
34568 * @return {Object} The size as an object {width: (the width), height: (the height)}
34570 getViewSize : function()
34573 if(this.el.dom != document.body){
34574 size = this.el.getSize();
34576 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34578 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34579 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34584 * Returns the Element this layout is bound to.
34585 * @return {Roo.Element}
34587 getEl : function(){
34592 * Returns the specified region.
34593 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34594 * @return {Roo.LayoutRegion}
34596 getRegion : function(target){
34597 return this.regions[target.toLowerCase()];
34600 onWindowResize : function(){
34601 if(this.monitorWindowResize){
34608 * Ext JS Library 1.1.1
34609 * Copyright(c) 2006-2007, Ext JS, LLC.
34611 * Originally Released Under LGPL - original licence link has changed is not relivant.
34614 * <script type="text/javascript">
34617 * @class Roo.bootstrap.layout.Border
34618 * @extends Roo.bootstrap.layout.Manager
34619 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34620 * please see: examples/bootstrap/nested.html<br><br>
34622 <b>The container the layout is rendered into can be either the body element or any other element.
34623 If it is not the body element, the container needs to either be an absolute positioned element,
34624 or you will need to add "position:relative" to the css of the container. You will also need to specify
34625 the container size if it is not the body element.</b>
34628 * Create a new Border
34629 * @param {Object} config Configuration options
34631 Roo.bootstrap.layout.Border = function(config){
34632 config = config || {};
34633 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34637 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34638 if(config[region]){
34639 config[region].region = region;
34640 this.addRegion(config[region]);
34646 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34648 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34650 * Creates and adds a new region if it doesn't already exist.
34651 * @param {String} target The target region key (north, south, east, west or center).
34652 * @param {Object} config The regions config object
34653 * @return {BorderLayoutRegion} The new region
34655 addRegion : function(config)
34657 if(!this.regions[config.region]){
34658 var r = this.factory(config);
34659 this.bindRegion(r);
34661 return this.regions[config.region];
34665 bindRegion : function(r){
34666 this.regions[r.config.region] = r;
34668 r.on("visibilitychange", this.layout, this);
34669 r.on("paneladded", this.layout, this);
34670 r.on("panelremoved", this.layout, this);
34671 r.on("invalidated", this.layout, this);
34672 r.on("resized", this.onRegionResized, this);
34673 r.on("collapsed", this.onRegionCollapsed, this);
34674 r.on("expanded", this.onRegionExpanded, this);
34678 * Performs a layout update.
34680 layout : function()
34682 if(this.updating) {
34686 // render all the rebions if they have not been done alreayd?
34687 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34688 if(this.regions[region] && !this.regions[region].bodyEl){
34689 this.regions[region].onRender(this.el)
34693 var size = this.getViewSize();
34694 var w = size.width;
34695 var h = size.height;
34700 //var x = 0, y = 0;
34702 var rs = this.regions;
34703 var north = rs["north"];
34704 var south = rs["south"];
34705 var west = rs["west"];
34706 var east = rs["east"];
34707 var center = rs["center"];
34708 //if(this.hideOnLayout){ // not supported anymore
34709 //c.el.setStyle("display", "none");
34711 if(north && north.isVisible()){
34712 var b = north.getBox();
34713 var m = north.getMargins();
34714 b.width = w - (m.left+m.right);
34717 centerY = b.height + b.y + m.bottom;
34718 centerH -= centerY;
34719 north.updateBox(this.safeBox(b));
34721 if(south && south.isVisible()){
34722 var b = south.getBox();
34723 var m = south.getMargins();
34724 b.width = w - (m.left+m.right);
34726 var totalHeight = (b.height + m.top + m.bottom);
34727 b.y = h - totalHeight + m.top;
34728 centerH -= totalHeight;
34729 south.updateBox(this.safeBox(b));
34731 if(west && west.isVisible()){
34732 var b = west.getBox();
34733 var m = west.getMargins();
34734 b.height = centerH - (m.top+m.bottom);
34736 b.y = centerY + m.top;
34737 var totalWidth = (b.width + m.left + m.right);
34738 centerX += totalWidth;
34739 centerW -= totalWidth;
34740 west.updateBox(this.safeBox(b));
34742 if(east && east.isVisible()){
34743 var b = east.getBox();
34744 var m = east.getMargins();
34745 b.height = centerH - (m.top+m.bottom);
34746 var totalWidth = (b.width + m.left + m.right);
34747 b.x = w - totalWidth + m.left;
34748 b.y = centerY + m.top;
34749 centerW -= totalWidth;
34750 east.updateBox(this.safeBox(b));
34753 var m = center.getMargins();
34755 x: centerX + m.left,
34756 y: centerY + m.top,
34757 width: centerW - (m.left+m.right),
34758 height: centerH - (m.top+m.bottom)
34760 //if(this.hideOnLayout){
34761 //center.el.setStyle("display", "block");
34763 center.updateBox(this.safeBox(centerBox));
34766 this.fireEvent("layout", this);
34770 safeBox : function(box){
34771 box.width = Math.max(0, box.width);
34772 box.height = Math.max(0, box.height);
34777 * Adds a ContentPanel (or subclass) to this layout.
34778 * @param {String} target The target region key (north, south, east, west or center).
34779 * @param {Roo.ContentPanel} panel The panel to add
34780 * @return {Roo.ContentPanel} The added panel
34782 add : function(target, panel){
34784 target = target.toLowerCase();
34785 return this.regions[target].add(panel);
34789 * Remove a ContentPanel (or subclass) to this layout.
34790 * @param {String} target The target region key (north, south, east, west or center).
34791 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34792 * @return {Roo.ContentPanel} The removed panel
34794 remove : function(target, panel){
34795 target = target.toLowerCase();
34796 return this.regions[target].remove(panel);
34800 * Searches all regions for a panel with the specified id
34801 * @param {String} panelId
34802 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34804 findPanel : function(panelId){
34805 var rs = this.regions;
34806 for(var target in rs){
34807 if(typeof rs[target] != "function"){
34808 var p = rs[target].getPanel(panelId);
34818 * Searches all regions for a panel with the specified id and activates (shows) it.
34819 * @param {String/ContentPanel} panelId The panels id or the panel itself
34820 * @return {Roo.ContentPanel} The shown panel or null
34822 showPanel : function(panelId) {
34823 var rs = this.regions;
34824 for(var target in rs){
34825 var r = rs[target];
34826 if(typeof r != "function"){
34827 if(r.hasPanel(panelId)){
34828 return r.showPanel(panelId);
34836 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34837 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34840 restoreState : function(provider){
34842 provider = Roo.state.Manager;
34844 var sm = new Roo.LayoutStateManager();
34845 sm.init(this, provider);
34851 * Adds a xtype elements to the layout.
34855 xtype : 'ContentPanel',
34862 xtype : 'NestedLayoutPanel',
34868 items : [ ... list of content panels or nested layout panels.. ]
34872 * @param {Object} cfg Xtype definition of item to add.
34874 addxtype : function(cfg)
34876 // basically accepts a pannel...
34877 // can accept a layout region..!?!?
34878 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34881 // theory? children can only be panels??
34883 //if (!cfg.xtype.match(/Panel$/)) {
34888 if (typeof(cfg.region) == 'undefined') {
34889 Roo.log("Failed to add Panel, region was not set");
34893 var region = cfg.region;
34899 xitems = cfg.items;
34906 case 'Content': // ContentPanel (el, cfg)
34907 case 'Scroll': // ContentPanel (el, cfg)
34909 cfg.autoCreate = true;
34910 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34912 // var el = this.el.createChild();
34913 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34916 this.add(region, ret);
34920 case 'TreePanel': // our new panel!
34921 cfg.el = this.el.createChild();
34922 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34923 this.add(region, ret);
34928 // create a new Layout (which is a Border Layout...
34930 var clayout = cfg.layout;
34931 clayout.el = this.el.createChild();
34932 clayout.items = clayout.items || [];
34936 // replace this exitems with the clayout ones..
34937 xitems = clayout.items;
34939 // force background off if it's in center...
34940 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34941 cfg.background = false;
34943 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34946 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34947 //console.log('adding nested layout panel ' + cfg.toSource());
34948 this.add(region, ret);
34949 nb = {}; /// find first...
34954 // needs grid and region
34956 //var el = this.getRegion(region).el.createChild();
34958 *var el = this.el.createChild();
34959 // create the grid first...
34960 cfg.grid.container = el;
34961 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34964 if (region == 'center' && this.active ) {
34965 cfg.background = false;
34968 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34970 this.add(region, ret);
34972 if (cfg.background) {
34973 // render grid on panel activation (if panel background)
34974 ret.on('activate', function(gp) {
34975 if (!gp.grid.rendered) {
34976 // gp.grid.render(el);
34980 // cfg.grid.render(el);
34986 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34987 // it was the old xcomponent building that caused this before.
34988 // espeically if border is the top element in the tree.
34998 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35000 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35001 this.add(region, ret);
35005 throw "Can not add '" + cfg.xtype + "' to Border";
35011 this.beginUpdate();
35015 Roo.each(xitems, function(i) {
35016 region = nb && i.region ? i.region : false;
35018 var add = ret.addxtype(i);
35021 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35022 if (!i.background) {
35023 abn[region] = nb[region] ;
35030 // make the last non-background panel active..
35031 //if (nb) { Roo.log(abn); }
35034 for(var r in abn) {
35035 region = this.getRegion(r);
35037 // tried using nb[r], but it does not work..
35039 region.showPanel(abn[r]);
35050 factory : function(cfg)
35053 var validRegions = Roo.bootstrap.layout.Border.regions;
35055 var target = cfg.region;
35058 var r = Roo.bootstrap.layout;
35062 return new r.North(cfg);
35064 return new r.South(cfg);
35066 return new r.East(cfg);
35068 return new r.West(cfg);
35070 return new r.Center(cfg);
35072 throw 'Layout region "'+target+'" not supported.';
35079 * Ext JS Library 1.1.1
35080 * Copyright(c) 2006-2007, Ext JS, LLC.
35082 * Originally Released Under LGPL - original licence link has changed is not relivant.
35085 * <script type="text/javascript">
35089 * @class Roo.bootstrap.layout.Basic
35090 * @extends Roo.util.Observable
35091 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35092 * and does not have a titlebar, tabs or any other features. All it does is size and position
35093 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35094 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35095 * @cfg {string} region the region that it inhabits..
35096 * @cfg {bool} skipConfig skip config?
35100 Roo.bootstrap.layout.Basic = function(config){
35102 this.mgr = config.mgr;
35104 this.position = config.region;
35106 var skipConfig = config.skipConfig;
35110 * @scope Roo.BasicLayoutRegion
35114 * @event beforeremove
35115 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35116 * @param {Roo.LayoutRegion} this
35117 * @param {Roo.ContentPanel} panel The panel
35118 * @param {Object} e The cancel event object
35120 "beforeremove" : true,
35122 * @event invalidated
35123 * Fires when the layout for this region is changed.
35124 * @param {Roo.LayoutRegion} this
35126 "invalidated" : true,
35128 * @event visibilitychange
35129 * Fires when this region is shown or hidden
35130 * @param {Roo.LayoutRegion} this
35131 * @param {Boolean} visibility true or false
35133 "visibilitychange" : true,
35135 * @event paneladded
35136 * Fires when a panel is added.
35137 * @param {Roo.LayoutRegion} this
35138 * @param {Roo.ContentPanel} panel The panel
35140 "paneladded" : true,
35142 * @event panelremoved
35143 * Fires when a panel is removed.
35144 * @param {Roo.LayoutRegion} this
35145 * @param {Roo.ContentPanel} panel The panel
35147 "panelremoved" : true,
35149 * @event beforecollapse
35150 * Fires when this region before collapse.
35151 * @param {Roo.LayoutRegion} this
35153 "beforecollapse" : true,
35156 * Fires when this region is collapsed.
35157 * @param {Roo.LayoutRegion} this
35159 "collapsed" : true,
35162 * Fires when this region is expanded.
35163 * @param {Roo.LayoutRegion} this
35168 * Fires when this region is slid into view.
35169 * @param {Roo.LayoutRegion} this
35171 "slideshow" : true,
35174 * Fires when this region slides out of view.
35175 * @param {Roo.LayoutRegion} this
35177 "slidehide" : true,
35179 * @event panelactivated
35180 * Fires when a panel is activated.
35181 * @param {Roo.LayoutRegion} this
35182 * @param {Roo.ContentPanel} panel The activated panel
35184 "panelactivated" : true,
35187 * Fires when the user resizes this region.
35188 * @param {Roo.LayoutRegion} this
35189 * @param {Number} newSize The new size (width for east/west, height for north/south)
35193 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35194 this.panels = new Roo.util.MixedCollection();
35195 this.panels.getKey = this.getPanelId.createDelegate(this);
35197 this.activePanel = null;
35198 // ensure listeners are added...
35200 if (config.listeners || config.events) {
35201 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35202 listeners : config.listeners || {},
35203 events : config.events || {}
35207 if(skipConfig !== true){
35208 this.applyConfig(config);
35212 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35214 getPanelId : function(p){
35218 applyConfig : function(config){
35219 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35220 this.config = config;
35225 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35226 * the width, for horizontal (north, south) the height.
35227 * @param {Number} newSize The new width or height
35229 resizeTo : function(newSize){
35230 var el = this.el ? this.el :
35231 (this.activePanel ? this.activePanel.getEl() : null);
35233 switch(this.position){
35236 el.setWidth(newSize);
35237 this.fireEvent("resized", this, newSize);
35241 el.setHeight(newSize);
35242 this.fireEvent("resized", this, newSize);
35248 getBox : function(){
35249 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35252 getMargins : function(){
35253 return this.margins;
35256 updateBox : function(box){
35258 var el = this.activePanel.getEl();
35259 el.dom.style.left = box.x + "px";
35260 el.dom.style.top = box.y + "px";
35261 this.activePanel.setSize(box.width, box.height);
35265 * Returns the container element for this region.
35266 * @return {Roo.Element}
35268 getEl : function(){
35269 return this.activePanel;
35273 * Returns true if this region is currently visible.
35274 * @return {Boolean}
35276 isVisible : function(){
35277 return this.activePanel ? true : false;
35280 setActivePanel : function(panel){
35281 panel = this.getPanel(panel);
35282 if(this.activePanel && this.activePanel != panel){
35283 this.activePanel.setActiveState(false);
35284 this.activePanel.getEl().setLeftTop(-10000,-10000);
35286 this.activePanel = panel;
35287 panel.setActiveState(true);
35289 panel.setSize(this.box.width, this.box.height);
35291 this.fireEvent("panelactivated", this, panel);
35292 this.fireEvent("invalidated");
35296 * Show the specified panel.
35297 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35298 * @return {Roo.ContentPanel} The shown panel or null
35300 showPanel : function(panel){
35301 panel = this.getPanel(panel);
35303 this.setActivePanel(panel);
35309 * Get the active panel for this region.
35310 * @return {Roo.ContentPanel} The active panel or null
35312 getActivePanel : function(){
35313 return this.activePanel;
35317 * Add the passed ContentPanel(s)
35318 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35319 * @return {Roo.ContentPanel} The panel added (if only one was added)
35321 add : function(panel){
35322 if(arguments.length > 1){
35323 for(var i = 0, len = arguments.length; i < len; i++) {
35324 this.add(arguments[i]);
35328 if(this.hasPanel(panel)){
35329 this.showPanel(panel);
35332 var el = panel.getEl();
35333 if(el.dom.parentNode != this.mgr.el.dom){
35334 this.mgr.el.dom.appendChild(el.dom);
35336 if(panel.setRegion){
35337 panel.setRegion(this);
35339 this.panels.add(panel);
35340 el.setStyle("position", "absolute");
35341 if(!panel.background){
35342 this.setActivePanel(panel);
35343 if(this.config.initialSize && this.panels.getCount()==1){
35344 this.resizeTo(this.config.initialSize);
35347 this.fireEvent("paneladded", this, panel);
35352 * Returns true if the panel is in this region.
35353 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35354 * @return {Boolean}
35356 hasPanel : function(panel){
35357 if(typeof panel == "object"){ // must be panel obj
35358 panel = panel.getId();
35360 return this.getPanel(panel) ? true : false;
35364 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35365 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366 * @param {Boolean} preservePanel Overrides the config preservePanel option
35367 * @return {Roo.ContentPanel} The panel that was removed
35369 remove : function(panel, preservePanel){
35370 panel = this.getPanel(panel);
35375 this.fireEvent("beforeremove", this, panel, e);
35376 if(e.cancel === true){
35379 var panelId = panel.getId();
35380 this.panels.removeKey(panelId);
35385 * Returns the panel specified or null if it's not in this region.
35386 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35387 * @return {Roo.ContentPanel}
35389 getPanel : function(id){
35390 if(typeof id == "object"){ // must be panel obj
35393 return this.panels.get(id);
35397 * Returns this regions position (north/south/east/west/center).
35400 getPosition: function(){
35401 return this.position;
35405 * Ext JS Library 1.1.1
35406 * Copyright(c) 2006-2007, Ext JS, LLC.
35408 * Originally Released Under LGPL - original licence link has changed is not relivant.
35411 * <script type="text/javascript">
35415 * @class Roo.bootstrap.layout.Region
35416 * @extends Roo.bootstrap.layout.Basic
35417 * This class represents a region in a layout manager.
35419 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35420 * @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})
35421 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35422 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35423 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35424 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35425 * @cfg {String} title The title for the region (overrides panel titles)
35426 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35427 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35428 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35429 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35430 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35431 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35432 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35433 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35434 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35435 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35437 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35438 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35439 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35440 * @cfg {Number} width For East/West panels
35441 * @cfg {Number} height For North/South panels
35442 * @cfg {Boolean} split To show the splitter
35443 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35445 * @cfg {string} cls Extra CSS classes to add to region
35447 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35448 * @cfg {string} region the region that it inhabits..
35451 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35452 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35454 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35455 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35456 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35458 Roo.bootstrap.layout.Region = function(config)
35460 this.applyConfig(config);
35462 var mgr = config.mgr;
35463 var pos = config.region;
35464 config.skipConfig = true;
35465 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35468 this.onRender(mgr.el);
35471 this.visible = true;
35472 this.collapsed = false;
35473 this.unrendered_panels = [];
35476 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35478 position: '', // set by wrapper (eg. north/south etc..)
35479 unrendered_panels : null, // unrendered panels.
35480 createBody : function(){
35481 /** This region's body element
35482 * @type Roo.Element */
35483 this.bodyEl = this.el.createChild({
35485 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35489 onRender: function(ctr, pos)
35491 var dh = Roo.DomHelper;
35492 /** This region's container element
35493 * @type Roo.Element */
35494 this.el = dh.append(ctr.dom, {
35496 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35498 /** This region's title element
35499 * @type Roo.Element */
35501 this.titleEl = dh.append(this.el.dom,
35504 unselectable: "on",
35505 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35507 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35508 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35511 this.titleEl.enableDisplayMode();
35512 /** This region's title text element
35513 * @type HTMLElement */
35514 this.titleTextEl = this.titleEl.dom.firstChild;
35515 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35517 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35518 this.closeBtn.enableDisplayMode();
35519 this.closeBtn.on("click", this.closeClicked, this);
35520 this.closeBtn.hide();
35522 this.createBody(this.config);
35523 if(this.config.hideWhenEmpty){
35525 this.on("paneladded", this.validateVisibility, this);
35526 this.on("panelremoved", this.validateVisibility, this);
35528 if(this.autoScroll){
35529 this.bodyEl.setStyle("overflow", "auto");
35531 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35533 //if(c.titlebar !== false){
35534 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35535 this.titleEl.hide();
35537 this.titleEl.show();
35538 if(this.config.title){
35539 this.titleTextEl.innerHTML = this.config.title;
35543 if(this.config.collapsed){
35544 this.collapse(true);
35546 if(this.config.hidden){
35550 if (this.unrendered_panels && this.unrendered_panels.length) {
35551 for (var i =0;i< this.unrendered_panels.length; i++) {
35552 this.add(this.unrendered_panels[i]);
35554 this.unrendered_panels = null;
35560 applyConfig : function(c)
35563 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35564 var dh = Roo.DomHelper;
35565 if(c.titlebar !== false){
35566 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35567 this.collapseBtn.on("click", this.collapse, this);
35568 this.collapseBtn.enableDisplayMode();
35570 if(c.showPin === true || this.showPin){
35571 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35572 this.stickBtn.enableDisplayMode();
35573 this.stickBtn.on("click", this.expand, this);
35574 this.stickBtn.hide();
35579 /** This region's collapsed element
35580 * @type Roo.Element */
35583 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35584 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35587 if(c.floatable !== false){
35588 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35589 this.collapsedEl.on("click", this.collapseClick, this);
35592 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35593 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35594 id: "message", unselectable: "on", style:{"float":"left"}});
35595 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35597 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35598 this.expandBtn.on("click", this.expand, this);
35602 if(this.collapseBtn){
35603 this.collapseBtn.setVisible(c.collapsible == true);
35606 this.cmargins = c.cmargins || this.cmargins ||
35607 (this.position == "west" || this.position == "east" ?
35608 {top: 0, left: 2, right:2, bottom: 0} :
35609 {top: 2, left: 0, right:0, bottom: 2});
35611 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35614 this.bottomTabs = c.tabPosition != "top";
35616 this.autoScroll = c.autoScroll || false;
35621 this.duration = c.duration || .30;
35622 this.slideDuration = c.slideDuration || .45;
35627 * Returns true if this region is currently visible.
35628 * @return {Boolean}
35630 isVisible : function(){
35631 return this.visible;
35635 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35636 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35638 //setCollapsedTitle : function(title){
35639 // title = title || " ";
35640 // if(this.collapsedTitleTextEl){
35641 // this.collapsedTitleTextEl.innerHTML = title;
35645 getBox : function(){
35647 // if(!this.collapsed){
35648 b = this.el.getBox(false, true);
35650 // b = this.collapsedEl.getBox(false, true);
35655 getMargins : function(){
35656 return this.margins;
35657 //return this.collapsed ? this.cmargins : this.margins;
35660 highlight : function(){
35661 this.el.addClass("x-layout-panel-dragover");
35664 unhighlight : function(){
35665 this.el.removeClass("x-layout-panel-dragover");
35668 updateBox : function(box)
35670 if (!this.bodyEl) {
35671 return; // not rendered yet..
35675 if(!this.collapsed){
35676 this.el.dom.style.left = box.x + "px";
35677 this.el.dom.style.top = box.y + "px";
35678 this.updateBody(box.width, box.height);
35680 this.collapsedEl.dom.style.left = box.x + "px";
35681 this.collapsedEl.dom.style.top = box.y + "px";
35682 this.collapsedEl.setSize(box.width, box.height);
35685 this.tabs.autoSizeTabs();
35689 updateBody : function(w, h)
35692 this.el.setWidth(w);
35693 w -= this.el.getBorderWidth("rl");
35694 if(this.config.adjustments){
35695 w += this.config.adjustments[0];
35698 if(h !== null && h > 0){
35699 this.el.setHeight(h);
35700 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35701 h -= this.el.getBorderWidth("tb");
35702 if(this.config.adjustments){
35703 h += this.config.adjustments[1];
35705 this.bodyEl.setHeight(h);
35707 h = this.tabs.syncHeight(h);
35710 if(this.panelSize){
35711 w = w !== null ? w : this.panelSize.width;
35712 h = h !== null ? h : this.panelSize.height;
35714 if(this.activePanel){
35715 var el = this.activePanel.getEl();
35716 w = w !== null ? w : el.getWidth();
35717 h = h !== null ? h : el.getHeight();
35718 this.panelSize = {width: w, height: h};
35719 this.activePanel.setSize(w, h);
35721 if(Roo.isIE && this.tabs){
35722 this.tabs.el.repaint();
35727 * Returns the container element for this region.
35728 * @return {Roo.Element}
35730 getEl : function(){
35735 * Hides this region.
35738 //if(!this.collapsed){
35739 this.el.dom.style.left = "-2000px";
35742 // this.collapsedEl.dom.style.left = "-2000px";
35743 // this.collapsedEl.hide();
35745 this.visible = false;
35746 this.fireEvent("visibilitychange", this, false);
35750 * Shows this region if it was previously hidden.
35753 //if(!this.collapsed){
35756 // this.collapsedEl.show();
35758 this.visible = true;
35759 this.fireEvent("visibilitychange", this, true);
35762 closeClicked : function(){
35763 if(this.activePanel){
35764 this.remove(this.activePanel);
35768 collapseClick : function(e){
35770 e.stopPropagation();
35773 e.stopPropagation();
35779 * Collapses this region.
35780 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35783 collapse : function(skipAnim, skipCheck = false){
35784 if(this.collapsed) {
35788 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35790 this.collapsed = true;
35792 this.split.el.hide();
35794 if(this.config.animate && skipAnim !== true){
35795 this.fireEvent("invalidated", this);
35796 this.animateCollapse();
35798 this.el.setLocation(-20000,-20000);
35800 this.collapsedEl.show();
35801 this.fireEvent("collapsed", this);
35802 this.fireEvent("invalidated", this);
35808 animateCollapse : function(){
35813 * Expands this region if it was previously collapsed.
35814 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35815 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35818 expand : function(e, skipAnim){
35820 e.stopPropagation();
35822 if(!this.collapsed || this.el.hasActiveFx()) {
35826 this.afterSlideIn();
35829 this.collapsed = false;
35830 if(this.config.animate && skipAnim !== true){
35831 this.animateExpand();
35835 this.split.el.show();
35837 this.collapsedEl.setLocation(-2000,-2000);
35838 this.collapsedEl.hide();
35839 this.fireEvent("invalidated", this);
35840 this.fireEvent("expanded", this);
35844 animateExpand : function(){
35848 initTabs : function()
35850 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35852 var ts = new Roo.bootstrap.panel.Tabs({
35853 el: this.bodyEl.dom,
35854 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35855 disableTooltips: this.config.disableTabTips,
35856 toolbar : this.config.toolbar
35859 if(this.config.hideTabs){
35860 ts.stripWrap.setDisplayed(false);
35863 ts.resizeTabs = this.config.resizeTabs === true;
35864 ts.minTabWidth = this.config.minTabWidth || 40;
35865 ts.maxTabWidth = this.config.maxTabWidth || 250;
35866 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35867 ts.monitorResize = false;
35868 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35869 ts.bodyEl.addClass('roo-layout-tabs-body');
35870 this.panels.each(this.initPanelAsTab, this);
35873 initPanelAsTab : function(panel){
35874 var ti = this.tabs.addTab(
35878 this.config.closeOnTab && panel.isClosable(),
35881 if(panel.tabTip !== undefined){
35882 ti.setTooltip(panel.tabTip);
35884 ti.on("activate", function(){
35885 this.setActivePanel(panel);
35888 if(this.config.closeOnTab){
35889 ti.on("beforeclose", function(t, e){
35891 this.remove(panel);
35895 panel.tabItem = ti;
35900 updatePanelTitle : function(panel, title)
35902 if(this.activePanel == panel){
35903 this.updateTitle(title);
35906 var ti = this.tabs.getTab(panel.getEl().id);
35908 if(panel.tabTip !== undefined){
35909 ti.setTooltip(panel.tabTip);
35914 updateTitle : function(title){
35915 if(this.titleTextEl && !this.config.title){
35916 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35920 setActivePanel : function(panel)
35922 panel = this.getPanel(panel);
35923 if(this.activePanel && this.activePanel != panel){
35924 if(this.activePanel.setActiveState(false) === false){
35928 this.activePanel = panel;
35929 panel.setActiveState(true);
35930 if(this.panelSize){
35931 panel.setSize(this.panelSize.width, this.panelSize.height);
35934 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35936 this.updateTitle(panel.getTitle());
35938 this.fireEvent("invalidated", this);
35940 this.fireEvent("panelactivated", this, panel);
35944 * Shows the specified panel.
35945 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35946 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35948 showPanel : function(panel)
35950 panel = this.getPanel(panel);
35953 var tab = this.tabs.getTab(panel.getEl().id);
35954 if(tab.isHidden()){
35955 this.tabs.unhideTab(tab.id);
35959 this.setActivePanel(panel);
35966 * Get the active panel for this region.
35967 * @return {Roo.ContentPanel} The active panel or null
35969 getActivePanel : function(){
35970 return this.activePanel;
35973 validateVisibility : function(){
35974 if(this.panels.getCount() < 1){
35975 this.updateTitle(" ");
35976 this.closeBtn.hide();
35979 if(!this.isVisible()){
35986 * Adds the passed ContentPanel(s) to this region.
35987 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35988 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35990 add : function(panel)
35992 if(arguments.length > 1){
35993 for(var i = 0, len = arguments.length; i < len; i++) {
35994 this.add(arguments[i]);
35999 // if we have not been rendered yet, then we can not really do much of this..
36000 if (!this.bodyEl) {
36001 this.unrendered_panels.push(panel);
36008 if(this.hasPanel(panel)){
36009 this.showPanel(panel);
36012 panel.setRegion(this);
36013 this.panels.add(panel);
36014 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36015 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36016 // and hide them... ???
36017 this.bodyEl.dom.appendChild(panel.getEl().dom);
36018 if(panel.background !== true){
36019 this.setActivePanel(panel);
36021 this.fireEvent("paneladded", this, panel);
36028 this.initPanelAsTab(panel);
36032 if(panel.background !== true){
36033 this.tabs.activate(panel.getEl().id);
36035 this.fireEvent("paneladded", this, panel);
36040 * Hides the tab for the specified panel.
36041 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36043 hidePanel : function(panel){
36044 if(this.tabs && (panel = this.getPanel(panel))){
36045 this.tabs.hideTab(panel.getEl().id);
36050 * Unhides the tab for a previously hidden panel.
36051 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36053 unhidePanel : function(panel){
36054 if(this.tabs && (panel = this.getPanel(panel))){
36055 this.tabs.unhideTab(panel.getEl().id);
36059 clearPanels : function(){
36060 while(this.panels.getCount() > 0){
36061 this.remove(this.panels.first());
36066 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36067 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36068 * @param {Boolean} preservePanel Overrides the config preservePanel option
36069 * @return {Roo.ContentPanel} The panel that was removed
36071 remove : function(panel, preservePanel)
36073 panel = this.getPanel(panel);
36078 this.fireEvent("beforeremove", this, panel, e);
36079 if(e.cancel === true){
36082 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36083 var panelId = panel.getId();
36084 this.panels.removeKey(panelId);
36086 document.body.appendChild(panel.getEl().dom);
36089 this.tabs.removeTab(panel.getEl().id);
36090 }else if (!preservePanel){
36091 this.bodyEl.dom.removeChild(panel.getEl().dom);
36093 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36094 var p = this.panels.first();
36095 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36096 tempEl.appendChild(p.getEl().dom);
36097 this.bodyEl.update("");
36098 this.bodyEl.dom.appendChild(p.getEl().dom);
36100 this.updateTitle(p.getTitle());
36102 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36103 this.setActivePanel(p);
36105 panel.setRegion(null);
36106 if(this.activePanel == panel){
36107 this.activePanel = null;
36109 if(this.config.autoDestroy !== false && preservePanel !== true){
36110 try{panel.destroy();}catch(e){}
36112 this.fireEvent("panelremoved", this, panel);
36117 * Returns the TabPanel component used by this region
36118 * @return {Roo.TabPanel}
36120 getTabs : function(){
36124 createTool : function(parentEl, className){
36125 var btn = Roo.DomHelper.append(parentEl, {
36127 cls: "x-layout-tools-button",
36130 cls: "roo-layout-tools-button-inner " + className,
36134 btn.addClassOnOver("roo-layout-tools-button-over");
36139 * Ext JS Library 1.1.1
36140 * Copyright(c) 2006-2007, Ext JS, LLC.
36142 * Originally Released Under LGPL - original licence link has changed is not relivant.
36145 * <script type="text/javascript">
36151 * @class Roo.SplitLayoutRegion
36152 * @extends Roo.LayoutRegion
36153 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36155 Roo.bootstrap.layout.Split = function(config){
36156 this.cursor = config.cursor;
36157 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36160 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36162 splitTip : "Drag to resize.",
36163 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36164 useSplitTips : false,
36166 applyConfig : function(config){
36167 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36170 onRender : function(ctr,pos) {
36172 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36173 if(!this.config.split){
36178 var splitEl = Roo.DomHelper.append(ctr.dom, {
36180 id: this.el.id + "-split",
36181 cls: "roo-layout-split roo-layout-split-"+this.position,
36184 /** The SplitBar for this region
36185 * @type Roo.SplitBar */
36186 // does not exist yet...
36187 Roo.log([this.position, this.orientation]);
36189 this.split = new Roo.bootstrap.SplitBar({
36190 dragElement : splitEl,
36191 resizingElement: this.el,
36192 orientation : this.orientation
36195 this.split.on("moved", this.onSplitMove, this);
36196 this.split.useShim = this.config.useShim === true;
36197 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36198 if(this.useSplitTips){
36199 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36201 //if(config.collapsible){
36202 // this.split.el.on("dblclick", this.collapse, this);
36205 if(typeof this.config.minSize != "undefined"){
36206 this.split.minSize = this.config.minSize;
36208 if(typeof this.config.maxSize != "undefined"){
36209 this.split.maxSize = this.config.maxSize;
36211 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36212 this.hideSplitter();
36217 getHMaxSize : function(){
36218 var cmax = this.config.maxSize || 10000;
36219 var center = this.mgr.getRegion("center");
36220 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36223 getVMaxSize : function(){
36224 var cmax = this.config.maxSize || 10000;
36225 var center = this.mgr.getRegion("center");
36226 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36229 onSplitMove : function(split, newSize){
36230 this.fireEvent("resized", this, newSize);
36234 * Returns the {@link Roo.SplitBar} for this region.
36235 * @return {Roo.SplitBar}
36237 getSplitBar : function(){
36242 this.hideSplitter();
36243 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36246 hideSplitter : function(){
36248 this.split.el.setLocation(-2000,-2000);
36249 this.split.el.hide();
36255 this.split.el.show();
36257 Roo.bootstrap.layout.Split.superclass.show.call(this);
36260 beforeSlide: function(){
36261 if(Roo.isGecko){// firefox overflow auto bug workaround
36262 this.bodyEl.clip();
36264 this.tabs.bodyEl.clip();
36266 if(this.activePanel){
36267 this.activePanel.getEl().clip();
36269 if(this.activePanel.beforeSlide){
36270 this.activePanel.beforeSlide();
36276 afterSlide : function(){
36277 if(Roo.isGecko){// firefox overflow auto bug workaround
36278 this.bodyEl.unclip();
36280 this.tabs.bodyEl.unclip();
36282 if(this.activePanel){
36283 this.activePanel.getEl().unclip();
36284 if(this.activePanel.afterSlide){
36285 this.activePanel.afterSlide();
36291 initAutoHide : function(){
36292 if(this.autoHide !== false){
36293 if(!this.autoHideHd){
36294 var st = new Roo.util.DelayedTask(this.slideIn, this);
36295 this.autoHideHd = {
36296 "mouseout": function(e){
36297 if(!e.within(this.el, true)){
36301 "mouseover" : function(e){
36307 this.el.on(this.autoHideHd);
36311 clearAutoHide : function(){
36312 if(this.autoHide !== false){
36313 this.el.un("mouseout", this.autoHideHd.mouseout);
36314 this.el.un("mouseover", this.autoHideHd.mouseover);
36318 clearMonitor : function(){
36319 Roo.get(document).un("click", this.slideInIf, this);
36322 // these names are backwards but not changed for compat
36323 slideOut : function(){
36324 if(this.isSlid || this.el.hasActiveFx()){
36327 this.isSlid = true;
36328 if(this.collapseBtn){
36329 this.collapseBtn.hide();
36331 this.closeBtnState = this.closeBtn.getStyle('display');
36332 this.closeBtn.hide();
36334 this.stickBtn.show();
36337 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36338 this.beforeSlide();
36339 this.el.setStyle("z-index", 10001);
36340 this.el.slideIn(this.getSlideAnchor(), {
36341 callback: function(){
36343 this.initAutoHide();
36344 Roo.get(document).on("click", this.slideInIf, this);
36345 this.fireEvent("slideshow", this);
36352 afterSlideIn : function(){
36353 this.clearAutoHide();
36354 this.isSlid = false;
36355 this.clearMonitor();
36356 this.el.setStyle("z-index", "");
36357 if(this.collapseBtn){
36358 this.collapseBtn.show();
36360 this.closeBtn.setStyle('display', this.closeBtnState);
36362 this.stickBtn.hide();
36364 this.fireEvent("slidehide", this);
36367 slideIn : function(cb){
36368 if(!this.isSlid || this.el.hasActiveFx()){
36372 this.isSlid = false;
36373 this.beforeSlide();
36374 this.el.slideOut(this.getSlideAnchor(), {
36375 callback: function(){
36376 this.el.setLeftTop(-10000, -10000);
36378 this.afterSlideIn();
36386 slideInIf : function(e){
36387 if(!e.within(this.el)){
36392 animateCollapse : function(){
36393 this.beforeSlide();
36394 this.el.setStyle("z-index", 20000);
36395 var anchor = this.getSlideAnchor();
36396 this.el.slideOut(anchor, {
36397 callback : function(){
36398 this.el.setStyle("z-index", "");
36399 this.collapsedEl.slideIn(anchor, {duration:.3});
36401 this.el.setLocation(-10000,-10000);
36403 this.fireEvent("collapsed", this);
36410 animateExpand : function(){
36411 this.beforeSlide();
36412 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36413 this.el.setStyle("z-index", 20000);
36414 this.collapsedEl.hide({
36417 this.el.slideIn(this.getSlideAnchor(), {
36418 callback : function(){
36419 this.el.setStyle("z-index", "");
36422 this.split.el.show();
36424 this.fireEvent("invalidated", this);
36425 this.fireEvent("expanded", this);
36453 getAnchor : function(){
36454 return this.anchors[this.position];
36457 getCollapseAnchor : function(){
36458 return this.canchors[this.position];
36461 getSlideAnchor : function(){
36462 return this.sanchors[this.position];
36465 getAlignAdj : function(){
36466 var cm = this.cmargins;
36467 switch(this.position){
36483 getExpandAdj : function(){
36484 var c = this.collapsedEl, cm = this.cmargins;
36485 switch(this.position){
36487 return [-(cm.right+c.getWidth()+cm.left), 0];
36490 return [cm.right+c.getWidth()+cm.left, 0];
36493 return [0, -(cm.top+cm.bottom+c.getHeight())];
36496 return [0, cm.top+cm.bottom+c.getHeight()];
36502 * Ext JS Library 1.1.1
36503 * Copyright(c) 2006-2007, Ext JS, LLC.
36505 * Originally Released Under LGPL - original licence link has changed is not relivant.
36508 * <script type="text/javascript">
36511 * These classes are private internal classes
36513 Roo.bootstrap.layout.Center = function(config){
36514 config.region = "center";
36515 Roo.bootstrap.layout.Region.call(this, config);
36516 this.visible = true;
36517 this.minWidth = config.minWidth || 20;
36518 this.minHeight = config.minHeight || 20;
36521 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36523 // center panel can't be hidden
36527 // center panel can't be hidden
36530 getMinWidth: function(){
36531 return this.minWidth;
36534 getMinHeight: function(){
36535 return this.minHeight;
36548 Roo.bootstrap.layout.North = function(config)
36550 config.region = 'north';
36551 config.cursor = 'n-resize';
36553 Roo.bootstrap.layout.Split.call(this, config);
36557 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36558 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36559 this.split.el.addClass("roo-layout-split-v");
36561 var size = config.initialSize || config.height;
36562 if(typeof size != "undefined"){
36563 this.el.setHeight(size);
36566 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36568 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36572 getBox : function(){
36573 if(this.collapsed){
36574 return this.collapsedEl.getBox();
36576 var box = this.el.getBox();
36578 box.height += this.split.el.getHeight();
36583 updateBox : function(box){
36584 if(this.split && !this.collapsed){
36585 box.height -= this.split.el.getHeight();
36586 this.split.el.setLeft(box.x);
36587 this.split.el.setTop(box.y+box.height);
36588 this.split.el.setWidth(box.width);
36590 if(this.collapsed){
36591 this.updateBody(box.width, null);
36593 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36601 Roo.bootstrap.layout.South = function(config){
36602 config.region = 'south';
36603 config.cursor = 's-resize';
36604 Roo.bootstrap.layout.Split.call(this, config);
36606 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36607 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36608 this.split.el.addClass("roo-layout-split-v");
36610 var size = config.initialSize || config.height;
36611 if(typeof size != "undefined"){
36612 this.el.setHeight(size);
36616 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36617 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36618 getBox : function(){
36619 if(this.collapsed){
36620 return this.collapsedEl.getBox();
36622 var box = this.el.getBox();
36624 var sh = this.split.el.getHeight();
36631 updateBox : function(box){
36632 if(this.split && !this.collapsed){
36633 var sh = this.split.el.getHeight();
36636 this.split.el.setLeft(box.x);
36637 this.split.el.setTop(box.y-sh);
36638 this.split.el.setWidth(box.width);
36640 if(this.collapsed){
36641 this.updateBody(box.width, null);
36643 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36647 Roo.bootstrap.layout.East = function(config){
36648 config.region = "east";
36649 config.cursor = "e-resize";
36650 Roo.bootstrap.layout.Split.call(this, config);
36652 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36653 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36654 this.split.el.addClass("roo-layout-split-h");
36656 var size = config.initialSize || config.width;
36657 if(typeof size != "undefined"){
36658 this.el.setWidth(size);
36661 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36662 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36663 getBox : function(){
36664 if(this.collapsed){
36665 return this.collapsedEl.getBox();
36667 var box = this.el.getBox();
36669 var sw = this.split.el.getWidth();
36676 updateBox : function(box){
36677 if(this.split && !this.collapsed){
36678 var sw = this.split.el.getWidth();
36680 this.split.el.setLeft(box.x);
36681 this.split.el.setTop(box.y);
36682 this.split.el.setHeight(box.height);
36685 if(this.collapsed){
36686 this.updateBody(null, box.height);
36688 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36692 Roo.bootstrap.layout.West = function(config){
36693 config.region = "west";
36694 config.cursor = "w-resize";
36696 Roo.bootstrap.layout.Split.call(this, config);
36698 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36699 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36700 this.split.el.addClass("roo-layout-split-h");
36704 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36705 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36707 onRender: function(ctr, pos)
36709 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36710 var size = this.config.initialSize || this.config.width;
36711 if(typeof size != "undefined"){
36712 this.el.setWidth(size);
36716 getBox : function(){
36717 if(this.collapsed){
36718 return this.collapsedEl.getBox();
36720 var box = this.el.getBox();
36722 box.width += this.split.el.getWidth();
36727 updateBox : function(box){
36728 if(this.split && !this.collapsed){
36729 var sw = this.split.el.getWidth();
36731 this.split.el.setLeft(box.x+box.width);
36732 this.split.el.setTop(box.y);
36733 this.split.el.setHeight(box.height);
36735 if(this.collapsed){
36736 this.updateBody(null, box.height);
36738 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36741 Roo.namespace("Roo.bootstrap.panel");/*
36743 * Ext JS Library 1.1.1
36744 * Copyright(c) 2006-2007, Ext JS, LLC.
36746 * Originally Released Under LGPL - original licence link has changed is not relivant.
36749 * <script type="text/javascript">
36752 * @class Roo.ContentPanel
36753 * @extends Roo.util.Observable
36754 * A basic ContentPanel element.
36755 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36756 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36757 * @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
36758 * @cfg {Boolean} closable True if the panel can be closed/removed
36759 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36760 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36761 * @cfg {Toolbar} toolbar A toolbar for this panel
36762 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36763 * @cfg {String} title The title for this panel
36764 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36765 * @cfg {String} url Calls {@link #setUrl} with this value
36766 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36767 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36768 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36769 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36770 * @cfg {Boolean} badges render the badges
36773 * Create a new ContentPanel.
36774 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36775 * @param {String/Object} config A string to set only the title or a config object
36776 * @param {String} content (optional) Set the HTML content for this panel
36777 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36779 Roo.bootstrap.panel.Content = function( config){
36781 this.tpl = config.tpl || false;
36783 var el = config.el;
36784 var content = config.content;
36786 if(config.autoCreate){ // xtype is available if this is called from factory
36789 this.el = Roo.get(el);
36790 if(!this.el && config && config.autoCreate){
36791 if(typeof config.autoCreate == "object"){
36792 if(!config.autoCreate.id){
36793 config.autoCreate.id = config.id||el;
36795 this.el = Roo.DomHelper.append(document.body,
36796 config.autoCreate, true);
36798 var elcfg = { tag: "div",
36799 cls: "roo-layout-inactive-content",
36803 elcfg.html = config.html;
36807 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36810 this.closable = false;
36811 this.loaded = false;
36812 this.active = false;
36815 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36817 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36819 this.wrapEl = this.el; //this.el.wrap();
36821 if (config.toolbar.items) {
36822 ti = config.toolbar.items ;
36823 delete config.toolbar.items ;
36827 this.toolbar.render(this.wrapEl, 'before');
36828 for(var i =0;i < ti.length;i++) {
36829 // Roo.log(['add child', items[i]]);
36830 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36832 this.toolbar.items = nitems;
36833 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36834 delete config.toolbar;
36838 // xtype created footer. - not sure if will work as we normally have to render first..
36839 if (this.footer && !this.footer.el && this.footer.xtype) {
36840 if (!this.wrapEl) {
36841 this.wrapEl = this.el.wrap();
36844 this.footer.container = this.wrapEl.createChild();
36846 this.footer = Roo.factory(this.footer, Roo);
36851 if(typeof config == "string"){
36852 this.title = config;
36854 Roo.apply(this, config);
36858 this.resizeEl = Roo.get(this.resizeEl, true);
36860 this.resizeEl = this.el;
36862 // handle view.xtype
36870 * Fires when this panel is activated.
36871 * @param {Roo.ContentPanel} this
36875 * @event deactivate
36876 * Fires when this panel is activated.
36877 * @param {Roo.ContentPanel} this
36879 "deactivate" : true,
36883 * Fires when this panel is resized if fitToFrame is true.
36884 * @param {Roo.ContentPanel} this
36885 * @param {Number} width The width after any component adjustments
36886 * @param {Number} height The height after any component adjustments
36892 * Fires when this tab is created
36893 * @param {Roo.ContentPanel} this
36904 if(this.autoScroll){
36905 this.resizeEl.setStyle("overflow", "auto");
36907 // fix randome scrolling
36908 //this.el.on('scroll', function() {
36909 // Roo.log('fix random scolling');
36910 // this.scrollTo('top',0);
36913 content = content || this.content;
36915 this.setContent(content);
36917 if(config && config.url){
36918 this.setUrl(this.url, this.params, this.loadOnce);
36923 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36925 if (this.view && typeof(this.view.xtype) != 'undefined') {
36926 this.view.el = this.el.appendChild(document.createElement("div"));
36927 this.view = Roo.factory(this.view);
36928 this.view.render && this.view.render(false, '');
36932 this.fireEvent('render', this);
36935 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36939 setRegion : function(region){
36940 this.region = region;
36941 this.setActiveClass(region && !this.background);
36945 setActiveClass: function(state)
36948 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36949 this.el.setStyle('position','relative');
36951 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36952 this.el.setStyle('position', 'absolute');
36957 * Returns the toolbar for this Panel if one was configured.
36958 * @return {Roo.Toolbar}
36960 getToolbar : function(){
36961 return this.toolbar;
36964 setActiveState : function(active)
36966 this.active = active;
36967 this.setActiveClass(active);
36969 if(this.fireEvent("deactivate", this) === false){
36974 this.fireEvent("activate", this);
36978 * Updates this panel's element
36979 * @param {String} content The new content
36980 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36982 setContent : function(content, loadScripts){
36983 this.el.update(content, loadScripts);
36986 ignoreResize : function(w, h){
36987 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36990 this.lastSize = {width: w, height: h};
36995 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36996 * @return {Roo.UpdateManager} The UpdateManager
36998 getUpdateManager : function(){
36999 return this.el.getUpdateManager();
37002 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37003 * @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:
37006 url: "your-url.php",
37007 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37008 callback: yourFunction,
37009 scope: yourObject, //(optional scope)
37012 text: "Loading...",
37017 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37018 * 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.
37019 * @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}
37020 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37021 * @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.
37022 * @return {Roo.ContentPanel} this
37025 var um = this.el.getUpdateManager();
37026 um.update.apply(um, arguments);
37032 * 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.
37033 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37034 * @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)
37035 * @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)
37036 * @return {Roo.UpdateManager} The UpdateManager
37038 setUrl : function(url, params, loadOnce){
37039 if(this.refreshDelegate){
37040 this.removeListener("activate", this.refreshDelegate);
37042 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37043 this.on("activate", this.refreshDelegate);
37044 return this.el.getUpdateManager();
37047 _handleRefresh : function(url, params, loadOnce){
37048 if(!loadOnce || !this.loaded){
37049 var updater = this.el.getUpdateManager();
37050 updater.update(url, params, this._setLoaded.createDelegate(this));
37054 _setLoaded : function(){
37055 this.loaded = true;
37059 * Returns this panel's id
37062 getId : function(){
37067 * Returns this panel's element - used by regiosn to add.
37068 * @return {Roo.Element}
37070 getEl : function(){
37071 return this.wrapEl || this.el;
37076 adjustForComponents : function(width, height)
37078 //Roo.log('adjustForComponents ');
37079 if(this.resizeEl != this.el){
37080 width -= this.el.getFrameWidth('lr');
37081 height -= this.el.getFrameWidth('tb');
37084 var te = this.toolbar.getEl();
37085 te.setWidth(width);
37086 height -= te.getHeight();
37089 var te = this.footer.getEl();
37090 te.setWidth(width);
37091 height -= te.getHeight();
37095 if(this.adjustments){
37096 width += this.adjustments[0];
37097 height += this.adjustments[1];
37099 return {"width": width, "height": height};
37102 setSize : function(width, height){
37103 if(this.fitToFrame && !this.ignoreResize(width, height)){
37104 if(this.fitContainer && this.resizeEl != this.el){
37105 this.el.setSize(width, height);
37107 var size = this.adjustForComponents(width, height);
37108 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37109 this.fireEvent('resize', this, size.width, size.height);
37114 * Returns this panel's title
37117 getTitle : function(){
37119 if (typeof(this.title) != 'object') {
37124 for (var k in this.title) {
37125 if (!this.title.hasOwnProperty(k)) {
37129 if (k.indexOf('-') >= 0) {
37130 var s = k.split('-');
37131 for (var i = 0; i<s.length; i++) {
37132 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37135 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37142 * Set this panel's title
37143 * @param {String} title
37145 setTitle : function(title){
37146 this.title = title;
37148 this.region.updatePanelTitle(this, title);
37153 * Returns true is this panel was configured to be closable
37154 * @return {Boolean}
37156 isClosable : function(){
37157 return this.closable;
37160 beforeSlide : function(){
37162 this.resizeEl.clip();
37165 afterSlide : function(){
37167 this.resizeEl.unclip();
37171 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37172 * Will fail silently if the {@link #setUrl} method has not been called.
37173 * This does not activate the panel, just updates its content.
37175 refresh : function(){
37176 if(this.refreshDelegate){
37177 this.loaded = false;
37178 this.refreshDelegate();
37183 * Destroys this panel
37185 destroy : function(){
37186 this.el.removeAllListeners();
37187 var tempEl = document.createElement("span");
37188 tempEl.appendChild(this.el.dom);
37189 tempEl.innerHTML = "";
37195 * form - if the content panel contains a form - this is a reference to it.
37196 * @type {Roo.form.Form}
37200 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37201 * This contains a reference to it.
37207 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37217 * @param {Object} cfg Xtype definition of item to add.
37221 getChildContainer: function () {
37222 return this.getEl();
37227 var ret = new Roo.factory(cfg);
37232 if (cfg.xtype.match(/^Form$/)) {
37235 //if (this.footer) {
37236 // el = this.footer.container.insertSibling(false, 'before');
37238 el = this.el.createChild();
37241 this.form = new Roo.form.Form(cfg);
37244 if ( this.form.allItems.length) {
37245 this.form.render(el.dom);
37249 // should only have one of theses..
37250 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37251 // views.. should not be just added - used named prop 'view''
37253 cfg.el = this.el.appendChild(document.createElement("div"));
37256 var ret = new Roo.factory(cfg);
37258 ret.render && ret.render(false, ''); // render blank..
37268 * @class Roo.bootstrap.panel.Grid
37269 * @extends Roo.bootstrap.panel.Content
37271 * Create a new GridPanel.
37272 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37273 * @param {Object} config A the config object
37279 Roo.bootstrap.panel.Grid = function(config)
37283 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37284 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37286 config.el = this.wrapper;
37287 //this.el = this.wrapper;
37289 if (config.container) {
37290 // ctor'ed from a Border/panel.grid
37293 this.wrapper.setStyle("overflow", "hidden");
37294 this.wrapper.addClass('roo-grid-container');
37299 if(config.toolbar){
37300 var tool_el = this.wrapper.createChild();
37301 this.toolbar = Roo.factory(config.toolbar);
37303 if (config.toolbar.items) {
37304 ti = config.toolbar.items ;
37305 delete config.toolbar.items ;
37309 this.toolbar.render(tool_el);
37310 for(var i =0;i < ti.length;i++) {
37311 // Roo.log(['add child', items[i]]);
37312 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37314 this.toolbar.items = nitems;
37316 delete config.toolbar;
37319 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37320 config.grid.scrollBody = true;;
37321 config.grid.monitorWindowResize = false; // turn off autosizing
37322 config.grid.autoHeight = false;
37323 config.grid.autoWidth = false;
37325 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37327 if (config.background) {
37328 // render grid on panel activation (if panel background)
37329 this.on('activate', function(gp) {
37330 if (!gp.grid.rendered) {
37331 gp.grid.render(this.wrapper);
37332 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37337 this.grid.render(this.wrapper);
37338 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37341 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37342 // ??? needed ??? config.el = this.wrapper;
37347 // xtype created footer. - not sure if will work as we normally have to render first..
37348 if (this.footer && !this.footer.el && this.footer.xtype) {
37350 var ctr = this.grid.getView().getFooterPanel(true);
37351 this.footer.dataSource = this.grid.dataSource;
37352 this.footer = Roo.factory(this.footer, Roo);
37353 this.footer.render(ctr);
37363 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37364 getId : function(){
37365 return this.grid.id;
37369 * Returns the grid for this panel
37370 * @return {Roo.bootstrap.Table}
37372 getGrid : function(){
37376 setSize : function(width, height){
37377 if(!this.ignoreResize(width, height)){
37378 var grid = this.grid;
37379 var size = this.adjustForComponents(width, height);
37380 var gridel = grid.getGridEl();
37381 gridel.setSize(size.width, size.height);
37383 var thd = grid.getGridEl().select('thead',true).first();
37384 var tbd = grid.getGridEl().select('tbody', true).first();
37386 tbd.setSize(width, height - thd.getHeight());
37395 beforeSlide : function(){
37396 this.grid.getView().scroller.clip();
37399 afterSlide : function(){
37400 this.grid.getView().scroller.unclip();
37403 destroy : function(){
37404 this.grid.destroy();
37406 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37411 * @class Roo.bootstrap.panel.Nest
37412 * @extends Roo.bootstrap.panel.Content
37414 * Create a new Panel, that can contain a layout.Border.
37417 * @param {Roo.BorderLayout} layout The layout for this panel
37418 * @param {String/Object} config A string to set only the title or a config object
37420 Roo.bootstrap.panel.Nest = function(config)
37422 // construct with only one argument..
37423 /* FIXME - implement nicer consturctors
37424 if (layout.layout) {
37426 layout = config.layout;
37427 delete config.layout;
37429 if (layout.xtype && !layout.getEl) {
37430 // then layout needs constructing..
37431 layout = Roo.factory(layout, Roo);
37435 config.el = config.layout.getEl();
37437 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37439 config.layout.monitorWindowResize = false; // turn off autosizing
37440 this.layout = config.layout;
37441 this.layout.getEl().addClass("roo-layout-nested-layout");
37448 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37450 setSize : function(width, height){
37451 if(!this.ignoreResize(width, height)){
37452 var size = this.adjustForComponents(width, height);
37453 var el = this.layout.getEl();
37454 if (size.height < 1) {
37455 el.setWidth(size.width);
37457 el.setSize(size.width, size.height);
37459 var touch = el.dom.offsetWidth;
37460 this.layout.layout();
37461 // ie requires a double layout on the first pass
37462 if(Roo.isIE && !this.initialized){
37463 this.initialized = true;
37464 this.layout.layout();
37469 // activate all subpanels if not currently active..
37471 setActiveState : function(active){
37472 this.active = active;
37473 this.setActiveClass(active);
37476 this.fireEvent("deactivate", this);
37480 this.fireEvent("activate", this);
37481 // not sure if this should happen before or after..
37482 if (!this.layout) {
37483 return; // should not happen..
37486 for (var r in this.layout.regions) {
37487 reg = this.layout.getRegion(r);
37488 if (reg.getActivePanel()) {
37489 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37490 reg.setActivePanel(reg.getActivePanel());
37493 if (!reg.panels.length) {
37496 reg.showPanel(reg.getPanel(0));
37505 * Returns the nested BorderLayout for this panel
37506 * @return {Roo.BorderLayout}
37508 getLayout : function(){
37509 return this.layout;
37513 * Adds a xtype elements to the layout of the nested panel
37517 xtype : 'ContentPanel',
37524 xtype : 'NestedLayoutPanel',
37530 items : [ ... list of content panels or nested layout panels.. ]
37534 * @param {Object} cfg Xtype definition of item to add.
37536 addxtype : function(cfg) {
37537 return this.layout.addxtype(cfg);
37542 * Ext JS Library 1.1.1
37543 * Copyright(c) 2006-2007, Ext JS, LLC.
37545 * Originally Released Under LGPL - original licence link has changed is not relivant.
37548 * <script type="text/javascript">
37551 * @class Roo.TabPanel
37552 * @extends Roo.util.Observable
37553 * A lightweight tab container.
37557 // basic tabs 1, built from existing content
37558 var tabs = new Roo.TabPanel("tabs1");
37559 tabs.addTab("script", "View Script");
37560 tabs.addTab("markup", "View Markup");
37561 tabs.activate("script");
37563 // more advanced tabs, built from javascript
37564 var jtabs = new Roo.TabPanel("jtabs");
37565 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37567 // set up the UpdateManager
37568 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37569 var updater = tab2.getUpdateManager();
37570 updater.setDefaultUrl("ajax1.htm");
37571 tab2.on('activate', updater.refresh, updater, true);
37573 // Use setUrl for Ajax loading
37574 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37575 tab3.setUrl("ajax2.htm", null, true);
37578 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37581 jtabs.activate("jtabs-1");
37584 * Create a new TabPanel.
37585 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37586 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37588 Roo.bootstrap.panel.Tabs = function(config){
37590 * The container element for this TabPanel.
37591 * @type Roo.Element
37593 this.el = Roo.get(config.el);
37596 if(typeof config == "boolean"){
37597 this.tabPosition = config ? "bottom" : "top";
37599 Roo.apply(this, config);
37603 if(this.tabPosition == "bottom"){
37604 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37605 this.el.addClass("roo-tabs-bottom");
37607 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37608 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37609 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37611 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37613 if(this.tabPosition != "bottom"){
37614 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37615 * @type Roo.Element
37617 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37618 this.el.addClass("roo-tabs-top");
37622 this.bodyEl.setStyle("position", "relative");
37624 this.active = null;
37625 this.activateDelegate = this.activate.createDelegate(this);
37630 * Fires when the active tab changes
37631 * @param {Roo.TabPanel} this
37632 * @param {Roo.TabPanelItem} activePanel The new active tab
37636 * @event beforetabchange
37637 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37638 * @param {Roo.TabPanel} this
37639 * @param {Object} e Set cancel to true on this object to cancel the tab change
37640 * @param {Roo.TabPanelItem} tab The tab being changed to
37642 "beforetabchange" : true
37645 Roo.EventManager.onWindowResize(this.onResize, this);
37646 this.cpad = this.el.getPadding("lr");
37647 this.hiddenCount = 0;
37650 // toolbar on the tabbar support...
37651 if (this.toolbar) {
37652 alert("no toolbar support yet");
37653 this.toolbar = false;
37655 var tcfg = this.toolbar;
37656 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37657 this.toolbar = new Roo.Toolbar(tcfg);
37658 if (Roo.isSafari) {
37659 var tbl = tcfg.container.child('table', true);
37660 tbl.setAttribute('width', '100%');
37668 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37671 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37673 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37675 tabPosition : "top",
37677 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37679 currentTabWidth : 0,
37681 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37685 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37689 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37691 preferredTabWidth : 175,
37693 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37695 resizeTabs : false,
37697 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37699 monitorResize : true,
37701 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37706 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37707 * @param {String} id The id of the div to use <b>or create</b>
37708 * @param {String} text The text for the tab
37709 * @param {String} content (optional) Content to put in the TabPanelItem body
37710 * @param {Boolean} closable (optional) True to create a close icon on the tab
37711 * @return {Roo.TabPanelItem} The created TabPanelItem
37713 addTab : function(id, text, content, closable, tpl)
37715 var item = new Roo.bootstrap.panel.TabItem({
37719 closable : closable,
37722 this.addTabItem(item);
37724 item.setContent(content);
37730 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37731 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37732 * @return {Roo.TabPanelItem}
37734 getTab : function(id){
37735 return this.items[id];
37739 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37740 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37742 hideTab : function(id){
37743 var t = this.items[id];
37746 this.hiddenCount++;
37747 this.autoSizeTabs();
37752 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37753 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37755 unhideTab : function(id){
37756 var t = this.items[id];
37758 t.setHidden(false);
37759 this.hiddenCount--;
37760 this.autoSizeTabs();
37765 * Adds an existing {@link Roo.TabPanelItem}.
37766 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37768 addTabItem : function(item){
37769 this.items[item.id] = item;
37770 this.items.push(item);
37771 // if(this.resizeTabs){
37772 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37773 // this.autoSizeTabs();
37775 // item.autoSize();
37780 * Removes a {@link Roo.TabPanelItem}.
37781 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37783 removeTab : function(id){
37784 var items = this.items;
37785 var tab = items[id];
37786 if(!tab) { return; }
37787 var index = items.indexOf(tab);
37788 if(this.active == tab && items.length > 1){
37789 var newTab = this.getNextAvailable(index);
37794 this.stripEl.dom.removeChild(tab.pnode.dom);
37795 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37796 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37798 items.splice(index, 1);
37799 delete this.items[tab.id];
37800 tab.fireEvent("close", tab);
37801 tab.purgeListeners();
37802 this.autoSizeTabs();
37805 getNextAvailable : function(start){
37806 var items = this.items;
37808 // look for a next tab that will slide over to
37809 // replace the one being removed
37810 while(index < items.length){
37811 var item = items[++index];
37812 if(item && !item.isHidden()){
37816 // if one isn't found select the previous tab (on the left)
37819 var item = items[--index];
37820 if(item && !item.isHidden()){
37828 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37829 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37831 disableTab : function(id){
37832 var tab = this.items[id];
37833 if(tab && this.active != tab){
37839 * Enables a {@link Roo.TabPanelItem} that is disabled.
37840 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37842 enableTab : function(id){
37843 var tab = this.items[id];
37848 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37849 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37850 * @return {Roo.TabPanelItem} The TabPanelItem.
37852 activate : function(id){
37853 var tab = this.items[id];
37857 if(tab == this.active || tab.disabled){
37861 this.fireEvent("beforetabchange", this, e, tab);
37862 if(e.cancel !== true && !tab.disabled){
37864 this.active.hide();
37866 this.active = this.items[id];
37867 this.active.show();
37868 this.fireEvent("tabchange", this, this.active);
37874 * Gets the active {@link Roo.TabPanelItem}.
37875 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37877 getActiveTab : function(){
37878 return this.active;
37882 * Updates the tab body element to fit the height of the container element
37883 * for overflow scrolling
37884 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37886 syncHeight : function(targetHeight){
37887 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37888 var bm = this.bodyEl.getMargins();
37889 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37890 this.bodyEl.setHeight(newHeight);
37894 onResize : function(){
37895 if(this.monitorResize){
37896 this.autoSizeTabs();
37901 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37903 beginUpdate : function(){
37904 this.updating = true;
37908 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37910 endUpdate : function(){
37911 this.updating = false;
37912 this.autoSizeTabs();
37916 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37918 autoSizeTabs : function(){
37919 var count = this.items.length;
37920 var vcount = count - this.hiddenCount;
37921 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37924 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37925 var availWidth = Math.floor(w / vcount);
37926 var b = this.stripBody;
37927 if(b.getWidth() > w){
37928 var tabs = this.items;
37929 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37930 if(availWidth < this.minTabWidth){
37931 /*if(!this.sleft){ // incomplete scrolling code
37932 this.createScrollButtons();
37935 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37938 if(this.currentTabWidth < this.preferredTabWidth){
37939 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37945 * Returns the number of tabs in this TabPanel.
37948 getCount : function(){
37949 return this.items.length;
37953 * Resizes all the tabs to the passed width
37954 * @param {Number} The new width
37956 setTabWidth : function(width){
37957 this.currentTabWidth = width;
37958 for(var i = 0, len = this.items.length; i < len; i++) {
37959 if(!this.items[i].isHidden()) {
37960 this.items[i].setWidth(width);
37966 * Destroys this TabPanel
37967 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37969 destroy : function(removeEl){
37970 Roo.EventManager.removeResizeListener(this.onResize, this);
37971 for(var i = 0, len = this.items.length; i < len; i++){
37972 this.items[i].purgeListeners();
37974 if(removeEl === true){
37975 this.el.update("");
37980 createStrip : function(container)
37982 var strip = document.createElement("nav");
37983 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37984 container.appendChild(strip);
37988 createStripList : function(strip)
37990 // div wrapper for retard IE
37991 // returns the "tr" element.
37992 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37993 //'<div class="x-tabs-strip-wrap">'+
37994 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37995 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37996 return strip.firstChild; //.firstChild.firstChild.firstChild;
37998 createBody : function(container)
38000 var body = document.createElement("div");
38001 Roo.id(body, "tab-body");
38002 //Roo.fly(body).addClass("x-tabs-body");
38003 Roo.fly(body).addClass("tab-content");
38004 container.appendChild(body);
38007 createItemBody :function(bodyEl, id){
38008 var body = Roo.getDom(id);
38010 body = document.createElement("div");
38013 //Roo.fly(body).addClass("x-tabs-item-body");
38014 Roo.fly(body).addClass("tab-pane");
38015 bodyEl.insertBefore(body, bodyEl.firstChild);
38019 createStripElements : function(stripEl, text, closable, tpl)
38021 var td = document.createElement("li"); // was td..
38024 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38027 stripEl.appendChild(td);
38029 td.className = "x-tabs-closable";
38030 if(!this.closeTpl){
38031 this.closeTpl = new Roo.Template(
38032 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38033 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38034 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38037 var el = this.closeTpl.overwrite(td, {"text": text});
38038 var close = el.getElementsByTagName("div")[0];
38039 var inner = el.getElementsByTagName("em")[0];
38040 return {"el": el, "close": close, "inner": inner};
38043 // not sure what this is..
38044 // if(!this.tabTpl){
38045 //this.tabTpl = new Roo.Template(
38046 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38047 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38049 // this.tabTpl = new Roo.Template(
38050 // '<a href="#">' +
38051 // '<span unselectable="on"' +
38052 // (this.disableTooltips ? '' : ' title="{text}"') +
38053 // ' >{text}</span></a>'
38059 var template = tpl || this.tabTpl || false;
38063 template = new Roo.Template(
38065 '<span unselectable="on"' +
38066 (this.disableTooltips ? '' : ' title="{text}"') +
38067 ' >{text}</span></a>'
38071 switch (typeof(template)) {
38075 template = new Roo.Template(template);
38081 var el = template.overwrite(td, {"text": text});
38083 var inner = el.getElementsByTagName("span")[0];
38085 return {"el": el, "inner": inner};
38093 * @class Roo.TabPanelItem
38094 * @extends Roo.util.Observable
38095 * Represents an individual item (tab plus body) in a TabPanel.
38096 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38097 * @param {String} id The id of this TabPanelItem
38098 * @param {String} text The text for the tab of this TabPanelItem
38099 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38101 Roo.bootstrap.panel.TabItem = function(config){
38103 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38104 * @type Roo.TabPanel
38106 this.tabPanel = config.panel;
38108 * The id for this TabPanelItem
38111 this.id = config.id;
38113 this.disabled = false;
38115 this.text = config.text;
38117 this.loaded = false;
38118 this.closable = config.closable;
38121 * The body element for this TabPanelItem.
38122 * @type Roo.Element
38124 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38125 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38126 this.bodyEl.setStyle("display", "block");
38127 this.bodyEl.setStyle("zoom", "1");
38128 //this.hideAction();
38130 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38132 this.el = Roo.get(els.el);
38133 this.inner = Roo.get(els.inner, true);
38134 this.textEl = Roo.get(this.el.dom.firstChild, true);
38135 this.pnode = Roo.get(els.el.parentNode, true);
38136 // this.el.on("mousedown", this.onTabMouseDown, this);
38137 this.el.on("click", this.onTabClick, this);
38139 if(config.closable){
38140 var c = Roo.get(els.close, true);
38141 c.dom.title = this.closeText;
38142 c.addClassOnOver("close-over");
38143 c.on("click", this.closeClick, this);
38149 * Fires when this tab becomes the active tab.
38150 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38151 * @param {Roo.TabPanelItem} this
38155 * @event beforeclose
38156 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38157 * @param {Roo.TabPanelItem} this
38158 * @param {Object} e Set cancel to true on this object to cancel the close.
38160 "beforeclose": true,
38163 * Fires when this tab is closed.
38164 * @param {Roo.TabPanelItem} this
38168 * @event deactivate
38169 * Fires when this tab is no longer the active tab.
38170 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38171 * @param {Roo.TabPanelItem} this
38173 "deactivate" : true
38175 this.hidden = false;
38177 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38180 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38182 purgeListeners : function(){
38183 Roo.util.Observable.prototype.purgeListeners.call(this);
38184 this.el.removeAllListeners();
38187 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38190 this.pnode.addClass("active");
38193 this.tabPanel.stripWrap.repaint();
38195 this.fireEvent("activate", this.tabPanel, this);
38199 * Returns true if this tab is the active tab.
38200 * @return {Boolean}
38202 isActive : function(){
38203 return this.tabPanel.getActiveTab() == this;
38207 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38210 this.pnode.removeClass("active");
38212 this.fireEvent("deactivate", this.tabPanel, this);
38215 hideAction : function(){
38216 this.bodyEl.hide();
38217 this.bodyEl.setStyle("position", "absolute");
38218 this.bodyEl.setLeft("-20000px");
38219 this.bodyEl.setTop("-20000px");
38222 showAction : function(){
38223 this.bodyEl.setStyle("position", "relative");
38224 this.bodyEl.setTop("");
38225 this.bodyEl.setLeft("");
38226 this.bodyEl.show();
38230 * Set the tooltip for the tab.
38231 * @param {String} tooltip The tab's tooltip
38233 setTooltip : function(text){
38234 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38235 this.textEl.dom.qtip = text;
38236 this.textEl.dom.removeAttribute('title');
38238 this.textEl.dom.title = text;
38242 onTabClick : function(e){
38243 e.preventDefault();
38244 this.tabPanel.activate(this.id);
38247 onTabMouseDown : function(e){
38248 e.preventDefault();
38249 this.tabPanel.activate(this.id);
38252 getWidth : function(){
38253 return this.inner.getWidth();
38256 setWidth : function(width){
38257 var iwidth = width - this.pnode.getPadding("lr");
38258 this.inner.setWidth(iwidth);
38259 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38260 this.pnode.setWidth(width);
38264 * Show or hide the tab
38265 * @param {Boolean} hidden True to hide or false to show.
38267 setHidden : function(hidden){
38268 this.hidden = hidden;
38269 this.pnode.setStyle("display", hidden ? "none" : "");
38273 * Returns true if this tab is "hidden"
38274 * @return {Boolean}
38276 isHidden : function(){
38277 return this.hidden;
38281 * Returns the text for this tab
38284 getText : function(){
38288 autoSize : function(){
38289 //this.el.beginMeasure();
38290 this.textEl.setWidth(1);
38292 * #2804 [new] Tabs in Roojs
38293 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38295 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38296 //this.el.endMeasure();
38300 * Sets the text for the tab (Note: this also sets the tooltip text)
38301 * @param {String} text The tab's text and tooltip
38303 setText : function(text){
38305 this.textEl.update(text);
38306 this.setTooltip(text);
38307 //if(!this.tabPanel.resizeTabs){
38308 // this.autoSize();
38312 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38314 activate : function(){
38315 this.tabPanel.activate(this.id);
38319 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38321 disable : function(){
38322 if(this.tabPanel.active != this){
38323 this.disabled = true;
38324 this.pnode.addClass("disabled");
38329 * Enables this TabPanelItem if it was previously disabled.
38331 enable : function(){
38332 this.disabled = false;
38333 this.pnode.removeClass("disabled");
38337 * Sets the content for this TabPanelItem.
38338 * @param {String} content The content
38339 * @param {Boolean} loadScripts true to look for and load scripts
38341 setContent : function(content, loadScripts){
38342 this.bodyEl.update(content, loadScripts);
38346 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38347 * @return {Roo.UpdateManager} The UpdateManager
38349 getUpdateManager : function(){
38350 return this.bodyEl.getUpdateManager();
38354 * Set a URL to be used to load the content for this TabPanelItem.
38355 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38356 * @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)
38357 * @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)
38358 * @return {Roo.UpdateManager} The UpdateManager
38360 setUrl : function(url, params, loadOnce){
38361 if(this.refreshDelegate){
38362 this.un('activate', this.refreshDelegate);
38364 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38365 this.on("activate", this.refreshDelegate);
38366 return this.bodyEl.getUpdateManager();
38370 _handleRefresh : function(url, params, loadOnce){
38371 if(!loadOnce || !this.loaded){
38372 var updater = this.bodyEl.getUpdateManager();
38373 updater.update(url, params, this._setLoaded.createDelegate(this));
38378 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38379 * Will fail silently if the setUrl method has not been called.
38380 * This does not activate the panel, just updates its content.
38382 refresh : function(){
38383 if(this.refreshDelegate){
38384 this.loaded = false;
38385 this.refreshDelegate();
38390 _setLoaded : function(){
38391 this.loaded = true;
38395 closeClick : function(e){
38398 this.fireEvent("beforeclose", this, o);
38399 if(o.cancel !== true){
38400 this.tabPanel.removeTab(this.id);
38404 * The text displayed in the tooltip for the close icon.
38407 closeText : "Close this tab"
38410 * This script refer to:
38411 * Title: International Telephone Input
38412 * Author: Jack O'Connor
38413 * Code version: v12.1.12
38414 * Availability: https://github.com/jackocnr/intl-tel-input.git
38417 Roo.bootstrap.PhoneInputData = function() {
38420 "Afghanistan (افغانستان)",
38425 "Albania (Shqipëri)",
38430 "Algeria (الجزائر)",
38455 "Antigua and Barbuda",
38465 "Armenia (Հայաստան)",
38481 "Austria (Österreich)",
38486 "Azerbaijan (Azərbaycan)",
38496 "Bahrain (البحرين)",
38501 "Bangladesh (বাংলাদেশ)",
38511 "Belarus (Беларусь)",
38516 "Belgium (België)",
38546 "Bosnia and Herzegovina (Босна и Херцеговина)",
38561 "British Indian Ocean Territory",
38566 "British Virgin Islands",
38576 "Bulgaria (България)",
38586 "Burundi (Uburundi)",
38591 "Cambodia (កម្ពុជា)",
38596 "Cameroon (Cameroun)",
38605 ["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"]
38608 "Cape Verde (Kabu Verdi)",
38613 "Caribbean Netherlands",
38624 "Central African Republic (République centrafricaine)",
38644 "Christmas Island",
38650 "Cocos (Keeling) Islands",
38661 "Comoros (جزر القمر)",
38666 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38671 "Congo (Republic) (Congo-Brazzaville)",
38691 "Croatia (Hrvatska)",
38712 "Czech Republic (Česká republika)",
38717 "Denmark (Danmark)",
38732 "Dominican Republic (República Dominicana)",
38736 ["809", "829", "849"]
38754 "Equatorial Guinea (Guinea Ecuatorial)",
38774 "Falkland Islands (Islas Malvinas)",
38779 "Faroe Islands (Føroyar)",
38800 "French Guiana (Guyane française)",
38805 "French Polynesia (Polynésie française)",
38820 "Georgia (საქართველო)",
38825 "Germany (Deutschland)",
38845 "Greenland (Kalaallit Nunaat)",
38882 "Guinea-Bissau (Guiné Bissau)",
38907 "Hungary (Magyarország)",
38912 "Iceland (Ísland)",
38932 "Iraq (العراق)",
38948 "Israel (ישראל)",
38975 "Jordan (الأردن)",
38980 "Kazakhstan (Казахстан)",
39001 "Kuwait (الكويت)",
39006 "Kyrgyzstan (Кыргызстан)",
39016 "Latvia (Latvija)",
39021 "Lebanon (لبنان)",
39036 "Libya (ليبيا)",
39046 "Lithuania (Lietuva)",
39061 "Macedonia (FYROM) (Македонија)",
39066 "Madagascar (Madagasikara)",
39096 "Marshall Islands",
39106 "Mauritania (موريتانيا)",
39111 "Mauritius (Moris)",
39132 "Moldova (Republica Moldova)",
39142 "Mongolia (Монгол)",
39147 "Montenegro (Crna Gora)",
39157 "Morocco (المغرب)",
39163 "Mozambique (Moçambique)",
39168 "Myanmar (Burma) (မြန်မာ)",
39173 "Namibia (Namibië)",
39188 "Netherlands (Nederland)",
39193 "New Caledonia (Nouvelle-Calédonie)",
39228 "North Korea (조선 민주주의 인민 공화국)",
39233 "Northern Mariana Islands",
39249 "Pakistan (پاکستان)",
39259 "Palestine (فلسطين)",
39269 "Papua New Guinea",
39311 "Réunion (La Réunion)",
39317 "Romania (România)",
39333 "Saint Barthélemy",
39344 "Saint Kitts and Nevis",
39354 "Saint Martin (Saint-Martin (partie française))",
39360 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39365 "Saint Vincent and the Grenadines",
39380 "São Tomé and Príncipe (São Tomé e Príncipe)",
39385 "Saudi Arabia (المملكة العربية السعودية)",
39390 "Senegal (Sénégal)",
39420 "Slovakia (Slovensko)",
39425 "Slovenia (Slovenija)",
39435 "Somalia (Soomaaliya)",
39445 "South Korea (대한민국)",
39450 "South Sudan (جنوب السودان)",
39460 "Sri Lanka (ශ්රී ලංකාව)",
39465 "Sudan (السودان)",
39475 "Svalbard and Jan Mayen",
39486 "Sweden (Sverige)",
39491 "Switzerland (Schweiz)",
39496 "Syria (سوريا)",
39541 "Trinidad and Tobago",
39546 "Tunisia (تونس)",
39551 "Turkey (Türkiye)",
39561 "Turks and Caicos Islands",
39571 "U.S. Virgin Islands",
39581 "Ukraine (Україна)",
39586 "United Arab Emirates (الإمارات العربية المتحدة)",
39608 "Uzbekistan (Oʻzbekiston)",
39618 "Vatican City (Città del Vaticano)",
39629 "Vietnam (Việt Nam)",
39634 "Wallis and Futuna (Wallis-et-Futuna)",
39639 "Western Sahara (الصحراء الغربية)",
39645 "Yemen (اليمن)",
39669 * This script refer to:
39670 * Title: International Telephone Input
39671 * Author: Jack O'Connor
39672 * Code version: v12.1.12
39673 * Availability: https://github.com/jackocnr/intl-tel-input.git
39677 * @class Roo.bootstrap.PhoneInput
39678 * @extends Roo.bootstrap.TriggerField
39679 * An input with International dial-code selection
39681 * @cfg {String} defaultDialCode default '+852'
39682 * @cfg {Array} preferedCountries default []
39685 * Create a new PhoneInput.
39686 * @param {Object} config Configuration options
39689 Roo.bootstrap.PhoneInput = function(config) {
39690 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39693 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39695 listWidth: undefined,
39697 selectedClass: 'active',
39699 invalidClass : "has-warning",
39701 validClass: 'has-success',
39703 allowed: '0123456789',
39706 * @cfg {String} defaultDialCode The default dial code when initializing the input
39708 defaultDialCode: '+852',
39711 * @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
39713 preferedCountries: false,
39715 getAutoCreate : function()
39717 var data = Roo.bootstrap.PhoneInputData();
39718 var align = this.labelAlign || this.parentLabelAlign();
39721 this.allCountries = [];
39722 this.dialCodeMapping = [];
39724 for (var i = 0; i < data.length; i++) {
39726 this.allCountries[i] = {
39730 priority: c[3] || 0,
39731 areaCodes: c[4] || null
39733 this.dialCodeMapping[c[2]] = {
39736 priority: c[3] || 0,
39737 areaCodes: c[4] || null
39749 cls : 'form-control tel-input',
39750 autocomplete: 'new-password'
39753 var hiddenInput = {
39756 cls: 'hidden-tel-input'
39760 hiddenInput.name = this.name;
39763 if (this.disabled) {
39764 input.disabled = true;
39767 var flag_container = {
39784 cls: this.hasFeedback ? 'has-feedback' : '',
39790 cls: 'dial-code-holder',
39797 cls: 'roo-select2-container input-group',
39804 if (this.fieldLabel.length) {
39807 tooltip: 'This field is required'
39813 cls: 'control-label',
39819 html: this.fieldLabel
39822 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39828 if(this.indicatorpos == 'right') {
39829 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39836 if(align == 'left') {
39844 if(this.labelWidth > 12){
39845 label.style = "width: " + this.labelWidth + 'px';
39847 if(this.labelWidth < 13 && this.labelmd == 0){
39848 this.labelmd = this.labelWidth;
39850 if(this.labellg > 0){
39851 label.cls += ' col-lg-' + this.labellg;
39852 input.cls += ' col-lg-' + (12 - this.labellg);
39854 if(this.labelmd > 0){
39855 label.cls += ' col-md-' + this.labelmd;
39856 container.cls += ' col-md-' + (12 - this.labelmd);
39858 if(this.labelsm > 0){
39859 label.cls += ' col-sm-' + this.labelsm;
39860 container.cls += ' col-sm-' + (12 - this.labelsm);
39862 if(this.labelxs > 0){
39863 label.cls += ' col-xs-' + this.labelxs;
39864 container.cls += ' col-xs-' + (12 - this.labelxs);
39874 var settings = this;
39876 ['xs','sm','md','lg'].map(function(size){
39877 if (settings[size]) {
39878 cfg.cls += ' col-' + size + '-' + settings[size];
39882 this.store = new Roo.data.Store({
39883 proxy : new Roo.data.MemoryProxy({}),
39884 reader : new Roo.data.JsonReader({
39895 'name' : 'dialCode',
39899 'name' : 'priority',
39903 'name' : 'areaCodes',
39910 if(!this.preferedCountries) {
39911 this.preferedCountries = [
39918 var p = this.preferedCountries.reverse();
39921 for (var i = 0; i < p.length; i++) {
39922 for (var j = 0; j < this.allCountries.length; j++) {
39923 if(this.allCountries[j].iso2 == p[i]) {
39924 var t = this.allCountries[j];
39925 this.allCountries.splice(j,1);
39926 this.allCountries.unshift(t);
39932 this.store.proxy.data = {
39934 data: this.allCountries
39940 initEvents : function()
39943 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39945 this.indicator = this.indicatorEl();
39946 this.flag = this.flagEl();
39947 this.dialCodeHolder = this.dialCodeHolderEl();
39949 this.trigger = this.el.select('div.flag-box',true).first();
39950 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39955 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39956 _this.list.setWidth(lw);
39959 this.list.on('mouseover', this.onViewOver, this);
39960 this.list.on('mousemove', this.onViewMove, this);
39961 this.inputEl().on("keyup", this.onKeyUp, this);
39963 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39965 this.view = new Roo.View(this.list, this.tpl, {
39966 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39969 this.view.on('click', this.onViewClick, this);
39970 this.setValue(this.defaultDialCode);
39973 onTriggerClick : function(e)
39975 Roo.log('trigger click');
39980 if(this.isExpanded()){
39982 this.hasFocus = false;
39984 this.store.load({});
39985 this.hasFocus = true;
39990 isExpanded : function()
39992 return this.list.isVisible();
39995 collapse : function()
39997 if(!this.isExpanded()){
40001 Roo.get(document).un('mousedown', this.collapseIf, this);
40002 Roo.get(document).un('mousewheel', this.collapseIf, this);
40003 this.fireEvent('collapse', this);
40007 expand : function()
40011 if(this.isExpanded() || !this.hasFocus){
40015 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40016 this.list.setWidth(lw);
40019 this.restrictHeight();
40021 Roo.get(document).on('mousedown', this.collapseIf, this);
40022 Roo.get(document).on('mousewheel', this.collapseIf, this);
40024 this.fireEvent('expand', this);
40027 restrictHeight : function()
40029 this.list.alignTo(this.inputEl(), this.listAlign);
40030 this.list.alignTo(this.inputEl(), this.listAlign);
40033 onViewOver : function(e, t)
40035 if(this.inKeyMode){
40038 var item = this.view.findItemFromChild(t);
40041 var index = this.view.indexOf(item);
40042 this.select(index, false);
40047 onViewClick : function(view, doFocus, el, e)
40049 var index = this.view.getSelectedIndexes()[0];
40051 var r = this.store.getAt(index);
40054 this.onSelect(r, index);
40056 if(doFocus !== false && !this.blockFocus){
40057 this.inputEl().focus();
40061 onViewMove : function(e, t)
40063 this.inKeyMode = false;
40066 select : function(index, scrollIntoView)
40068 this.selectedIndex = index;
40069 this.view.select(index);
40070 if(scrollIntoView !== false){
40071 var el = this.view.getNode(index);
40073 this.list.scrollChildIntoView(el, false);
40078 createList : function()
40080 this.list = Roo.get(document.body).createChild({
40082 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40083 style: 'display:none'
40086 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40089 collapseIf : function(e)
40091 var in_combo = e.within(this.el);
40092 var in_list = e.within(this.list);
40093 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40095 if (in_combo || in_list || is_list) {
40101 onSelect : function(record, index)
40103 if(this.fireEvent('beforeselect', this, record, index) !== false){
40105 this.setFlagClass(record.data.iso2);
40106 this.setDialCode(record.data.dialCode);
40107 this.hasFocus = false;
40109 this.fireEvent('select', this, record, index);
40113 flagEl : function()
40115 var flag = this.el.select('div.flag',true).first();
40122 dialCodeHolderEl : function()
40124 var d = this.el.select('input.dial-code-holder',true).first();
40131 setDialCode : function(v)
40133 this.dialCodeHolder.dom.value = '+'+v;
40136 setFlagClass : function(n)
40138 this.flag.dom.className = 'flag '+n;
40141 getValue : function()
40143 var v = this.inputEl().getValue();
40144 if(this.dialCodeHolder) {
40145 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40150 setValue : function(v)
40152 var d = this.getDialCode(v);
40154 //invalid dial code
40155 if(v.length == 0 || !d || d.length == 0) {
40157 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40158 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40164 this.setFlagClass(this.dialCodeMapping[d].iso2);
40165 this.setDialCode(d);
40166 this.inputEl().dom.value = v.replace('+'+d,'');
40167 this.hiddenEl().dom.value = this.getValue();
40172 getDialCode : function(v)
40176 if (v.length == 0) {
40177 return this.dialCodeHolder.dom.value;
40181 if (v.charAt(0) != "+") {
40184 var numericChars = "";
40185 for (var i = 1; i < v.length; i++) {
40186 var c = v.charAt(i);
40189 if (this.dialCodeMapping[numericChars]) {
40190 dialCode = v.substr(1, i);
40192 if (numericChars.length == 4) {
40202 this.setValue(this.defaultDialCode);
40206 hiddenEl : function()
40208 return this.el.select('input.hidden-tel-input',true).first();
40211 onKeyUp : function(e){
40213 var k = e.getKey();
40214 var c = e.getCharCode();
40217 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40218 this.allowed.indexOf(String.fromCharCode(c)) === -1
40223 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40226 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40230 this.setValue(this.getValue());
40235 * @class Roo.bootstrap.MoneyField
40236 * @extends Roo.bootstrap.ComboBox
40237 * Bootstrap MoneyField class
40240 * Create a new MoneyField.
40241 * @param {Object} config Configuration options
40244 Roo.bootstrap.MoneyField = function(config) {
40246 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40250 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40253 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40255 allowDecimals : true,
40257 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40259 decimalSeparator : ".",
40261 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40263 decimalPrecision : 0,
40265 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40267 allowNegative : true,
40269 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40273 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40275 minValue : Number.NEGATIVE_INFINITY,
40277 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40279 maxValue : Number.MAX_VALUE,
40281 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40283 minText : "The minimum value for this field is {0}",
40285 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40287 maxText : "The maximum value for this field is {0}",
40289 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40290 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40292 nanText : "{0} is not a valid number",
40294 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40298 * @cfg {String} defaults currency of the MoneyField
40299 * value should be in lkey
40301 defaultCurrency : false,
40303 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40305 thousandsDelimiter : false,
40315 getAutoCreate : function()
40317 var align = this.labelAlign || this.parentLabelAlign();
40329 cls : 'form-control roo-money-amount-input',
40330 autocomplete: 'new-password'
40333 var hiddenInput = {
40337 cls: 'hidden-number-input'
40341 hiddenInput.name = this.name;
40344 if (this.disabled) {
40345 input.disabled = true;
40348 var clg = 12 - this.inputlg;
40349 var cmd = 12 - this.inputmd;
40350 var csm = 12 - this.inputsm;
40351 var cxs = 12 - this.inputxs;
40355 cls : 'row roo-money-field',
40359 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40363 cls: 'roo-select2-container input-group',
40367 cls : 'form-control roo-money-currency-input',
40368 autocomplete: 'new-password',
40370 name : this.currencyName
40374 cls : 'input-group-addon',
40388 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40392 cls: this.hasFeedback ? 'has-feedback' : '',
40403 if (this.fieldLabel.length) {
40406 tooltip: 'This field is required'
40412 cls: 'control-label',
40418 html: this.fieldLabel
40421 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40427 if(this.indicatorpos == 'right') {
40428 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40435 if(align == 'left') {
40443 if(this.labelWidth > 12){
40444 label.style = "width: " + this.labelWidth + 'px';
40446 if(this.labelWidth < 13 && this.labelmd == 0){
40447 this.labelmd = this.labelWidth;
40449 if(this.labellg > 0){
40450 label.cls += ' col-lg-' + this.labellg;
40451 input.cls += ' col-lg-' + (12 - this.labellg);
40453 if(this.labelmd > 0){
40454 label.cls += ' col-md-' + this.labelmd;
40455 container.cls += ' col-md-' + (12 - this.labelmd);
40457 if(this.labelsm > 0){
40458 label.cls += ' col-sm-' + this.labelsm;
40459 container.cls += ' col-sm-' + (12 - this.labelsm);
40461 if(this.labelxs > 0){
40462 label.cls += ' col-xs-' + this.labelxs;
40463 container.cls += ' col-xs-' + (12 - this.labelxs);
40474 var settings = this;
40476 ['xs','sm','md','lg'].map(function(size){
40477 if (settings[size]) {
40478 cfg.cls += ' col-' + size + '-' + settings[size];
40485 initEvents : function()
40487 this.indicator = this.indicatorEl();
40489 this.initCurrencyEvent();
40491 this.initNumberEvent();
40494 initCurrencyEvent : function()
40497 throw "can not find store for combo";
40500 this.store = Roo.factory(this.store, Roo.data);
40501 this.store.parent = this;
40505 this.triggerEl = this.el.select('.input-group-addon', true).first();
40507 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40512 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40513 _this.list.setWidth(lw);
40516 this.list.on('mouseover', this.onViewOver, this);
40517 this.list.on('mousemove', this.onViewMove, this);
40518 this.list.on('scroll', this.onViewScroll, this);
40521 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40524 this.view = new Roo.View(this.list, this.tpl, {
40525 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40528 this.view.on('click', this.onViewClick, this);
40530 this.store.on('beforeload', this.onBeforeLoad, this);
40531 this.store.on('load', this.onLoad, this);
40532 this.store.on('loadexception', this.onLoadException, this);
40534 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40535 "up" : function(e){
40536 this.inKeyMode = true;
40540 "down" : function(e){
40541 if(!this.isExpanded()){
40542 this.onTriggerClick();
40544 this.inKeyMode = true;
40549 "enter" : function(e){
40552 if(this.fireEvent("specialkey", this, e)){
40553 this.onViewClick(false);
40559 "esc" : function(e){
40563 "tab" : function(e){
40566 if(this.fireEvent("specialkey", this, e)){
40567 this.onViewClick(false);
40575 doRelay : function(foo, bar, hname){
40576 if(hname == 'down' || this.scope.isExpanded()){
40577 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40585 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40589 initNumberEvent : function(e)
40591 this.inputEl().on("keydown" , this.fireKey, this);
40592 this.inputEl().on("focus", this.onFocus, this);
40593 this.inputEl().on("blur", this.onBlur, this);
40595 this.inputEl().relayEvent('keyup', this);
40597 if(this.indicator){
40598 this.indicator.addClass('invisible');
40601 this.originalValue = this.getValue();
40603 if(this.validationEvent == 'keyup'){
40604 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40605 this.inputEl().on('keyup', this.filterValidation, this);
40607 else if(this.validationEvent !== false){
40608 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40611 if(this.selectOnFocus){
40612 this.on("focus", this.preFocus, this);
40615 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40616 this.inputEl().on("keypress", this.filterKeys, this);
40618 this.inputEl().relayEvent('keypress', this);
40621 var allowed = "0123456789";
40623 if(this.allowDecimals){
40624 allowed += this.decimalSeparator;
40627 if(this.allowNegative){
40631 if(this.thousandsDelimiter) {
40635 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40637 var keyPress = function(e){
40639 var k = e.getKey();
40641 var c = e.getCharCode();
40644 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40645 allowed.indexOf(String.fromCharCode(c)) === -1
40651 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40655 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40660 this.inputEl().on("keypress", keyPress, this);
40664 onTriggerClick : function(e)
40671 this.loadNext = false;
40673 if(this.isExpanded()){
40678 this.hasFocus = true;
40680 if(this.triggerAction == 'all') {
40681 this.doQuery(this.allQuery, true);
40685 this.doQuery(this.getRawValue());
40688 getCurrency : function()
40690 var v = this.currencyEl().getValue();
40695 restrictHeight : function()
40697 this.list.alignTo(this.currencyEl(), this.listAlign);
40698 this.list.alignTo(this.currencyEl(), this.listAlign);
40701 onViewClick : function(view, doFocus, el, e)
40703 var index = this.view.getSelectedIndexes()[0];
40705 var r = this.store.getAt(index);
40708 this.onSelect(r, index);
40712 onSelect : function(record, index){
40714 if(this.fireEvent('beforeselect', this, record, index) !== false){
40716 this.setFromCurrencyData(index > -1 ? record.data : false);
40720 this.fireEvent('select', this, record, index);
40724 setFromCurrencyData : function(o)
40728 this.lastCurrency = o;
40730 if (this.currencyField) {
40731 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40733 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40736 this.lastSelectionText = currency;
40738 //setting default currency
40739 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40740 this.setCurrency(this.defaultCurrency);
40744 this.setCurrency(currency);
40747 setFromData : function(o)
40751 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40753 this.setFromCurrencyData(c);
40758 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40760 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40763 this.setValue(value);
40767 setCurrency : function(v)
40769 this.currencyValue = v;
40772 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40777 setValue : function(v)
40779 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40785 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40787 this.inputEl().dom.value = (v == '') ? '' :
40788 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40790 if(!this.allowZero && v === '0') {
40791 this.hiddenEl().dom.value = '';
40792 this.inputEl().dom.value = '';
40799 getRawValue : function()
40801 var v = this.inputEl().getValue();
40806 getValue : function()
40808 return this.fixPrecision(this.parseValue(this.getRawValue()));
40811 parseValue : function(value)
40813 if(this.thousandsDelimiter) {
40815 r = new RegExp(",", "g");
40816 value = value.replace(r, "");
40819 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40820 return isNaN(value) ? '' : value;
40824 fixPrecision : function(value)
40826 if(this.thousandsDelimiter) {
40828 r = new RegExp(",", "g");
40829 value = value.replace(r, "");
40832 var nan = isNaN(value);
40834 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40835 return nan ? '' : value;
40837 return parseFloat(value).toFixed(this.decimalPrecision);
40840 decimalPrecisionFcn : function(v)
40842 return Math.floor(v);
40845 validateValue : function(value)
40847 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40851 var num = this.parseValue(value);
40854 this.markInvalid(String.format(this.nanText, value));
40858 if(num < this.minValue){
40859 this.markInvalid(String.format(this.minText, this.minValue));
40863 if(num > this.maxValue){
40864 this.markInvalid(String.format(this.maxText, this.maxValue));
40871 validate : function()
40873 if(this.disabled || this.allowBlank){
40878 var currency = this.getCurrency();
40880 if(this.validateValue(this.getRawValue()) && currency.length){
40885 this.markInvalid();
40889 getName: function()
40894 beforeBlur : function()
40900 var v = this.parseValue(this.getRawValue());
40907 onBlur : function()
40911 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40912 //this.el.removeClass(this.focusClass);
40915 this.hasFocus = false;
40917 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40921 var v = this.getValue();
40923 if(String(v) !== String(this.startValue)){
40924 this.fireEvent('change', this, v, this.startValue);
40927 this.fireEvent("blur", this);
40930 inputEl : function()
40932 return this.el.select('.roo-money-amount-input', true).first();
40935 currencyEl : function()
40937 return this.el.select('.roo-money-currency-input', true).first();
40940 hiddenEl : function()
40942 return this.el.select('input.hidden-number-input',true).first();