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 xy = this.el.getAlignToXY(this.triggerEl, pos || this.defaultAlign);
2251 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2252 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2255 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2260 this.fireEvent("show", this);
2266 this.doFocus.defer(50, this);
2270 doFocus : function(){
2272 this.focusEl.focus();
2277 * Hides this menu and optionally all parent menus
2278 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2280 hide : function(deep)
2283 this.hideMenuItems();
2284 if(this.el && this.isVisible()){
2285 this.fireEvent("beforehide", this);
2286 if(this.activeItem){
2287 this.activeItem.deactivate();
2288 this.activeItem = null;
2290 this.triggerEl.removeClass('open');;
2292 this.fireEvent("hide", this);
2294 if(deep === true && this.parentMenu){
2295 this.parentMenu.hide(true);
2299 onTriggerClick : function(e)
2301 Roo.log('trigger click');
2303 var target = e.getTarget();
2305 Roo.log(target.nodeName.toLowerCase());
2307 if(target.nodeName.toLowerCase() === 'i'){
2313 onTriggerPress : function(e)
2315 Roo.log('trigger press');
2316 //Roo.log(e.getTarget());
2317 // Roo.log(this.triggerEl.dom);
2319 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2320 var pel = Roo.get(e.getTarget());
2321 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2322 Roo.log('is treeview or dropdown?');
2326 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2330 if (this.isVisible()) {
2335 this.show(this.triggerEl, false, false);
2338 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2345 hideMenuItems : function()
2347 Roo.log("hide Menu Items");
2351 //$(backdrop).remove()
2352 this.el.select('.open',true).each(function(aa) {
2354 aa.removeClass('open');
2355 //var parent = getParent($(this))
2356 //var relatedTarget = { relatedTarget: this }
2358 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2359 //if (e.isDefaultPrevented()) return
2360 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363 addxtypeChild : function (tree, cntr) {
2364 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2366 this.menuitems.add(comp);
2378 this.getEl().dom.innerHTML = '';
2379 this.menuitems.clear();
2393 * @class Roo.bootstrap.MenuItem
2394 * @extends Roo.bootstrap.Component
2395 * Bootstrap MenuItem class
2396 * @cfg {String} html the menu label
2397 * @cfg {String} href the link
2398 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2399 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2400 * @cfg {Boolean} active used on sidebars to highlight active itesm
2401 * @cfg {String} fa favicon to show on left of menu item.
2402 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2406 * Create a new MenuItem
2407 * @param {Object} config The config object
2411 Roo.bootstrap.MenuItem = function(config){
2412 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2417 * The raw click event for the entire grid.
2418 * @param {Roo.bootstrap.MenuItem} this
2419 * @param {Roo.EventObject} e
2425 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2429 preventDefault: false,
2430 isContainer : false,
2434 getAutoCreate : function(){
2436 if(this.isContainer){
2439 cls: 'dropdown-menu-item'
2453 if (this.fa !== false) {
2456 cls : 'fa fa-' + this.fa
2465 cls: 'dropdown-menu-item',
2468 if (this.parent().type == 'treeview') {
2469 cfg.cls = 'treeview-menu';
2472 cfg.cls += ' active';
2477 anc.href = this.href || cfg.cn[0].href ;
2478 ctag.html = this.html || cfg.cn[0].html ;
2482 initEvents: function()
2484 if (this.parent().type == 'treeview') {
2485 this.el.select('a').on('click', this.onClick, this);
2489 this.menu.parentType = this.xtype;
2490 this.menu.triggerEl = this.el;
2491 this.menu = this.addxtype(Roo.apply({}, this.menu));
2495 onClick : function(e)
2497 Roo.log('item on click ');
2499 if(this.preventDefault){
2502 //this.parent().hideMenuItems();
2504 this.fireEvent('click', this, e);
2523 * @class Roo.bootstrap.MenuSeparator
2524 * @extends Roo.bootstrap.Component
2525 * Bootstrap MenuSeparator class
2528 * Create a new MenuItem
2529 * @param {Object} config The config object
2533 Roo.bootstrap.MenuSeparator = function(config){
2534 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2539 getAutoCreate : function(){
2558 * @class Roo.bootstrap.Modal
2559 * @extends Roo.bootstrap.Component
2560 * Bootstrap Modal class
2561 * @cfg {String} title Title of dialog
2562 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2563 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2564 * @cfg {Boolean} specificTitle default false
2565 * @cfg {Array} buttons Array of buttons or standard button set..
2566 * @cfg {String} buttonPosition (left|right|center) default right
2567 * @cfg {Boolean} animate default true
2568 * @cfg {Boolean} allow_close default true
2569 * @cfg {Boolean} fitwindow default false
2570 * @cfg {String} size (sm|lg) default empty
2574 * Create a new Modal Dialog
2575 * @param {Object} config The config object
2578 Roo.bootstrap.Modal = function(config){
2579 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2584 * The raw btnclick event for the button
2585 * @param {Roo.EventObject} e
2590 * Fire when dialog resize
2591 * @param {Roo.bootstrap.Modal} this
2592 * @param {Roo.EventObject} e
2596 this.buttons = this.buttons || [];
2599 this.tmpl = Roo.factory(this.tmpl);
2604 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2606 title : 'test dialog',
2616 specificTitle: false,
2618 buttonPosition: 'right',
2637 onRender : function(ct, position)
2639 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2642 var cfg = Roo.apply({}, this.getAutoCreate());
2645 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2647 //if (!cfg.name.length) {
2651 cfg.cls += ' ' + this.cls;
2654 cfg.style = this.style;
2656 this.el = Roo.get(document.body).createChild(cfg, position);
2658 //var type = this.el.dom.type;
2661 if(this.tabIndex !== undefined){
2662 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2665 this.dialogEl = this.el.select('.modal-dialog',true).first();
2666 this.bodyEl = this.el.select('.modal-body',true).first();
2667 this.closeEl = this.el.select('.modal-header .close', true).first();
2668 this.headerEl = this.el.select('.modal-header',true).first();
2669 this.titleEl = this.el.select('.modal-title',true).first();
2670 this.footerEl = this.el.select('.modal-footer',true).first();
2672 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2674 //this.el.addClass("x-dlg-modal");
2676 if (this.buttons.length) {
2677 Roo.each(this.buttons, function(bb) {
2678 var b = Roo.apply({}, bb);
2679 b.xns = b.xns || Roo.bootstrap;
2680 b.xtype = b.xtype || 'Button';
2681 if (typeof(b.listeners) == 'undefined') {
2682 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2685 var btn = Roo.factory(b);
2687 btn.render(this.el.select('.modal-footer div').first());
2691 // render the children.
2694 if(typeof(this.items) != 'undefined'){
2695 var items = this.items;
2698 for(var i =0;i < items.length;i++) {
2699 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2703 this.items = nitems;
2705 // where are these used - they used to be body/close/footer
2709 //this.el.addClass([this.fieldClass, this.cls]);
2713 getAutoCreate : function(){
2718 html : this.html || ''
2723 cls : 'modal-title',
2727 if(this.specificTitle){
2733 if (this.allow_close) {
2745 if(this.size.length){
2746 size = 'modal-' + this.size;
2753 cls: "modal-dialog " + size,
2756 cls : "modal-content",
2759 cls : 'modal-header',
2764 cls : 'modal-footer',
2768 cls: 'btn-' + this.buttonPosition
2785 modal.cls += ' fade';
2791 getChildContainer : function() {
2796 getButtonContainer : function() {
2797 return this.el.select('.modal-footer div',true).first();
2800 initEvents : function()
2802 if (this.allow_close) {
2803 this.closeEl.on('click', this.hide, this);
2805 Roo.EventManager.onWindowResize(this.resize, this, true);
2812 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2813 if (this.fitwindow) {
2814 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2815 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2820 setSize : function(w,h)
2830 if (!this.rendered) {
2834 //this.el.setStyle('display', 'block');
2835 this.el.removeClass('hideing');
2836 this.el.addClass('show');
2838 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2841 this.el.addClass('in');
2844 this.el.addClass('in');
2848 // not sure how we can show data in here..
2850 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2853 Roo.get(document.body).addClass("x-body-masked");
2855 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2856 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2857 this.maskEl.addClass('show');
2861 this.fireEvent('show', this);
2863 // set zindex here - otherwise it appears to be ignored...
2864 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2867 this.items.forEach( function(e) {
2868 e.layout ? e.layout() : false;
2876 if(this.fireEvent("beforehide", this) !== false){
2877 this.maskEl.removeClass('show');
2878 Roo.get(document.body).removeClass("x-body-masked");
2879 this.el.removeClass('in');
2880 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2882 if(this.animate){ // why
2883 this.el.addClass('hideing');
2885 if (!this.el.hasClass('hideing')) {
2886 return; // it's been shown again...
2888 this.el.removeClass('show');
2889 this.el.removeClass('hideing');
2893 this.el.removeClass('show');
2895 this.fireEvent('hide', this);
2898 isVisible : function()
2901 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2905 addButton : function(str, cb)
2909 var b = Roo.apply({}, { html : str } );
2910 b.xns = b.xns || Roo.bootstrap;
2911 b.xtype = b.xtype || 'Button';
2912 if (typeof(b.listeners) == 'undefined') {
2913 b.listeners = { click : cb.createDelegate(this) };
2916 var btn = Roo.factory(b);
2918 btn.render(this.el.select('.modal-footer div').first());
2924 setDefaultButton : function(btn)
2926 //this.el.select('.modal-footer').()
2930 resizeTo: function(w,h)
2934 this.dialogEl.setWidth(w);
2935 if (this.diff === false) {
2936 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2939 this.bodyEl.setHeight(h-this.diff);
2941 this.fireEvent('resize', this);
2944 setContentSize : function(w, h)
2948 onButtonClick: function(btn,e)
2951 this.fireEvent('btnclick', btn.name, e);
2954 * Set the title of the Dialog
2955 * @param {String} str new Title
2957 setTitle: function(str) {
2958 this.titleEl.dom.innerHTML = str;
2961 * Set the body of the Dialog
2962 * @param {String} str new Title
2964 setBody: function(str) {
2965 this.bodyEl.dom.innerHTML = str;
2968 * Set the body of the Dialog using the template
2969 * @param {Obj} data - apply this data to the template and replace the body contents.
2971 applyBody: function(obj)
2974 Roo.log("Error - using apply Body without a template");
2977 this.tmpl.overwrite(this.bodyEl, obj);
2983 Roo.apply(Roo.bootstrap.Modal, {
2985 * Button config that displays a single OK button
2994 * Button config that displays Yes and No buttons
3010 * Button config that displays OK and Cancel buttons
3025 * Button config that displays Yes, No and Cancel buttons
3049 * messagebox - can be used as a replace
3053 * @class Roo.MessageBox
3054 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3058 Roo.Msg.alert('Status', 'Changes saved successfully.');
3060 // Prompt for user data:
3061 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3063 // process text value...
3067 // Show a dialog using config options:
3069 title:'Save Changes?',
3070 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3071 buttons: Roo.Msg.YESNOCANCEL,
3078 Roo.bootstrap.MessageBox = function(){
3079 var dlg, opt, mask, waitTimer;
3080 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3081 var buttons, activeTextEl, bwidth;
3085 var handleButton = function(button){
3087 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3091 var handleHide = function(){
3093 dlg.el.removeClass(opt.cls);
3096 // Roo.TaskMgr.stop(waitTimer);
3097 // waitTimer = null;
3102 var updateButtons = function(b){
3105 buttons["ok"].hide();
3106 buttons["cancel"].hide();
3107 buttons["yes"].hide();
3108 buttons["no"].hide();
3109 //dlg.footer.dom.style.display = 'none';
3112 dlg.footerEl.dom.style.display = '';
3113 for(var k in buttons){
3114 if(typeof buttons[k] != "function"){
3117 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3118 width += buttons[k].el.getWidth()+15;
3128 var handleEsc = function(d, k, e){
3129 if(opt && opt.closable !== false){
3139 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3140 * @return {Roo.BasicDialog} The BasicDialog element
3142 getDialog : function(){
3144 dlg = new Roo.bootstrap.Modal( {
3147 //constraintoviewport:false,
3149 //collapsible : false,
3154 //buttonAlign:"center",
3155 closeClick : function(){
3156 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3159 handleButton("cancel");
3164 dlg.on("hide", handleHide);
3166 //dlg.addKeyListener(27, handleEsc);
3168 this.buttons = buttons;
3169 var bt = this.buttonText;
3170 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3171 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3172 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3173 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3175 bodyEl = dlg.bodyEl.createChild({
3177 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3178 '<textarea class="roo-mb-textarea"></textarea>' +
3179 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3181 msgEl = bodyEl.dom.firstChild;
3182 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3183 textboxEl.enableDisplayMode();
3184 textboxEl.addKeyListener([10,13], function(){
3185 if(dlg.isVisible() && opt && opt.buttons){
3188 }else if(opt.buttons.yes){
3189 handleButton("yes");
3193 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3194 textareaEl.enableDisplayMode();
3195 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3196 progressEl.enableDisplayMode();
3198 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3199 var pf = progressEl.dom.firstChild;
3201 pp = Roo.get(pf.firstChild);
3202 pp.setHeight(pf.offsetHeight);
3210 * Updates the message box body text
3211 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3212 * the XHTML-compliant non-breaking space character '&#160;')
3213 * @return {Roo.MessageBox} This message box
3215 updateText : function(text)
3217 if(!dlg.isVisible() && !opt.width){
3218 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3219 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3221 msgEl.innerHTML = text || ' ';
3223 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3224 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3226 Math.min(opt.width || cw , this.maxWidth),
3227 Math.max(opt.minWidth || this.minWidth, bwidth)
3230 activeTextEl.setWidth(w);
3232 if(dlg.isVisible()){
3233 dlg.fixedcenter = false;
3235 // to big, make it scroll. = But as usual stupid IE does not support
3238 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3239 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3240 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3242 bodyEl.dom.style.height = '';
3243 bodyEl.dom.style.overflowY = '';
3246 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3248 bodyEl.dom.style.overflowX = '';
3251 dlg.setContentSize(w, bodyEl.getHeight());
3252 if(dlg.isVisible()){
3253 dlg.fixedcenter = true;
3259 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3260 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3261 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3262 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3263 * @return {Roo.MessageBox} This message box
3265 updateProgress : function(value, text){
3267 this.updateText(text);
3270 if (pp) { // weird bug on my firefox - for some reason this is not defined
3271 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3272 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3278 * Returns true if the message box is currently displayed
3279 * @return {Boolean} True if the message box is visible, else false
3281 isVisible : function(){
3282 return dlg && dlg.isVisible();
3286 * Hides the message box if it is displayed
3289 if(this.isVisible()){
3295 * Displays a new message box, or reinitializes an existing message box, based on the config options
3296 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3297 * The following config object properties are supported:
3299 Property Type Description
3300 ---------- --------------- ------------------------------------------------------------------------------------
3301 animEl String/Element An id or Element from which the message box should animate as it opens and
3302 closes (defaults to undefined)
3303 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3304 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3305 closable Boolean False to hide the top-right close button (defaults to true). Note that
3306 progress and wait dialogs will ignore this property and always hide the
3307 close button as they can only be closed programmatically.
3308 cls String A custom CSS class to apply to the message box element
3309 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3310 displayed (defaults to 75)
3311 fn Function A callback function to execute after closing the dialog. The arguments to the
3312 function will be btn (the name of the button that was clicked, if applicable,
3313 e.g. "ok"), and text (the value of the active text field, if applicable).
3314 Progress and wait dialogs will ignore this option since they do not respond to
3315 user actions and can only be closed programmatically, so any required function
3316 should be called by the same code after it closes the dialog.
3317 icon String A CSS class that provides a background image to be used as an icon for
3318 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3319 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3320 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3321 modal Boolean False to allow user interaction with the page while the message box is
3322 displayed (defaults to true)
3323 msg String A string that will replace the existing message box body text (defaults
3324 to the XHTML-compliant non-breaking space character ' ')
3325 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3326 progress Boolean True to display a progress bar (defaults to false)
3327 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3328 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3329 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3330 title String The title text
3331 value String The string value to set into the active textbox element if displayed
3332 wait Boolean True to display a progress bar (defaults to false)
3333 width Number The width of the dialog in pixels
3340 msg: 'Please enter your address:',
3342 buttons: Roo.MessageBox.OKCANCEL,
3345 animEl: 'addAddressBtn'
3348 * @param {Object} config Configuration options
3349 * @return {Roo.MessageBox} This message box
3351 show : function(options)
3354 // this causes nightmares if you show one dialog after another
3355 // especially on callbacks..
3357 if(this.isVisible()){
3360 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3361 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3362 Roo.log("New Dialog Message:" + options.msg )
3363 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3364 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3367 var d = this.getDialog();
3369 d.setTitle(opt.title || " ");
3370 d.closeEl.setDisplayed(opt.closable !== false);
3371 activeTextEl = textboxEl;
3372 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3377 textareaEl.setHeight(typeof opt.multiline == "number" ?
3378 opt.multiline : this.defaultTextHeight);
3379 activeTextEl = textareaEl;
3388 progressEl.setDisplayed(opt.progress === true);
3389 this.updateProgress(0);
3390 activeTextEl.dom.value = opt.value || "";
3392 dlg.setDefaultButton(activeTextEl);
3394 var bs = opt.buttons;
3398 }else if(bs && bs.yes){
3399 db = buttons["yes"];
3401 dlg.setDefaultButton(db);
3403 bwidth = updateButtons(opt.buttons);
3404 this.updateText(opt.msg);
3406 d.el.addClass(opt.cls);
3408 d.proxyDrag = opt.proxyDrag === true;
3409 d.modal = opt.modal !== false;
3410 d.mask = opt.modal !== false ? mask : false;
3412 // force it to the end of the z-index stack so it gets a cursor in FF
3413 document.body.appendChild(dlg.el.dom);
3414 d.animateTarget = null;
3415 d.show(options.animEl);
3421 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3422 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3423 * and closing the message box when the process is complete.
3424 * @param {String} title The title bar text
3425 * @param {String} msg The message box body text
3426 * @return {Roo.MessageBox} This message box
3428 progress : function(title, msg){
3435 minWidth: this.minProgressWidth,
3442 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3443 * If a callback function is passed it will be called after the user clicks the button, and the
3444 * id of the button that was clicked will be passed as the only parameter to the callback
3445 * (could also be the top-right close button).
3446 * @param {String} title The title bar text
3447 * @param {String} msg The message box body text
3448 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3449 * @param {Object} scope (optional) The scope of the callback function
3450 * @return {Roo.MessageBox} This message box
3452 alert : function(title, msg, fn, scope)
3467 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3468 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3469 * You are responsible for closing the message box when the process is complete.
3470 * @param {String} msg The message box body text
3471 * @param {String} title (optional) The title bar text
3472 * @return {Roo.MessageBox} This message box
3474 wait : function(msg, title){
3485 waitTimer = Roo.TaskMgr.start({
3487 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3495 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3496 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3497 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3498 * @param {String} title The title bar text
3499 * @param {String} msg The message box body text
3500 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3501 * @param {Object} scope (optional) The scope of the callback function
3502 * @return {Roo.MessageBox} This message box
3504 confirm : function(title, msg, fn, scope){
3508 buttons: this.YESNO,
3517 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3518 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3519 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3520 * (could also be the top-right close button) and the text that was entered will be passed as the two
3521 * parameters to the callback.
3522 * @param {String} title The title bar text
3523 * @param {String} msg The message box body text
3524 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3525 * @param {Object} scope (optional) The scope of the callback function
3526 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3527 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3528 * @return {Roo.MessageBox} This message box
3530 prompt : function(title, msg, fn, scope, multiline){
3534 buttons: this.OKCANCEL,
3539 multiline: multiline,
3546 * Button config that displays a single OK button
3551 * Button config that displays Yes and No buttons
3554 YESNO : {yes:true, no:true},
3556 * Button config that displays OK and Cancel buttons
3559 OKCANCEL : {ok:true, cancel:true},
3561 * Button config that displays Yes, No and Cancel buttons
3564 YESNOCANCEL : {yes:true, no:true, cancel:true},
3567 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3570 defaultTextHeight : 75,
3572 * The maximum width in pixels of the message box (defaults to 600)
3577 * The minimum width in pixels of the message box (defaults to 100)
3582 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3583 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3586 minProgressWidth : 250,
3588 * An object containing the default button text strings that can be overriden for localized language support.
3589 * Supported properties are: ok, cancel, yes and no.
3590 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3603 * Shorthand for {@link Roo.MessageBox}
3605 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3606 Roo.Msg = Roo.Msg || Roo.MessageBox;
3615 * @class Roo.bootstrap.Navbar
3616 * @extends Roo.bootstrap.Component
3617 * Bootstrap Navbar class
3620 * Create a new Navbar
3621 * @param {Object} config The config object
3625 Roo.bootstrap.Navbar = function(config){
3626 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3630 * @event beforetoggle
3631 * Fire before toggle the menu
3632 * @param {Roo.EventObject} e
3634 "beforetoggle" : true
3638 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3647 getAutoCreate : function(){
3650 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3654 initEvents :function ()
3656 //Roo.log(this.el.select('.navbar-toggle',true));
3657 this.el.select('.navbar-toggle',true).on('click', function() {
3658 if(this.fireEvent('beforetoggle', this) !== false){
3659 this.el.select('.navbar-collapse',true).toggleClass('in');
3669 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3671 var size = this.el.getSize();
3672 this.maskEl.setSize(size.width, size.height);
3673 this.maskEl.enableDisplayMode("block");
3682 getChildContainer : function()
3684 if (this.el.select('.collapse').getCount()) {
3685 return this.el.select('.collapse',true).first();
3718 * @class Roo.bootstrap.NavSimplebar
3719 * @extends Roo.bootstrap.Navbar
3720 * Bootstrap Sidebar class
3722 * @cfg {Boolean} inverse is inverted color
3724 * @cfg {String} type (nav | pills | tabs)
3725 * @cfg {Boolean} arrangement stacked | justified
3726 * @cfg {String} align (left | right) alignment
3728 * @cfg {Boolean} main (true|false) main nav bar? default false
3729 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3731 * @cfg {String} tag (header|footer|nav|div) default is nav
3737 * Create a new Sidebar
3738 * @param {Object} config The config object
3742 Roo.bootstrap.NavSimplebar = function(config){
3743 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3746 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3762 getAutoCreate : function(){
3766 tag : this.tag || 'div',
3779 this.type = this.type || 'nav';
3780 if (['tabs','pills'].indexOf(this.type)!==-1) {
3781 cfg.cn[0].cls += ' nav-' + this.type
3785 if (this.type!=='nav') {
3786 Roo.log('nav type must be nav/tabs/pills')
3788 cfg.cn[0].cls += ' navbar-nav'
3794 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3795 cfg.cn[0].cls += ' nav-' + this.arrangement;
3799 if (this.align === 'right') {
3800 cfg.cn[0].cls += ' navbar-right';
3804 cfg.cls += ' navbar-inverse';
3831 * @class Roo.bootstrap.NavHeaderbar
3832 * @extends Roo.bootstrap.NavSimplebar
3833 * Bootstrap Sidebar class
3835 * @cfg {String} brand what is brand
3836 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3837 * @cfg {String} brand_href href of the brand
3838 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3839 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3840 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3841 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3844 * Create a new Sidebar
3845 * @param {Object} config The config object
3849 Roo.bootstrap.NavHeaderbar = function(config){
3850 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3854 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3861 desktopCenter : false,
3864 getAutoCreate : function(){
3867 tag: this.nav || 'nav',
3874 if (this.desktopCenter) {
3875 cn.push({cls : 'container', cn : []});
3882 cls: 'navbar-header',
3887 cls: 'navbar-toggle',
3888 'data-toggle': 'collapse',
3893 html: 'Toggle navigation'
3915 cls: 'collapse navbar-collapse',
3919 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3921 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3922 cfg.cls += ' navbar-' + this.position;
3924 // tag can override this..
3926 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3929 if (this.brand !== '') {
3932 href: this.brand_href ? this.brand_href : '#',
3933 cls: 'navbar-brand',
3941 cfg.cls += ' main-nav';
3949 getHeaderChildContainer : function()
3951 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3952 return this.el.select('.navbar-header',true).first();
3955 return this.getChildContainer();
3959 initEvents : function()
3961 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3963 if (this.autohide) {
3968 Roo.get(document).on('scroll',function(e) {
3969 var ns = Roo.get(document).getScroll().top;
3970 var os = prevScroll;
3974 ft.removeClass('slideDown');
3975 ft.addClass('slideUp');
3978 ft.removeClass('slideUp');
3979 ft.addClass('slideDown');
4000 * @class Roo.bootstrap.NavSidebar
4001 * @extends Roo.bootstrap.Navbar
4002 * Bootstrap Sidebar class
4005 * Create a new Sidebar
4006 * @param {Object} config The config object
4010 Roo.bootstrap.NavSidebar = function(config){
4011 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4016 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4018 getAutoCreate : function(){
4023 cls: 'sidebar sidebar-nav'
4045 * @class Roo.bootstrap.NavGroup
4046 * @extends Roo.bootstrap.Component
4047 * Bootstrap NavGroup class
4048 * @cfg {String} align (left|right)
4049 * @cfg {Boolean} inverse
4050 * @cfg {String} type (nav|pills|tab) default nav
4051 * @cfg {String} navId - reference Id for navbar.
4055 * Create a new nav group
4056 * @param {Object} config The config object
4059 Roo.bootstrap.NavGroup = function(config){
4060 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4063 Roo.bootstrap.NavGroup.register(this);
4067 * Fires when the active item changes
4068 * @param {Roo.bootstrap.NavGroup} this
4069 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4070 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4088 getAutoCreate : function()
4090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4097 if (['tabs','pills'].indexOf(this.type)!==-1) {
4098 cfg.cls += ' nav-' + this.type
4100 if (this.type!=='nav') {
4101 Roo.log('nav type must be nav/tabs/pills')
4103 cfg.cls += ' navbar-nav'
4106 if (this.parent() && this.parent().sidebar) {
4109 cls: 'dashboard-menu sidebar-menu'
4115 if (this.form === true) {
4121 if (this.align === 'right') {
4122 cfg.cls += ' navbar-right';
4124 cfg.cls += ' navbar-left';
4128 if (this.align === 'right') {
4129 cfg.cls += ' navbar-right';
4133 cfg.cls += ' navbar-inverse';
4141 * sets the active Navigation item
4142 * @param {Roo.bootstrap.NavItem} the new current navitem
4144 setActiveItem : function(item)
4147 Roo.each(this.navItems, function(v){
4152 v.setActive(false, true);
4159 item.setActive(true, true);
4160 this.fireEvent('changed', this, item, prev);
4165 * gets the active Navigation item
4166 * @return {Roo.bootstrap.NavItem} the current navitem
4168 getActive : function()
4172 Roo.each(this.navItems, function(v){
4183 indexOfNav : function()
4187 Roo.each(this.navItems, function(v,i){
4198 * adds a Navigation item
4199 * @param {Roo.bootstrap.NavItem} the navitem to add
4201 addItem : function(cfg)
4203 var cn = new Roo.bootstrap.NavItem(cfg);
4205 cn.parentId = this.id;
4206 cn.onRender(this.el, null);
4210 * register a Navigation item
4211 * @param {Roo.bootstrap.NavItem} the navitem to add
4213 register : function(item)
4215 this.navItems.push( item);
4216 item.navId = this.navId;
4221 * clear all the Navigation item
4224 clearAll : function()
4227 this.el.dom.innerHTML = '';
4230 getNavItem: function(tabId)
4233 Roo.each(this.navItems, function(e) {
4234 if (e.tabId == tabId) {
4244 setActiveNext : function()
4246 var i = this.indexOfNav(this.getActive());
4247 if (i > this.navItems.length) {
4250 this.setActiveItem(this.navItems[i+1]);
4252 setActivePrev : function()
4254 var i = this.indexOfNav(this.getActive());
4258 this.setActiveItem(this.navItems[i-1]);
4260 clearWasActive : function(except) {
4261 Roo.each(this.navItems, function(e) {
4262 if (e.tabId != except.tabId && e.was_active) {
4263 e.was_active = false;
4270 getWasActive : function ()
4273 Roo.each(this.navItems, function(e) {
4288 Roo.apply(Roo.bootstrap.NavGroup, {
4292 * register a Navigation Group
4293 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4295 register : function(navgrp)
4297 this.groups[navgrp.navId] = navgrp;
4301 * fetch a Navigation Group based on the navigation ID
4302 * @param {string} the navgroup to add
4303 * @returns {Roo.bootstrap.NavGroup} the navgroup
4305 get: function(navId) {
4306 if (typeof(this.groups[navId]) == 'undefined') {
4308 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4310 return this.groups[navId] ;
4325 * @class Roo.bootstrap.NavItem
4326 * @extends Roo.bootstrap.Component
4327 * Bootstrap Navbar.NavItem class
4328 * @cfg {String} href link to
4329 * @cfg {String} html content of button
4330 * @cfg {String} badge text inside badge
4331 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4332 * @cfg {String} glyphicon name of glyphicon
4333 * @cfg {String} icon name of font awesome icon
4334 * @cfg {Boolean} active Is item active
4335 * @cfg {Boolean} disabled Is item disabled
4337 * @cfg {Boolean} preventDefault (true | false) default false
4338 * @cfg {String} tabId the tab that this item activates.
4339 * @cfg {String} tagtype (a|span) render as a href or span?
4340 * @cfg {Boolean} animateRef (true|false) link to element default false
4343 * Create a new Navbar Item
4344 * @param {Object} config The config object
4346 Roo.bootstrap.NavItem = function(config){
4347 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4352 * The raw click event for the entire grid.
4353 * @param {Roo.EventObject} e
4358 * Fires when the active item active state changes
4359 * @param {Roo.bootstrap.NavItem} this
4360 * @param {boolean} state the new state
4366 * Fires when scroll to element
4367 * @param {Roo.bootstrap.NavItem} this
4368 * @param {Object} options
4369 * @param {Roo.EventObject} e
4377 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4385 preventDefault : false,
4392 getAutoCreate : function(){
4401 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4403 if (this.disabled) {
4404 cfg.cls += ' disabled';
4407 if (this.href || this.html || this.glyphicon || this.icon) {
4411 href : this.href || "#",
4412 html: this.html || ''
4417 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4420 if(this.glyphicon) {
4421 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4426 cfg.cn[0].html += " <span class='caret'></span>";
4430 if (this.badge !== '') {
4432 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4440 initEvents: function()
4442 if (typeof (this.menu) != 'undefined') {
4443 this.menu.parentType = this.xtype;
4444 this.menu.triggerEl = this.el;
4445 this.menu = this.addxtype(Roo.apply({}, this.menu));
4448 this.el.select('a',true).on('click', this.onClick, this);
4450 if(this.tagtype == 'span'){
4451 this.el.select('span',true).on('click', this.onClick, this);
4454 // at this point parent should be available..
4455 this.parent().register(this);
4458 onClick : function(e)
4460 if (e.getTarget('.dropdown-menu-item')) {
4461 // did you click on a menu itemm.... - then don't trigger onclick..
4466 this.preventDefault ||
4469 Roo.log("NavItem - prevent Default?");
4473 if (this.disabled) {
4477 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4478 if (tg && tg.transition) {
4479 Roo.log("waiting for the transitionend");
4485 //Roo.log("fire event clicked");
4486 if(this.fireEvent('click', this, e) === false){
4490 if(this.tagtype == 'span'){
4494 //Roo.log(this.href);
4495 var ael = this.el.select('a',true).first();
4498 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4499 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4500 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4501 return; // ignore... - it's a 'hash' to another page.
4503 Roo.log("NavItem - prevent Default?");
4505 this.scrollToElement(e);
4509 var p = this.parent();
4511 if (['tabs','pills'].indexOf(p.type)!==-1) {
4512 if (typeof(p.setActiveItem) !== 'undefined') {
4513 p.setActiveItem(this);
4517 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4518 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4519 // remove the collapsed menu expand...
4520 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4524 isActive: function () {
4527 setActive : function(state, fire, is_was_active)
4529 if (this.active && !state && this.navId) {
4530 this.was_active = true;
4531 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4533 nv.clearWasActive(this);
4537 this.active = state;
4540 this.el.removeClass('active');
4541 } else if (!this.el.hasClass('active')) {
4542 this.el.addClass('active');
4545 this.fireEvent('changed', this, state);
4548 // show a panel if it's registered and related..
4550 if (!this.navId || !this.tabId || !state || is_was_active) {
4554 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4558 var pan = tg.getPanelByName(this.tabId);
4562 // if we can not flip to new panel - go back to old nav highlight..
4563 if (false == tg.showPanel(pan)) {
4564 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4566 var onav = nv.getWasActive();
4568 onav.setActive(true, false, true);
4577 // this should not be here...
4578 setDisabled : function(state)
4580 this.disabled = state;
4582 this.el.removeClass('disabled');
4583 } else if (!this.el.hasClass('disabled')) {
4584 this.el.addClass('disabled');
4590 * Fetch the element to display the tooltip on.
4591 * @return {Roo.Element} defaults to this.el
4593 tooltipEl : function()
4595 return this.el.select('' + this.tagtype + '', true).first();
4598 scrollToElement : function(e)
4600 var c = document.body;
4603 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4605 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4606 c = document.documentElement;
4609 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4615 var o = target.calcOffsetsTo(c);
4622 this.fireEvent('scrollto', this, options, e);
4624 Roo.get(c).scrollTo('top', options.value, true);
4637 * <span> icon </span>
4638 * <span> text </span>
4639 * <span>badge </span>
4643 * @class Roo.bootstrap.NavSidebarItem
4644 * @extends Roo.bootstrap.NavItem
4645 * Bootstrap Navbar.NavSidebarItem class
4646 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4647 * {Boolean} open is the menu open
4648 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4649 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4650 * {String} buttonSize (sm|md|lg)the extra classes for the button
4651 * {Boolean} showArrow show arrow next to the text (default true)
4653 * Create a new Navbar Button
4654 * @param {Object} config The config object
4656 Roo.bootstrap.NavSidebarItem = function(config){
4657 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4662 * The raw click event for the entire grid.
4663 * @param {Roo.EventObject} e
4668 * Fires when the active item active state changes
4669 * @param {Roo.bootstrap.NavSidebarItem} this
4670 * @param {boolean} state the new state
4678 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4680 badgeWeight : 'default',
4686 buttonWeight : 'default',
4692 getAutoCreate : function(){
4697 href : this.href || '#',
4703 if(this.buttonView){
4706 href : this.href || '#',
4707 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4720 cfg.cls += ' active';
4723 if (this.disabled) {
4724 cfg.cls += ' disabled';
4727 cfg.cls += ' open x-open';
4730 if (this.glyphicon || this.icon) {
4731 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4732 a.cn.push({ tag : 'i', cls : c }) ;
4735 if(!this.buttonView){
4738 html : this.html || ''
4745 if (this.badge !== '') {
4746 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4752 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4755 a.cls += ' dropdown-toggle treeview' ;
4761 initEvents : function()
4763 if (typeof (this.menu) != 'undefined') {
4764 this.menu.parentType = this.xtype;
4765 this.menu.triggerEl = this.el;
4766 this.menu = this.addxtype(Roo.apply({}, this.menu));
4769 this.el.on('click', this.onClick, this);
4771 if(this.badge !== ''){
4772 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4777 onClick : function(e)
4784 if(this.preventDefault){
4788 this.fireEvent('click', this);
4791 disable : function()
4793 this.setDisabled(true);
4798 this.setDisabled(false);
4801 setDisabled : function(state)
4803 if(this.disabled == state){
4807 this.disabled = state;
4810 this.el.addClass('disabled');
4814 this.el.removeClass('disabled');
4819 setActive : function(state)
4821 if(this.active == state){
4825 this.active = state;
4828 this.el.addClass('active');
4832 this.el.removeClass('active');
4837 isActive: function ()
4842 setBadge : function(str)
4848 this.badgeEl.dom.innerHTML = str;
4865 * @class Roo.bootstrap.Row
4866 * @extends Roo.bootstrap.Component
4867 * Bootstrap Row class (contains columns...)
4871 * @param {Object} config The config object
4874 Roo.bootstrap.Row = function(config){
4875 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4878 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4880 getAutoCreate : function(){
4899 * @class Roo.bootstrap.Element
4900 * @extends Roo.bootstrap.Component
4901 * Bootstrap Element class
4902 * @cfg {String} html contents of the element
4903 * @cfg {String} tag tag of the element
4904 * @cfg {String} cls class of the element
4905 * @cfg {Boolean} preventDefault (true|false) default false
4906 * @cfg {Boolean} clickable (true|false) default false
4909 * Create a new Element
4910 * @param {Object} config The config object
4913 Roo.bootstrap.Element = function(config){
4914 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4920 * When a element is chick
4921 * @param {Roo.bootstrap.Element} this
4922 * @param {Roo.EventObject} e
4928 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4933 preventDefault: false,
4936 getAutoCreate : function(){
4940 // cls: this.cls, double assign in parent class Component.js :: onRender
4947 initEvents: function()
4949 Roo.bootstrap.Element.superclass.initEvents.call(this);
4952 this.el.on('click', this.onClick, this);
4957 onClick : function(e)
4959 if(this.preventDefault){
4963 this.fireEvent('click', this, e);
4966 getValue : function()
4968 return this.el.dom.innerHTML;
4971 setValue : function(value)
4973 this.el.dom.innerHTML = value;
4988 * @class Roo.bootstrap.Pagination
4989 * @extends Roo.bootstrap.Component
4990 * Bootstrap Pagination class
4991 * @cfg {String} size xs | sm | md | lg
4992 * @cfg {Boolean} inverse false | true
4995 * Create a new Pagination
4996 * @param {Object} config The config object
4999 Roo.bootstrap.Pagination = function(config){
5000 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5003 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5009 getAutoCreate : function(){
5015 cfg.cls += ' inverse';
5021 cfg.cls += " " + this.cls;
5039 * @class Roo.bootstrap.PaginationItem
5040 * @extends Roo.bootstrap.Component
5041 * Bootstrap PaginationItem class
5042 * @cfg {String} html text
5043 * @cfg {String} href the link
5044 * @cfg {Boolean} preventDefault (true | false) default true
5045 * @cfg {Boolean} active (true | false) default false
5046 * @cfg {Boolean} disabled default false
5050 * Create a new PaginationItem
5051 * @param {Object} config The config object
5055 Roo.bootstrap.PaginationItem = function(config){
5056 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5061 * The raw click event for the entire grid.
5062 * @param {Roo.EventObject} e
5068 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5072 preventDefault: true,
5077 getAutoCreate : function(){
5083 href : this.href ? this.href : '#',
5084 html : this.html ? this.html : ''
5094 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5098 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5104 initEvents: function() {
5106 this.el.on('click', this.onClick, this);
5109 onClick : function(e)
5111 Roo.log('PaginationItem on click ');
5112 if(this.preventDefault){
5120 this.fireEvent('click', this, e);
5136 * @class Roo.bootstrap.Slider
5137 * @extends Roo.bootstrap.Component
5138 * Bootstrap Slider class
5141 * Create a new Slider
5142 * @param {Object} config The config object
5145 Roo.bootstrap.Slider = function(config){
5146 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5149 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5151 getAutoCreate : function(){
5155 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5159 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5171 * Ext JS Library 1.1.1
5172 * Copyright(c) 2006-2007, Ext JS, LLC.
5174 * Originally Released Under LGPL - original licence link has changed is not relivant.
5177 * <script type="text/javascript">
5182 * @class Roo.grid.ColumnModel
5183 * @extends Roo.util.Observable
5184 * This is the default implementation of a ColumnModel used by the Grid. It defines
5185 * the columns in the grid.
5188 var colModel = new Roo.grid.ColumnModel([
5189 {header: "Ticker", width: 60, sortable: true, locked: true},
5190 {header: "Company Name", width: 150, sortable: true},
5191 {header: "Market Cap.", width: 100, sortable: true},
5192 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5193 {header: "Employees", width: 100, sortable: true, resizable: false}
5198 * The config options listed for this class are options which may appear in each
5199 * individual column definition.
5200 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5202 * @param {Object} config An Array of column config objects. See this class's
5203 * config objects for details.
5205 Roo.grid.ColumnModel = function(config){
5207 * The config passed into the constructor
5209 this.config = config;
5212 // if no id, create one
5213 // if the column does not have a dataIndex mapping,
5214 // map it to the order it is in the config
5215 for(var i = 0, len = config.length; i < len; i++){
5217 if(typeof c.dataIndex == "undefined"){
5220 if(typeof c.renderer == "string"){
5221 c.renderer = Roo.util.Format[c.renderer];
5223 if(typeof c.id == "undefined"){
5226 if(c.editor && c.editor.xtype){
5227 c.editor = Roo.factory(c.editor, Roo.grid);
5229 if(c.editor && c.editor.isFormField){
5230 c.editor = new Roo.grid.GridEditor(c.editor);
5232 this.lookup[c.id] = c;
5236 * The width of columns which have no width specified (defaults to 100)
5239 this.defaultWidth = 100;
5242 * Default sortable of columns which have no sortable specified (defaults to false)
5245 this.defaultSortable = false;
5249 * @event widthchange
5250 * Fires when the width of a column changes.
5251 * @param {ColumnModel} this
5252 * @param {Number} columnIndex The column index
5253 * @param {Number} newWidth The new width
5255 "widthchange": true,
5257 * @event headerchange
5258 * Fires when the text of a header changes.
5259 * @param {ColumnModel} this
5260 * @param {Number} columnIndex The column index
5261 * @param {Number} newText The new header text
5263 "headerchange": true,
5265 * @event hiddenchange
5266 * Fires when a column is hidden or "unhidden".
5267 * @param {ColumnModel} this
5268 * @param {Number} columnIndex The column index
5269 * @param {Boolean} hidden true if hidden, false otherwise
5271 "hiddenchange": true,
5273 * @event columnmoved
5274 * Fires when a column is moved.
5275 * @param {ColumnModel} this
5276 * @param {Number} oldIndex
5277 * @param {Number} newIndex
5279 "columnmoved" : true,
5281 * @event columlockchange
5282 * Fires when a column's locked state is changed
5283 * @param {ColumnModel} this
5284 * @param {Number} colIndex
5285 * @param {Boolean} locked true if locked
5287 "columnlockchange" : true
5289 Roo.grid.ColumnModel.superclass.constructor.call(this);
5291 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5293 * @cfg {String} header The header text to display in the Grid view.
5296 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5297 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5298 * specified, the column's index is used as an index into the Record's data Array.
5301 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5302 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5305 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5306 * Defaults to the value of the {@link #defaultSortable} property.
5307 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5310 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5313 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5316 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5319 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5322 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5323 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5324 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5325 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5328 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5331 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5334 * @cfg {String} cursor (Optional)
5337 * @cfg {String} tooltip (Optional)
5340 * @cfg {Number} xs (Optional)
5343 * @cfg {Number} sm (Optional)
5346 * @cfg {Number} md (Optional)
5349 * @cfg {Number} lg (Optional)
5352 * Returns the id of the column at the specified index.
5353 * @param {Number} index The column index
5354 * @return {String} the id
5356 getColumnId : function(index){
5357 return this.config[index].id;
5361 * Returns the column for a specified id.
5362 * @param {String} id The column id
5363 * @return {Object} the column
5365 getColumnById : function(id){
5366 return this.lookup[id];
5371 * Returns the column for a specified dataIndex.
5372 * @param {String} dataIndex The column dataIndex
5373 * @return {Object|Boolean} the column or false if not found
5375 getColumnByDataIndex: function(dataIndex){
5376 var index = this.findColumnIndex(dataIndex);
5377 return index > -1 ? this.config[index] : false;
5381 * Returns the index for a specified column id.
5382 * @param {String} id The column id
5383 * @return {Number} the index, or -1 if not found
5385 getIndexById : function(id){
5386 for(var i = 0, len = this.config.length; i < len; i++){
5387 if(this.config[i].id == id){
5395 * Returns the index for a specified column dataIndex.
5396 * @param {String} dataIndex The column dataIndex
5397 * @return {Number} the index, or -1 if not found
5400 findColumnIndex : function(dataIndex){
5401 for(var i = 0, len = this.config.length; i < len; i++){
5402 if(this.config[i].dataIndex == dataIndex){
5410 moveColumn : function(oldIndex, newIndex){
5411 var c = this.config[oldIndex];
5412 this.config.splice(oldIndex, 1);
5413 this.config.splice(newIndex, 0, c);
5414 this.dataMap = null;
5415 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5418 isLocked : function(colIndex){
5419 return this.config[colIndex].locked === true;
5422 setLocked : function(colIndex, value, suppressEvent){
5423 if(this.isLocked(colIndex) == value){
5426 this.config[colIndex].locked = value;
5428 this.fireEvent("columnlockchange", this, colIndex, value);
5432 getTotalLockedWidth : function(){
5434 for(var i = 0; i < this.config.length; i++){
5435 if(this.isLocked(i) && !this.isHidden(i)){
5436 this.totalWidth += this.getColumnWidth(i);
5442 getLockedCount : function(){
5443 for(var i = 0, len = this.config.length; i < len; i++){
5444 if(!this.isLocked(i)){
5449 return this.config.length;
5453 * Returns the number of columns.
5456 getColumnCount : function(visibleOnly){
5457 if(visibleOnly === true){
5459 for(var i = 0, len = this.config.length; i < len; i++){
5460 if(!this.isHidden(i)){
5466 return this.config.length;
5470 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5471 * @param {Function} fn
5472 * @param {Object} scope (optional)
5473 * @return {Array} result
5475 getColumnsBy : function(fn, scope){
5477 for(var i = 0, len = this.config.length; i < len; i++){
5478 var c = this.config[i];
5479 if(fn.call(scope||this, c, i) === true){
5487 * Returns true if the specified column is sortable.
5488 * @param {Number} col The column index
5491 isSortable : function(col){
5492 if(typeof this.config[col].sortable == "undefined"){
5493 return this.defaultSortable;
5495 return this.config[col].sortable;
5499 * Returns the rendering (formatting) function defined for the column.
5500 * @param {Number} col The column index.
5501 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5503 getRenderer : function(col){
5504 if(!this.config[col].renderer){
5505 return Roo.grid.ColumnModel.defaultRenderer;
5507 return this.config[col].renderer;
5511 * Sets the rendering (formatting) function for a column.
5512 * @param {Number} col The column index
5513 * @param {Function} fn The function to use to process the cell's raw data
5514 * to return HTML markup for the grid view. The render function is called with
5515 * the following parameters:<ul>
5516 * <li>Data value.</li>
5517 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5518 * <li>css A CSS style string to apply to the table cell.</li>
5519 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5520 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5521 * <li>Row index</li>
5522 * <li>Column index</li>
5523 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5525 setRenderer : function(col, fn){
5526 this.config[col].renderer = fn;
5530 * Returns the width for the specified column.
5531 * @param {Number} col The column index
5534 getColumnWidth : function(col){
5535 return this.config[col].width * 1 || this.defaultWidth;
5539 * Sets the width for a column.
5540 * @param {Number} col The column index
5541 * @param {Number} width The new width
5543 setColumnWidth : function(col, width, suppressEvent){
5544 this.config[col].width = width;
5545 this.totalWidth = null;
5547 this.fireEvent("widthchange", this, col, width);
5552 * Returns the total width of all columns.
5553 * @param {Boolean} includeHidden True to include hidden column widths
5556 getTotalWidth : function(includeHidden){
5557 if(!this.totalWidth){
5558 this.totalWidth = 0;
5559 for(var i = 0, len = this.config.length; i < len; i++){
5560 if(includeHidden || !this.isHidden(i)){
5561 this.totalWidth += this.getColumnWidth(i);
5565 return this.totalWidth;
5569 * Returns the header for the specified column.
5570 * @param {Number} col The column index
5573 getColumnHeader : function(col){
5574 return this.config[col].header;
5578 * Sets the header for a column.
5579 * @param {Number} col The column index
5580 * @param {String} header The new header
5582 setColumnHeader : function(col, header){
5583 this.config[col].header = header;
5584 this.fireEvent("headerchange", this, col, header);
5588 * Returns the tooltip for the specified column.
5589 * @param {Number} col The column index
5592 getColumnTooltip : function(col){
5593 return this.config[col].tooltip;
5596 * Sets the tooltip for a column.
5597 * @param {Number} col The column index
5598 * @param {String} tooltip The new tooltip
5600 setColumnTooltip : function(col, tooltip){
5601 this.config[col].tooltip = tooltip;
5605 * Returns the dataIndex for the specified column.
5606 * @param {Number} col The column index
5609 getDataIndex : function(col){
5610 return this.config[col].dataIndex;
5614 * Sets the dataIndex for a column.
5615 * @param {Number} col The column index
5616 * @param {Number} dataIndex The new dataIndex
5618 setDataIndex : function(col, dataIndex){
5619 this.config[col].dataIndex = dataIndex;
5625 * Returns true if the cell is editable.
5626 * @param {Number} colIndex The column index
5627 * @param {Number} rowIndex The row index - this is nto actually used..?
5630 isCellEditable : function(colIndex, rowIndex){
5631 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5635 * Returns the editor defined for the cell/column.
5636 * return false or null to disable editing.
5637 * @param {Number} colIndex The column index
5638 * @param {Number} rowIndex The row index
5641 getCellEditor : function(colIndex, rowIndex){
5642 return this.config[colIndex].editor;
5646 * Sets if a column is editable.
5647 * @param {Number} col The column index
5648 * @param {Boolean} editable True if the column is editable
5650 setEditable : function(col, editable){
5651 this.config[col].editable = editable;
5656 * Returns true if the column is hidden.
5657 * @param {Number} colIndex The column index
5660 isHidden : function(colIndex){
5661 return this.config[colIndex].hidden;
5666 * Returns true if the column width cannot be changed
5668 isFixed : function(colIndex){
5669 return this.config[colIndex].fixed;
5673 * Returns true if the column can be resized
5676 isResizable : function(colIndex){
5677 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5680 * Sets if a column is hidden.
5681 * @param {Number} colIndex The column index
5682 * @param {Boolean} hidden True if the column is hidden
5684 setHidden : function(colIndex, hidden){
5685 this.config[colIndex].hidden = hidden;
5686 this.totalWidth = null;
5687 this.fireEvent("hiddenchange", this, colIndex, hidden);
5691 * Sets the editor for a column.
5692 * @param {Number} col The column index
5693 * @param {Object} editor The editor object
5695 setEditor : function(col, editor){
5696 this.config[col].editor = editor;
5700 Roo.grid.ColumnModel.defaultRenderer = function(value)
5702 if(typeof value == "object") {
5705 if(typeof value == "string" && value.length < 1){
5709 return String.format("{0}", value);
5712 // Alias for backwards compatibility
5713 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5716 * Ext JS Library 1.1.1
5717 * Copyright(c) 2006-2007, Ext JS, LLC.
5719 * Originally Released Under LGPL - original licence link has changed is not relivant.
5722 * <script type="text/javascript">
5726 * @class Roo.LoadMask
5727 * A simple utility class for generically masking elements while loading data. If the element being masked has
5728 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5729 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5730 * element's UpdateManager load indicator and will be destroyed after the initial load.
5732 * Create a new LoadMask
5733 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5734 * @param {Object} config The config object
5736 Roo.LoadMask = function(el, config){
5737 this.el = Roo.get(el);
5738 Roo.apply(this, config);
5740 this.store.on('beforeload', this.onBeforeLoad, this);
5741 this.store.on('load', this.onLoad, this);
5742 this.store.on('loadexception', this.onLoadException, this);
5743 this.removeMask = false;
5745 var um = this.el.getUpdateManager();
5746 um.showLoadIndicator = false; // disable the default indicator
5747 um.on('beforeupdate', this.onBeforeLoad, this);
5748 um.on('update', this.onLoad, this);
5749 um.on('failure', this.onLoad, this);
5750 this.removeMask = true;
5754 Roo.LoadMask.prototype = {
5756 * @cfg {Boolean} removeMask
5757 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5758 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5762 * The text to display in a centered loading message box (defaults to 'Loading...')
5766 * @cfg {String} msgCls
5767 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5769 msgCls : 'x-mask-loading',
5772 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5778 * Disables the mask to prevent it from being displayed
5780 disable : function(){
5781 this.disabled = true;
5785 * Enables the mask so that it can be displayed
5787 enable : function(){
5788 this.disabled = false;
5791 onLoadException : function()
5795 if (typeof(arguments[3]) != 'undefined') {
5796 Roo.MessageBox.alert("Error loading",arguments[3]);
5800 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5801 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5808 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5813 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5817 onBeforeLoad : function(){
5819 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5824 destroy : function(){
5826 this.store.un('beforeload', this.onBeforeLoad, this);
5827 this.store.un('load', this.onLoad, this);
5828 this.store.un('loadexception', this.onLoadException, this);
5830 var um = this.el.getUpdateManager();
5831 um.un('beforeupdate', this.onBeforeLoad, this);
5832 um.un('update', this.onLoad, this);
5833 um.un('failure', this.onLoad, this);
5844 * @class Roo.bootstrap.Table
5845 * @extends Roo.bootstrap.Component
5846 * Bootstrap Table class
5847 * @cfg {String} cls table class
5848 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5849 * @cfg {String} bgcolor Specifies the background color for a table
5850 * @cfg {Number} border Specifies whether the table cells should have borders or not
5851 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5852 * @cfg {Number} cellspacing Specifies the space between cells
5853 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5854 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5855 * @cfg {String} sortable Specifies that the table should be sortable
5856 * @cfg {String} summary Specifies a summary of the content of a table
5857 * @cfg {Number} width Specifies the width of a table
5858 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5860 * @cfg {boolean} striped Should the rows be alternative striped
5861 * @cfg {boolean} bordered Add borders to the table
5862 * @cfg {boolean} hover Add hover highlighting
5863 * @cfg {boolean} condensed Format condensed
5864 * @cfg {boolean} responsive Format condensed
5865 * @cfg {Boolean} loadMask (true|false) default false
5866 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5867 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5868 * @cfg {Boolean} rowSelection (true|false) default false
5869 * @cfg {Boolean} cellSelection (true|false) default false
5870 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5871 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5872 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5876 * Create a new Table
5877 * @param {Object} config The config object
5880 Roo.bootstrap.Table = function(config){
5881 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5886 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5887 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5888 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5889 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5891 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5893 this.sm.grid = this;
5894 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5895 this.sm = this.selModel;
5896 this.sm.xmodule = this.xmodule || false;
5899 if (this.cm && typeof(this.cm.config) == 'undefined') {
5900 this.colModel = new Roo.grid.ColumnModel(this.cm);
5901 this.cm = this.colModel;
5902 this.cm.xmodule = this.xmodule || false;
5905 this.store= Roo.factory(this.store, Roo.data);
5906 this.ds = this.store;
5907 this.ds.xmodule = this.xmodule || false;
5910 if (this.footer && this.store) {
5911 this.footer.dataSource = this.ds;
5912 this.footer = Roo.factory(this.footer);
5919 * Fires when a cell is clicked
5920 * @param {Roo.bootstrap.Table} this
5921 * @param {Roo.Element} el
5922 * @param {Number} rowIndex
5923 * @param {Number} columnIndex
5924 * @param {Roo.EventObject} e
5928 * @event celldblclick
5929 * Fires when a cell is double 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
5936 "celldblclick" : true,
5939 * Fires when a row is clicked
5940 * @param {Roo.bootstrap.Table} this
5941 * @param {Roo.Element} el
5942 * @param {Number} rowIndex
5943 * @param {Roo.EventObject} e
5947 * @event rowdblclick
5948 * Fires when a row is double clicked
5949 * @param {Roo.bootstrap.Table} this
5950 * @param {Roo.Element} el
5951 * @param {Number} rowIndex
5952 * @param {Roo.EventObject} e
5954 "rowdblclick" : true,
5957 * Fires when a mouseover occur
5958 * @param {Roo.bootstrap.Table} this
5959 * @param {Roo.Element} el
5960 * @param {Number} rowIndex
5961 * @param {Number} columnIndex
5962 * @param {Roo.EventObject} e
5967 * Fires when a mouseout 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 row is rendered, so you can change add a style to it.
5978 * @param {Roo.bootstrap.Table} this
5979 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5983 * @event rowsrendered
5984 * Fires when all the rows have been rendered
5985 * @param {Roo.bootstrap.Table} this
5987 'rowsrendered' : true,
5989 * @event contextmenu
5990 * The raw contextmenu event for the entire grid.
5991 * @param {Roo.EventObject} e
5993 "contextmenu" : true,
5995 * @event rowcontextmenu
5996 * Fires when a row is right clicked
5997 * @param {Roo.bootstrap.Table} this
5998 * @param {Number} rowIndex
5999 * @param {Roo.EventObject} e
6001 "rowcontextmenu" : true,
6003 * @event cellcontextmenu
6004 * Fires when a cell is right clicked
6005 * @param {Roo.bootstrap.Table} this
6006 * @param {Number} rowIndex
6007 * @param {Number} cellIndex
6008 * @param {Roo.EventObject} e
6010 "cellcontextmenu" : true,
6012 * @event headercontextmenu
6013 * Fires when a header is right clicked
6014 * @param {Roo.bootstrap.Table} this
6015 * @param {Number} columnIndex
6016 * @param {Roo.EventObject} e
6018 "headercontextmenu" : true
6022 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6048 rowSelection : false,
6049 cellSelection : false,
6052 // Roo.Element - the tbody
6054 // Roo.Element - thead element
6057 container: false, // used by gridpanel...
6063 getAutoCreate : function()
6065 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6072 if (this.scrollBody) {
6073 cfg.cls += ' table-body-fixed';
6076 cfg.cls += ' table-striped';
6080 cfg.cls += ' table-hover';
6082 if (this.bordered) {
6083 cfg.cls += ' table-bordered';
6085 if (this.condensed) {
6086 cfg.cls += ' table-condensed';
6088 if (this.responsive) {
6089 cfg.cls += ' table-responsive';
6093 cfg.cls+= ' ' +this.cls;
6096 // this lot should be simplifed...
6099 cfg.align=this.align;
6102 cfg.bgcolor=this.bgcolor;
6105 cfg.border=this.border;
6107 if (this.cellpadding) {
6108 cfg.cellpadding=this.cellpadding;
6110 if (this.cellspacing) {
6111 cfg.cellspacing=this.cellspacing;
6114 cfg.frame=this.frame;
6117 cfg.rules=this.rules;
6119 if (this.sortable) {
6120 cfg.sortable=this.sortable;
6123 cfg.summary=this.summary;
6126 cfg.width=this.width;
6129 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6132 if(this.store || this.cm){
6133 if(this.headerShow){
6134 cfg.cn.push(this.renderHeader());
6137 cfg.cn.push(this.renderBody());
6139 if(this.footerShow){
6140 cfg.cn.push(this.renderFooter());
6142 // where does this come from?
6143 //cfg.cls+= ' TableGrid';
6146 return { cn : [ cfg ] };
6149 initEvents : function()
6151 if(!this.store || !this.cm){
6154 if (this.selModel) {
6155 this.selModel.initEvents();
6159 //Roo.log('initEvents with ds!!!!');
6161 this.mainBody = this.el.select('tbody', true).first();
6162 this.mainHead = this.el.select('thead', true).first();
6169 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6170 e.on('click', _this.sort, _this);
6173 this.mainBody.on("click", this.onClick, this);
6174 this.mainBody.on("dblclick", this.onDblClick, this);
6176 // why is this done????? = it breaks dialogs??
6177 //this.parent().el.setStyle('position', 'relative');
6181 this.footer.parentId = this.id;
6182 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6185 this.el.select('tfoot tr td').first().addClass('hide');
6189 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6191 this.store.on('load', this.onLoad, this);
6192 this.store.on('beforeload', this.onBeforeLoad, this);
6193 this.store.on('update', this.onUpdate, this);
6194 this.store.on('add', this.onAdd, this);
6195 this.store.on("clear", this.clear, this);
6197 this.el.on("contextmenu", this.onContextMenu, this);
6199 this.mainBody.on('scroll', this.onBodyScroll, this);
6201 this.cm.on("headerchange", this.onHeaderChange, this);
6203 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6207 onContextMenu : function(e, t)
6209 this.processEvent("contextmenu", e);
6212 processEvent : function(name, e)
6214 if (name != 'touchstart' ) {
6215 this.fireEvent(name, e);
6218 var t = e.getTarget();
6220 var cell = Roo.get(t);
6226 if(cell.findParent('tfoot', false, true)){
6230 if(cell.findParent('thead', false, true)){
6232 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6233 cell = Roo.get(t).findParent('th', false, true);
6235 Roo.log("failed to find th in thead?");
6236 Roo.log(e.getTarget());
6241 var cellIndex = cell.dom.cellIndex;
6243 var ename = name == 'touchstart' ? 'click' : name;
6244 this.fireEvent("header" + ename, this, cellIndex, e);
6249 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6250 cell = Roo.get(t).findParent('td', false, true);
6252 Roo.log("failed to find th in tbody?");
6253 Roo.log(e.getTarget());
6258 var row = cell.findParent('tr', false, true);
6259 var cellIndex = cell.dom.cellIndex;
6260 var rowIndex = row.dom.rowIndex - 1;
6264 this.fireEvent("row" + name, this, rowIndex, e);
6268 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6274 onMouseover : function(e, el)
6276 var cell = Roo.get(el);
6282 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6283 cell = cell.findParent('td', false, true);
6286 var row = cell.findParent('tr', false, true);
6287 var cellIndex = cell.dom.cellIndex;
6288 var rowIndex = row.dom.rowIndex - 1; // start from 0
6290 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6294 onMouseout : function(e, el)
6296 var cell = Roo.get(el);
6302 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6303 cell = cell.findParent('td', false, true);
6306 var row = cell.findParent('tr', false, true);
6307 var cellIndex = cell.dom.cellIndex;
6308 var rowIndex = row.dom.rowIndex - 1; // start from 0
6310 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6314 onClick : function(e, el)
6316 var cell = Roo.get(el);
6318 if(!cell || (!this.cellSelection && !this.rowSelection)){
6322 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6323 cell = cell.findParent('td', false, true);
6326 if(!cell || typeof(cell) == 'undefined'){
6330 var row = cell.findParent('tr', false, true);
6332 if(!row || typeof(row) == 'undefined'){
6336 var cellIndex = cell.dom.cellIndex;
6337 var rowIndex = this.getRowIndex(row);
6339 // why??? - should these not be based on SelectionModel?
6340 if(this.cellSelection){
6341 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6344 if(this.rowSelection){
6345 this.fireEvent('rowclick', this, row, rowIndex, e);
6351 onDblClick : function(e,el)
6353 var cell = Roo.get(el);
6355 if(!cell || (!this.cellSelection && !this.rowSelection)){
6359 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6360 cell = cell.findParent('td', false, true);
6363 if(!cell || typeof(cell) == 'undefined'){
6367 var row = cell.findParent('tr', false, true);
6369 if(!row || typeof(row) == 'undefined'){
6373 var cellIndex = cell.dom.cellIndex;
6374 var rowIndex = this.getRowIndex(row);
6376 if(this.cellSelection){
6377 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6380 if(this.rowSelection){
6381 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6385 sort : function(e,el)
6387 var col = Roo.get(el);
6389 if(!col.hasClass('sortable')){
6393 var sort = col.attr('sort');
6396 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6400 this.store.sortInfo = {field : sort, direction : dir};
6403 Roo.log("calling footer first");
6404 this.footer.onClick('first');
6407 this.store.load({ params : { start : 0 } });
6411 renderHeader : function()
6419 this.totalWidth = 0;
6421 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6423 var config = cm.config[i];
6427 cls : 'x-hcol-' + i,
6429 html: cm.getColumnHeader(i)
6434 if(typeof(config.sortable) != 'undefined' && config.sortable){
6436 c.html = '<i class="glyphicon"></i>' + c.html;
6439 if(typeof(config.lgHeader) != 'undefined'){
6440 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6443 if(typeof(config.mdHeader) != 'undefined'){
6444 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6447 if(typeof(config.smHeader) != 'undefined'){
6448 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6451 if(typeof(config.xsHeader) != 'undefined'){
6452 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6459 if(typeof(config.tooltip) != 'undefined'){
6460 c.tooltip = config.tooltip;
6463 if(typeof(config.colspan) != 'undefined'){
6464 c.colspan = config.colspan;
6467 if(typeof(config.hidden) != 'undefined' && config.hidden){
6468 c.style += ' display:none;';
6471 if(typeof(config.dataIndex) != 'undefined'){
6472 c.sort = config.dataIndex;
6477 if(typeof(config.align) != 'undefined' && config.align.length){
6478 c.style += ' text-align:' + config.align + ';';
6481 if(typeof(config.width) != 'undefined'){
6482 c.style += ' width:' + config.width + 'px;';
6483 this.totalWidth += config.width;
6485 this.totalWidth += 100; // assume minimum of 100 per column?
6488 if(typeof(config.cls) != 'undefined'){
6489 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6492 ['xs','sm','md','lg'].map(function(size){
6494 if(typeof(config[size]) == 'undefined'){
6498 if (!config[size]) { // 0 = hidden
6499 c.cls += ' hidden-' + size;
6503 c.cls += ' col-' + size + '-' + config[size];
6513 renderBody : function()
6523 colspan : this.cm.getColumnCount()
6533 renderFooter : function()
6543 colspan : this.cm.getColumnCount()
6557 // Roo.log('ds onload');
6562 var ds = this.store;
6564 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6565 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6566 if (_this.store.sortInfo) {
6568 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6569 e.select('i', true).addClass(['glyphicon-arrow-up']);
6572 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6573 e.select('i', true).addClass(['glyphicon-arrow-down']);
6578 var tbody = this.mainBody;
6580 if(ds.getCount() > 0){
6581 ds.data.each(function(d,rowIndex){
6582 var row = this.renderRow(cm, ds, rowIndex);
6584 tbody.createChild(row);
6588 if(row.cellObjects.length){
6589 Roo.each(row.cellObjects, function(r){
6590 _this.renderCellObject(r);
6597 Roo.each(this.el.select('tbody td', true).elements, function(e){
6598 e.on('mouseover', _this.onMouseover, _this);
6601 Roo.each(this.el.select('tbody td', true).elements, function(e){
6602 e.on('mouseout', _this.onMouseout, _this);
6604 this.fireEvent('rowsrendered', this);
6605 //if(this.loadMask){
6606 // this.maskEl.hide();
6613 onUpdate : function(ds,record)
6615 this.refreshRow(record);
6619 onRemove : function(ds, record, index, isUpdate){
6620 if(isUpdate !== true){
6621 this.fireEvent("beforerowremoved", this, index, record);
6623 var bt = this.mainBody.dom;
6625 var rows = this.el.select('tbody > tr', true).elements;
6627 if(typeof(rows[index]) != 'undefined'){
6628 bt.removeChild(rows[index].dom);
6631 // if(bt.rows[index]){
6632 // bt.removeChild(bt.rows[index]);
6635 if(isUpdate !== true){
6636 //this.stripeRows(index);
6637 //this.syncRowHeights(index, index);
6639 this.fireEvent("rowremoved", this, index, record);
6643 onAdd : function(ds, records, rowIndex)
6645 //Roo.log('on Add called');
6646 // - note this does not handle multiple adding very well..
6647 var bt = this.mainBody.dom;
6648 for (var i =0 ; i < records.length;i++) {
6649 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6650 //Roo.log(records[i]);
6651 //Roo.log(this.store.getAt(rowIndex+i));
6652 this.insertRow(this.store, rowIndex + i, false);
6659 refreshRow : function(record){
6660 var ds = this.store, index;
6661 if(typeof record == 'number'){
6663 record = ds.getAt(index);
6665 index = ds.indexOf(record);
6667 this.insertRow(ds, index, true);
6669 this.onRemove(ds, record, index+1, true);
6671 //this.syncRowHeights(index, index);
6673 this.fireEvent("rowupdated", this, index, record);
6676 insertRow : function(dm, rowIndex, isUpdate){
6679 this.fireEvent("beforerowsinserted", this, rowIndex);
6681 //var s = this.getScrollState();
6682 var row = this.renderRow(this.cm, this.store, rowIndex);
6683 // insert before rowIndex..
6684 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6688 if(row.cellObjects.length){
6689 Roo.each(row.cellObjects, function(r){
6690 _this.renderCellObject(r);
6695 this.fireEvent("rowsinserted", this, rowIndex);
6696 //this.syncRowHeights(firstRow, lastRow);
6697 //this.stripeRows(firstRow);
6704 getRowDom : function(rowIndex)
6706 var rows = this.el.select('tbody > tr', true).elements;
6708 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6711 // returns the object tree for a tr..
6714 renderRow : function(cm, ds, rowIndex)
6716 var d = ds.getAt(rowIndex);
6720 cls : 'x-row-' + rowIndex,
6724 var cellObjects = [];
6726 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6727 var config = cm.config[i];
6729 var renderer = cm.getRenderer(i);
6733 if(typeof(renderer) !== 'undefined'){
6734 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6736 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6737 // and are rendered into the cells after the row is rendered - using the id for the element.
6739 if(typeof(value) === 'object'){
6749 rowIndex : rowIndex,
6754 this.fireEvent('rowclass', this, rowcfg);
6758 cls : rowcfg.rowClass + ' x-col-' + i,
6760 html: (typeof(value) === 'object') ? '' : value
6767 if(typeof(config.colspan) != 'undefined'){
6768 td.colspan = config.colspan;
6771 if(typeof(config.hidden) != 'undefined' && config.hidden){
6772 td.style += ' display:none;';
6775 if(typeof(config.align) != 'undefined' && config.align.length){
6776 td.style += ' text-align:' + config.align + ';';
6779 if(typeof(config.width) != 'undefined'){
6780 td.style += ' width:' + config.width + 'px;';
6783 if(typeof(config.cursor) != 'undefined'){
6784 td.style += ' cursor:' + config.cursor + ';';
6787 if(typeof(config.cls) != 'undefined'){
6788 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6791 ['xs','sm','md','lg'].map(function(size){
6793 if(typeof(config[size]) == 'undefined'){
6797 if (!config[size]) { // 0 = hidden
6798 td.cls += ' hidden-' + size;
6802 td.cls += ' col-' + size + '-' + config[size];
6810 row.cellObjects = cellObjects;
6818 onBeforeLoad : function()
6820 //Roo.log('ds onBeforeLoad');
6824 //if(this.loadMask){
6825 // this.maskEl.show();
6833 this.el.select('tbody', true).first().dom.innerHTML = '';
6836 * Show or hide a row.
6837 * @param {Number} rowIndex to show or hide
6838 * @param {Boolean} state hide
6840 setRowVisibility : function(rowIndex, state)
6842 var bt = this.mainBody.dom;
6844 var rows = this.el.select('tbody > tr', true).elements;
6846 if(typeof(rows[rowIndex]) == 'undefined'){
6849 rows[rowIndex].dom.style.display = state ? '' : 'none';
6853 getSelectionModel : function(){
6855 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6857 return this.selModel;
6860 * Render the Roo.bootstrap object from renderder
6862 renderCellObject : function(r)
6866 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6868 var t = r.cfg.render(r.container);
6871 Roo.each(r.cfg.cn, function(c){
6873 container: t.getChildContainer(),
6876 _this.renderCellObject(child);
6881 getRowIndex : function(row)
6885 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6896 * Returns the grid's underlying element = used by panel.Grid
6897 * @return {Element} The element
6899 getGridEl : function(){
6903 * Forces a resize - used by panel.Grid
6904 * @return {Element} The element
6906 autoSize : function()
6908 //var ctr = Roo.get(this.container.dom.parentElement);
6909 var ctr = Roo.get(this.el.dom);
6911 var thd = this.getGridEl().select('thead',true).first();
6912 var tbd = this.getGridEl().select('tbody', true).first();
6913 var tfd = this.getGridEl().select('tfoot', true).first();
6915 var cw = ctr.getWidth();
6919 tbd.setSize(ctr.getWidth(),
6920 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6922 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6925 cw = Math.max(cw, this.totalWidth);
6926 this.getGridEl().select('tr',true).setWidth(cw);
6927 // resize 'expandable coloumn?
6929 return; // we doe not have a view in this design..
6932 onBodyScroll: function()
6934 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6936 this.mainHead.setStyle({
6937 'position' : 'relative',
6938 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6944 var scrollHeight = this.mainBody.dom.scrollHeight;
6946 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6948 var height = this.mainBody.getHeight();
6950 if(scrollHeight - height == scrollTop) {
6952 var total = this.ds.getTotalCount();
6954 if(this.footer.cursor + this.footer.pageSize < total){
6956 this.footer.ds.load({
6958 start : this.footer.cursor + this.footer.pageSize,
6959 limit : this.footer.pageSize
6969 onHeaderChange : function()
6971 var header = this.renderHeader();
6972 var table = this.el.select('table', true).first();
6974 this.mainHead.remove();
6975 this.mainHead = table.createChild(header, this.mainBody, false);
6978 onHiddenChange : function(colModel, colIndex, hidden)
6980 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6981 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6983 this.CSS.updateRule(thSelector, "display", "");
6984 this.CSS.updateRule(tdSelector, "display", "");
6987 this.CSS.updateRule(thSelector, "display", "none");
6988 this.CSS.updateRule(tdSelector, "display", "none");
6991 this.onHeaderChange();
7008 * @class Roo.bootstrap.TableCell
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap TableCell class
7011 * @cfg {String} html cell contain text
7012 * @cfg {String} cls cell class
7013 * @cfg {String} tag cell tag (td|th) default td
7014 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7015 * @cfg {String} align Aligns the content in a cell
7016 * @cfg {String} axis Categorizes cells
7017 * @cfg {String} bgcolor Specifies the background color of a cell
7018 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7019 * @cfg {Number} colspan Specifies the number of columns a cell should span
7020 * @cfg {String} headers Specifies one or more header cells a cell is related to
7021 * @cfg {Number} height Sets the height of a cell
7022 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7023 * @cfg {Number} rowspan Sets the number of rows a cell should span
7024 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7025 * @cfg {String} valign Vertical aligns the content in a cell
7026 * @cfg {Number} width Specifies the width of a cell
7029 * Create a new TableCell
7030 * @param {Object} config The config object
7033 Roo.bootstrap.TableCell = function(config){
7034 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7037 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7057 getAutoCreate : function(){
7058 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7078 cfg.align=this.align
7084 cfg.bgcolor=this.bgcolor
7087 cfg.charoff=this.charoff
7090 cfg.colspan=this.colspan
7093 cfg.headers=this.headers
7096 cfg.height=this.height
7099 cfg.nowrap=this.nowrap
7102 cfg.rowspan=this.rowspan
7105 cfg.scope=this.scope
7108 cfg.valign=this.valign
7111 cfg.width=this.width
7130 * @class Roo.bootstrap.TableRow
7131 * @extends Roo.bootstrap.Component
7132 * Bootstrap TableRow class
7133 * @cfg {String} cls row class
7134 * @cfg {String} align Aligns the content in a table row
7135 * @cfg {String} bgcolor Specifies a background color for a table row
7136 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7137 * @cfg {String} valign Vertical aligns the content in a table row
7140 * Create a new TableRow
7141 * @param {Object} config The config object
7144 Roo.bootstrap.TableRow = function(config){
7145 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7148 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7156 getAutoCreate : function(){
7157 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7167 cfg.align = this.align;
7170 cfg.bgcolor = this.bgcolor;
7173 cfg.charoff = this.charoff;
7176 cfg.valign = this.valign;
7194 * @class Roo.bootstrap.TableBody
7195 * @extends Roo.bootstrap.Component
7196 * Bootstrap TableBody class
7197 * @cfg {String} cls element class
7198 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7199 * @cfg {String} align Aligns the content inside the element
7200 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7201 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7204 * Create a new TableBody
7205 * @param {Object} config The config object
7208 Roo.bootstrap.TableBody = function(config){
7209 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7212 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7220 getAutoCreate : function(){
7221 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7235 cfg.align = this.align;
7238 cfg.charoff = this.charoff;
7241 cfg.valign = this.valign;
7248 // initEvents : function()
7255 // this.store = Roo.factory(this.store, Roo.data);
7256 // this.store.on('load', this.onLoad, this);
7258 // this.store.load();
7262 // onLoad: function ()
7264 // this.fireEvent('load', this);
7274 * Ext JS Library 1.1.1
7275 * Copyright(c) 2006-2007, Ext JS, LLC.
7277 * Originally Released Under LGPL - original licence link has changed is not relivant.
7280 * <script type="text/javascript">
7283 // as we use this in bootstrap.
7284 Roo.namespace('Roo.form');
7286 * @class Roo.form.Action
7287 * Internal Class used to handle form actions
7289 * @param {Roo.form.BasicForm} el The form element or its id
7290 * @param {Object} config Configuration options
7295 // define the action interface
7296 Roo.form.Action = function(form, options){
7298 this.options = options || {};
7301 * Client Validation Failed
7304 Roo.form.Action.CLIENT_INVALID = 'client';
7306 * Server Validation Failed
7309 Roo.form.Action.SERVER_INVALID = 'server';
7311 * Connect to Server Failed
7314 Roo.form.Action.CONNECT_FAILURE = 'connect';
7316 * Reading Data from Server Failed
7319 Roo.form.Action.LOAD_FAILURE = 'load';
7321 Roo.form.Action.prototype = {
7323 failureType : undefined,
7324 response : undefined,
7328 run : function(options){
7333 success : function(response){
7338 handleResponse : function(response){
7342 // default connection failure
7343 failure : function(response){
7345 this.response = response;
7346 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7347 this.form.afterAction(this, false);
7350 processResponse : function(response){
7351 this.response = response;
7352 if(!response.responseText){
7355 this.result = this.handleResponse(response);
7359 // utility functions used internally
7360 getUrl : function(appendParams){
7361 var url = this.options.url || this.form.url || this.form.el.dom.action;
7363 var p = this.getParams();
7365 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7371 getMethod : function(){
7372 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7375 getParams : function(){
7376 var bp = this.form.baseParams;
7377 var p = this.options.params;
7379 if(typeof p == "object"){
7380 p = Roo.urlEncode(Roo.applyIf(p, bp));
7381 }else if(typeof p == 'string' && bp){
7382 p += '&' + Roo.urlEncode(bp);
7385 p = Roo.urlEncode(bp);
7390 createCallback : function(){
7392 success: this.success,
7393 failure: this.failure,
7395 timeout: (this.form.timeout*1000),
7396 upload: this.form.fileUpload ? this.success : undefined
7401 Roo.form.Action.Submit = function(form, options){
7402 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7405 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7408 haveProgress : false,
7409 uploadComplete : false,
7411 // uploadProgress indicator.
7412 uploadProgress : function()
7414 if (!this.form.progressUrl) {
7418 if (!this.haveProgress) {
7419 Roo.MessageBox.progress("Uploading", "Uploading");
7421 if (this.uploadComplete) {
7422 Roo.MessageBox.hide();
7426 this.haveProgress = true;
7428 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7430 var c = new Roo.data.Connection();
7432 url : this.form.progressUrl,
7437 success : function(req){
7438 //console.log(data);
7442 rdata = Roo.decode(req.responseText)
7444 Roo.log("Invalid data from server..");
7448 if (!rdata || !rdata.success) {
7450 Roo.MessageBox.alert(Roo.encode(rdata));
7453 var data = rdata.data;
7455 if (this.uploadComplete) {
7456 Roo.MessageBox.hide();
7461 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7462 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7465 this.uploadProgress.defer(2000,this);
7468 failure: function(data) {
7469 Roo.log('progress url failed ');
7480 // run get Values on the form, so it syncs any secondary forms.
7481 this.form.getValues();
7483 var o = this.options;
7484 var method = this.getMethod();
7485 var isPost = method == 'POST';
7486 if(o.clientValidation === false || this.form.isValid()){
7488 if (this.form.progressUrl) {
7489 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7490 (new Date() * 1) + '' + Math.random());
7495 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7496 form:this.form.el.dom,
7497 url:this.getUrl(!isPost),
7499 params:isPost ? this.getParams() : null,
7500 isUpload: this.form.fileUpload
7503 this.uploadProgress();
7505 }else if (o.clientValidation !== false){ // client validation failed
7506 this.failureType = Roo.form.Action.CLIENT_INVALID;
7507 this.form.afterAction(this, false);
7511 success : function(response)
7513 this.uploadComplete= true;
7514 if (this.haveProgress) {
7515 Roo.MessageBox.hide();
7519 var result = this.processResponse(response);
7520 if(result === true || result.success){
7521 this.form.afterAction(this, true);
7525 this.form.markInvalid(result.errors);
7526 this.failureType = Roo.form.Action.SERVER_INVALID;
7528 this.form.afterAction(this, false);
7530 failure : function(response)
7532 this.uploadComplete= true;
7533 if (this.haveProgress) {
7534 Roo.MessageBox.hide();
7537 this.response = response;
7538 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7539 this.form.afterAction(this, false);
7542 handleResponse : function(response){
7543 if(this.form.errorReader){
7544 var rs = this.form.errorReader.read(response);
7547 for(var i = 0, len = rs.records.length; i < len; i++) {
7548 var r = rs.records[i];
7552 if(errors.length < 1){
7556 success : rs.success,
7562 ret = Roo.decode(response.responseText);
7566 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7576 Roo.form.Action.Load = function(form, options){
7577 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7578 this.reader = this.form.reader;
7581 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7586 Roo.Ajax.request(Roo.apply(
7587 this.createCallback(), {
7588 method:this.getMethod(),
7589 url:this.getUrl(false),
7590 params:this.getParams()
7594 success : function(response){
7596 var result = this.processResponse(response);
7597 if(result === true || !result.success || !result.data){
7598 this.failureType = Roo.form.Action.LOAD_FAILURE;
7599 this.form.afterAction(this, false);
7602 this.form.clearInvalid();
7603 this.form.setValues(result.data);
7604 this.form.afterAction(this, true);
7607 handleResponse : function(response){
7608 if(this.form.reader){
7609 var rs = this.form.reader.read(response);
7610 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7612 success : rs.success,
7616 return Roo.decode(response.responseText);
7620 Roo.form.Action.ACTION_TYPES = {
7621 'load' : Roo.form.Action.Load,
7622 'submit' : Roo.form.Action.Submit
7631 * @class Roo.bootstrap.Form
7632 * @extends Roo.bootstrap.Component
7633 * Bootstrap Form class
7634 * @cfg {String} method GET | POST (default POST)
7635 * @cfg {String} labelAlign top | left (default top)
7636 * @cfg {String} align left | right - for navbars
7637 * @cfg {Boolean} loadMask load mask when submit (default true)
7642 * @param {Object} config The config object
7646 Roo.bootstrap.Form = function(config){
7648 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7650 Roo.bootstrap.Form.popover.apply();
7654 * @event clientvalidation
7655 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7656 * @param {Form} this
7657 * @param {Boolean} valid true if the form has passed client-side validation
7659 clientvalidation: true,
7661 * @event beforeaction
7662 * Fires before any action is performed. Return false to cancel the action.
7663 * @param {Form} this
7664 * @param {Action} action The action to be performed
7668 * @event actionfailed
7669 * Fires when an action fails.
7670 * @param {Form} this
7671 * @param {Action} action The action that failed
7673 actionfailed : true,
7675 * @event actioncomplete
7676 * Fires when an action is completed.
7677 * @param {Form} this
7678 * @param {Action} action The action that completed
7680 actioncomplete : true
7684 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7687 * @cfg {String} method
7688 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7693 * The URL to use for form actions if one isn't supplied in the action options.
7696 * @cfg {Boolean} fileUpload
7697 * Set to true if this form is a file upload.
7701 * @cfg {Object} baseParams
7702 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7706 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7710 * @cfg {Sting} align (left|right) for navbar forms
7715 activeAction : null,
7718 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7719 * element by passing it or its id or mask the form itself by passing in true.
7722 waitMsgTarget : false,
7727 * @cfg {Boolean} errorMask (true|false) default false
7732 * @cfg {Number} maskOffset Default 100
7737 * @cfg {Boolean} maskBody
7741 getAutoCreate : function(){
7745 method : this.method || 'POST',
7746 id : this.id || Roo.id(),
7749 if (this.parent().xtype.match(/^Nav/)) {
7750 cfg.cls = 'navbar-form navbar-' + this.align;
7754 if (this.labelAlign == 'left' ) {
7755 cfg.cls += ' form-horizontal';
7761 initEvents : function()
7763 this.el.on('submit', this.onSubmit, this);
7764 // this was added as random key presses on the form where triggering form submit.
7765 this.el.on('keypress', function(e) {
7766 if (e.getCharCode() != 13) {
7769 // we might need to allow it for textareas.. and some other items.
7770 // check e.getTarget().
7772 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7776 Roo.log("keypress blocked");
7784 onSubmit : function(e){
7789 * Returns true if client-side validation on the form is successful.
7792 isValid : function(){
7793 var items = this.getItems();
7797 items.each(function(f){
7804 if(!target && f.el.isVisible(true)){
7810 if(this.errorMask && !valid){
7811 Roo.bootstrap.Form.popover.mask(this, target);
7818 * Returns true if any fields in this form have changed since their original load.
7821 isDirty : function(){
7823 var items = this.getItems();
7824 items.each(function(f){
7834 * Performs a predefined action (submit or load) or custom actions you define on this form.
7835 * @param {String} actionName The name of the action type
7836 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7837 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7838 * accept other config options):
7840 Property Type Description
7841 ---------------- --------------- ----------------------------------------------------------------------------------
7842 url String The url for the action (defaults to the form's url)
7843 method String The form method to use (defaults to the form's method, or POST if not defined)
7844 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7845 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7846 validate the form on the client (defaults to false)
7848 * @return {BasicForm} this
7850 doAction : function(action, options){
7851 if(typeof action == 'string'){
7852 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7854 if(this.fireEvent('beforeaction', this, action) !== false){
7855 this.beforeAction(action);
7856 action.run.defer(100, action);
7862 beforeAction : function(action){
7863 var o = action.options;
7868 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7870 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7873 // not really supported yet.. ??
7875 //if(this.waitMsgTarget === true){
7876 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7877 //}else if(this.waitMsgTarget){
7878 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7879 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7881 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7887 afterAction : function(action, success){
7888 this.activeAction = null;
7889 var o = action.options;
7894 Roo.get(document.body).unmask();
7900 //if(this.waitMsgTarget === true){
7901 // this.el.unmask();
7902 //}else if(this.waitMsgTarget){
7903 // this.waitMsgTarget.unmask();
7905 // Roo.MessageBox.updateProgress(1);
7906 // Roo.MessageBox.hide();
7913 Roo.callback(o.success, o.scope, [this, action]);
7914 this.fireEvent('actioncomplete', this, action);
7918 // failure condition..
7919 // we have a scenario where updates need confirming.
7920 // eg. if a locking scenario exists..
7921 // we look for { errors : { needs_confirm : true }} in the response.
7923 (typeof(action.result) != 'undefined') &&
7924 (typeof(action.result.errors) != 'undefined') &&
7925 (typeof(action.result.errors.needs_confirm) != 'undefined')
7928 Roo.log("not supported yet");
7931 Roo.MessageBox.confirm(
7932 "Change requires confirmation",
7933 action.result.errorMsg,
7938 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7948 Roo.callback(o.failure, o.scope, [this, action]);
7949 // show an error message if no failed handler is set..
7950 if (!this.hasListener('actionfailed')) {
7951 Roo.log("need to add dialog support");
7953 Roo.MessageBox.alert("Error",
7954 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7955 action.result.errorMsg :
7956 "Saving Failed, please check your entries or try again"
7961 this.fireEvent('actionfailed', this, action);
7966 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7967 * @param {String} id The value to search for
7970 findField : function(id){
7971 var items = this.getItems();
7972 var field = items.get(id);
7974 items.each(function(f){
7975 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7982 return field || null;
7985 * Mark fields in this form invalid in bulk.
7986 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7987 * @return {BasicForm} this
7989 markInvalid : function(errors){
7990 if(errors instanceof Array){
7991 for(var i = 0, len = errors.length; i < len; i++){
7992 var fieldError = errors[i];
7993 var f = this.findField(fieldError.id);
7995 f.markInvalid(fieldError.msg);
8001 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8002 field.markInvalid(errors[id]);
8006 //Roo.each(this.childForms || [], function (f) {
8007 // f.markInvalid(errors);
8014 * Set values for fields in this form in bulk.
8015 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8016 * @return {BasicForm} this
8018 setValues : function(values){
8019 if(values instanceof Array){ // array of objects
8020 for(var i = 0, len = values.length; i < len; i++){
8022 var f = this.findField(v.id);
8024 f.setValue(v.value);
8025 if(this.trackResetOnLoad){
8026 f.originalValue = f.getValue();
8030 }else{ // object hash
8033 if(typeof values[id] != 'function' && (field = this.findField(id))){
8035 if (field.setFromData &&
8037 field.displayField &&
8038 // combos' with local stores can
8039 // be queried via setValue()
8040 // to set their value..
8041 (field.store && !field.store.isLocal)
8045 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8046 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8047 field.setFromData(sd);
8049 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8051 field.setFromData(values);
8054 field.setValue(values[id]);
8058 if(this.trackResetOnLoad){
8059 field.originalValue = field.getValue();
8065 //Roo.each(this.childForms || [], function (f) {
8066 // f.setValues(values);
8073 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8074 * they are returned as an array.
8075 * @param {Boolean} asString
8078 getValues : function(asString){
8079 //if (this.childForms) {
8080 // copy values from the child forms
8081 // Roo.each(this.childForms, function (f) {
8082 // this.setValues(f.getValues());
8088 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8089 if(asString === true){
8092 return Roo.urlDecode(fs);
8096 * Returns the fields in this form as an object with key/value pairs.
8097 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8100 getFieldValues : function(with_hidden)
8102 var items = this.getItems();
8104 items.each(function(f){
8110 var v = f.getValue();
8112 if (f.inputType =='radio') {
8113 if (typeof(ret[f.getName()]) == 'undefined') {
8114 ret[f.getName()] = ''; // empty..
8117 if (!f.el.dom.checked) {
8125 if(f.xtype == 'MoneyField'){
8126 ret[f.currencyName] = f.getCurrency();
8129 // not sure if this supported any more..
8130 if ((typeof(v) == 'object') && f.getRawValue) {
8131 v = f.getRawValue() ; // dates..
8133 // combo boxes where name != hiddenName...
8134 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8135 ret[f.name] = f.getRawValue();
8137 ret[f.getName()] = v;
8144 * Clears all invalid messages in this form.
8145 * @return {BasicForm} this
8147 clearInvalid : function(){
8148 var items = this.getItems();
8150 items.each(function(f){
8159 * @return {BasicForm} this
8162 var items = this.getItems();
8163 items.each(function(f){
8167 Roo.each(this.childForms || [], function (f) {
8175 getItems : function()
8177 var r=new Roo.util.MixedCollection(false, function(o){
8178 return o.id || (o.id = Roo.id());
8180 var iter = function(el) {
8187 Roo.each(el.items,function(e) {
8196 hideFields : function(items)
8198 Roo.each(items, function(i){
8200 var f = this.findField(i);
8206 if(f.xtype == 'DateField'){
8207 f.setVisible(false);
8216 showFields : function(items)
8218 Roo.each(items, function(i){
8220 var f = this.findField(i);
8226 if(f.xtype == 'DateField'){
8238 Roo.apply(Roo.bootstrap.Form, {
8265 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8266 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8267 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8268 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8271 this.maskEl.top.enableDisplayMode("block");
8272 this.maskEl.left.enableDisplayMode("block");
8273 this.maskEl.bottom.enableDisplayMode("block");
8274 this.maskEl.right.enableDisplayMode("block");
8276 this.toolTip = new Roo.bootstrap.Tooltip({
8277 cls : 'roo-form-error-popover',
8279 'left' : ['r-l', [-2,0], 'right'],
8280 'right' : ['l-r', [2,0], 'left'],
8281 'bottom' : ['tl-bl', [0,2], 'top'],
8282 'top' : [ 'bl-tl', [0,-2], 'bottom']
8286 this.toolTip.render(Roo.get(document.body));
8288 this.toolTip.el.enableDisplayMode("block");
8290 Roo.get(document.body).on('click', function(){
8294 Roo.get(document.body).on('touchstart', function(){
8298 this.isApplied = true
8301 mask : function(form, target)
8305 this.target = target;
8307 if(!this.form.errorMask || !target.el){
8311 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8313 Roo.log(scrollable);
8315 var ot = this.target.el.calcOffsetsTo(scrollable);
8317 var scrollTo = ot[1] - this.form.maskOffset;
8319 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8321 scrollable.scrollTo('top', scrollTo);
8323 var box = this.target.el.getBox();
8325 var zIndex = Roo.bootstrap.Modal.zIndex++;
8328 this.maskEl.top.setStyle('position', 'absolute');
8329 this.maskEl.top.setStyle('z-index', zIndex);
8330 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8331 this.maskEl.top.setLeft(0);
8332 this.maskEl.top.setTop(0);
8333 this.maskEl.top.show();
8335 this.maskEl.left.setStyle('position', 'absolute');
8336 this.maskEl.left.setStyle('z-index', zIndex);
8337 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8338 this.maskEl.left.setLeft(0);
8339 this.maskEl.left.setTop(box.y - this.padding);
8340 this.maskEl.left.show();
8342 this.maskEl.bottom.setStyle('position', 'absolute');
8343 this.maskEl.bottom.setStyle('z-index', zIndex);
8344 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8345 this.maskEl.bottom.setLeft(0);
8346 this.maskEl.bottom.setTop(box.bottom + this.padding);
8347 this.maskEl.bottom.show();
8349 this.maskEl.right.setStyle('position', 'absolute');
8350 this.maskEl.right.setStyle('z-index', zIndex);
8351 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8352 this.maskEl.right.setLeft(box.right + this.padding);
8353 this.maskEl.right.setTop(box.y - this.padding);
8354 this.maskEl.right.show();
8356 this.toolTip.bindEl = this.target.el;
8358 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8360 var tip = this.target.blankText;
8362 if(this.target.getValue() !== '' ) {
8364 if (this.target.invalidText.length) {
8365 tip = this.target.invalidText;
8366 } else if (this.target.regexText.length){
8367 tip = this.target.regexText;
8371 this.toolTip.show(tip);
8373 this.intervalID = window.setInterval(function() {
8374 Roo.bootstrap.Form.popover.unmask();
8377 window.onwheel = function(){ return false;};
8379 (function(){ this.isMasked = true; }).defer(500, this);
8385 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8389 this.maskEl.top.setStyle('position', 'absolute');
8390 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8391 this.maskEl.top.hide();
8393 this.maskEl.left.setStyle('position', 'absolute');
8394 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8395 this.maskEl.left.hide();
8397 this.maskEl.bottom.setStyle('position', 'absolute');
8398 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8399 this.maskEl.bottom.hide();
8401 this.maskEl.right.setStyle('position', 'absolute');
8402 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8403 this.maskEl.right.hide();
8405 this.toolTip.hide();
8407 this.toolTip.el.hide();
8409 window.onwheel = function(){ return true;};
8411 if(this.intervalID){
8412 window.clearInterval(this.intervalID);
8413 this.intervalID = false;
8416 this.isMasked = false;
8426 * Ext JS Library 1.1.1
8427 * Copyright(c) 2006-2007, Ext JS, LLC.
8429 * Originally Released Under LGPL - original licence link has changed is not relivant.
8432 * <script type="text/javascript">
8435 * @class Roo.form.VTypes
8436 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8439 Roo.form.VTypes = function(){
8440 // closure these in so they are only created once.
8441 var alpha = /^[a-zA-Z_]+$/;
8442 var alphanum = /^[a-zA-Z0-9_]+$/;
8443 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8444 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8446 // All these messages and functions are configurable
8449 * The function used to validate email addresses
8450 * @param {String} value The email address
8452 'email' : function(v){
8453 return email.test(v);
8456 * The error text to display when the email validation function returns false
8459 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8461 * The keystroke filter mask to be applied on email input
8464 'emailMask' : /[a-z0-9_\.\-@]/i,
8467 * The function used to validate URLs
8468 * @param {String} value The URL
8470 'url' : function(v){
8474 * The error text to display when the url validation function returns false
8477 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8480 * The function used to validate alpha values
8481 * @param {String} value The value
8483 'alpha' : function(v){
8484 return alpha.test(v);
8487 * The error text to display when the alpha validation function returns false
8490 'alphaText' : 'This field should only contain letters and _',
8492 * The keystroke filter mask to be applied on alpha input
8495 'alphaMask' : /[a-z_]/i,
8498 * The function used to validate alphanumeric values
8499 * @param {String} value The value
8501 'alphanum' : function(v){
8502 return alphanum.test(v);
8505 * The error text to display when the alphanumeric validation function returns false
8508 'alphanumText' : 'This field should only contain letters, numbers and _',
8510 * The keystroke filter mask to be applied on alphanumeric input
8513 'alphanumMask' : /[a-z0-9_]/i
8523 * @class Roo.bootstrap.Input
8524 * @extends Roo.bootstrap.Component
8525 * Bootstrap Input class
8526 * @cfg {Boolean} disabled is it disabled
8527 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8528 * @cfg {String} name name of the input
8529 * @cfg {string} fieldLabel - the label associated
8530 * @cfg {string} placeholder - placeholder to put in text.
8531 * @cfg {string} before - input group add on before
8532 * @cfg {string} after - input group add on after
8533 * @cfg {string} size - (lg|sm) or leave empty..
8534 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8535 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8536 * @cfg {Number} md colspan out of 12 for computer-sized screens
8537 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8538 * @cfg {string} value default value of the input
8539 * @cfg {Number} labelWidth set the width of label
8540 * @cfg {Number} labellg set the width of label (1-12)
8541 * @cfg {Number} labelmd set the width of label (1-12)
8542 * @cfg {Number} labelsm set the width of label (1-12)
8543 * @cfg {Number} labelxs set the width of label (1-12)
8544 * @cfg {String} labelAlign (top|left)
8545 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8546 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8547 * @cfg {String} indicatorpos (left|right) default left
8548 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8549 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8551 * @cfg {String} align (left|center|right) Default left
8552 * @cfg {Boolean} forceFeedback (true|false) Default false
8555 * Create a new Input
8556 * @param {Object} config The config object
8559 Roo.bootstrap.Input = function(config){
8561 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8566 * Fires when this field receives input focus.
8567 * @param {Roo.form.Field} this
8572 * Fires when this field loses input focus.
8573 * @param {Roo.form.Field} this
8578 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8579 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8580 * @param {Roo.form.Field} this
8581 * @param {Roo.EventObject} e The event object
8586 * Fires just before the field blurs if the field value has changed.
8587 * @param {Roo.form.Field} this
8588 * @param {Mixed} newValue The new value
8589 * @param {Mixed} oldValue The original value
8594 * Fires after the field has been marked as invalid.
8595 * @param {Roo.form.Field} this
8596 * @param {String} msg The validation message
8601 * Fires after the field has been validated with no errors.
8602 * @param {Roo.form.Field} this
8607 * Fires after the key up
8608 * @param {Roo.form.Field} this
8609 * @param {Roo.EventObject} e The event Object
8615 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8617 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8618 automatic validation (defaults to "keyup").
8620 validationEvent : "keyup",
8622 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8624 validateOnBlur : true,
8626 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8628 validationDelay : 250,
8630 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8632 focusClass : "x-form-focus", // not needed???
8636 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8638 invalidClass : "has-warning",
8641 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8643 validClass : "has-success",
8646 * @cfg {Boolean} hasFeedback (true|false) default true
8651 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8653 invalidFeedbackClass : "glyphicon-warning-sign",
8656 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8658 validFeedbackClass : "glyphicon-ok",
8661 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8663 selectOnFocus : false,
8666 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8670 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8675 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8677 disableKeyFilter : false,
8680 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8684 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8688 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8690 blankText : "Please complete this mandatory field",
8693 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8697 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8699 maxLength : Number.MAX_VALUE,
8701 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8703 minLengthText : "The minimum length for this field is {0}",
8705 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8707 maxLengthText : "The maximum length for this field is {0}",
8711 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8712 * If available, this function will be called only after the basic validators all return true, and will be passed the
8713 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8717 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8718 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8719 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8723 * @cfg {String} regexText -- Depricated - use Invalid Text
8728 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8734 autocomplete: false,
8753 formatedValue : false,
8754 forceFeedback : false,
8756 indicatorpos : 'left',
8766 parentLabelAlign : function()
8769 while (parent.parent()) {
8770 parent = parent.parent();
8771 if (typeof(parent.labelAlign) !='undefined') {
8772 return parent.labelAlign;
8779 getAutoCreate : function()
8781 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8787 if(this.inputType != 'hidden'){
8788 cfg.cls = 'form-group' //input-group
8794 type : this.inputType,
8796 cls : 'form-control',
8797 placeholder : this.placeholder || '',
8798 autocomplete : this.autocomplete || 'new-password'
8801 if(this.capture.length){
8802 input.capture = this.capture;
8805 if(this.accept.length){
8806 input.accept = this.accept + "/*";
8810 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8813 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8814 input.maxLength = this.maxLength;
8817 if (this.disabled) {
8818 input.disabled=true;
8821 if (this.readOnly) {
8822 input.readonly=true;
8826 input.name = this.name;
8830 input.cls += ' input-' + this.size;
8834 ['xs','sm','md','lg'].map(function(size){
8835 if (settings[size]) {
8836 cfg.cls += ' col-' + size + '-' + settings[size];
8840 var inputblock = input;
8844 cls: 'glyphicon form-control-feedback'
8847 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8850 cls : 'has-feedback',
8858 if (this.before || this.after) {
8861 cls : 'input-group',
8865 if (this.before && typeof(this.before) == 'string') {
8867 inputblock.cn.push({
8869 cls : 'roo-input-before input-group-addon',
8873 if (this.before && typeof(this.before) == 'object') {
8874 this.before = Roo.factory(this.before);
8876 inputblock.cn.push({
8878 cls : 'roo-input-before input-group-' +
8879 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8883 inputblock.cn.push(input);
8885 if (this.after && typeof(this.after) == 'string') {
8886 inputblock.cn.push({
8888 cls : 'roo-input-after input-group-addon',
8892 if (this.after && typeof(this.after) == 'object') {
8893 this.after = Roo.factory(this.after);
8895 inputblock.cn.push({
8897 cls : 'roo-input-after input-group-' +
8898 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8902 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8903 inputblock.cls += ' has-feedback';
8904 inputblock.cn.push(feedback);
8908 if (align ==='left' && this.fieldLabel.length) {
8910 cfg.cls += ' roo-form-group-label-left';
8915 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8916 tooltip : 'This field is required'
8921 cls : 'control-label',
8922 html : this.fieldLabel
8933 var labelCfg = cfg.cn[1];
8934 var contentCfg = cfg.cn[2];
8936 if(this.indicatorpos == 'right'){
8941 cls : 'control-label',
8945 html : this.fieldLabel
8949 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8950 tooltip : 'This field is required'
8963 labelCfg = cfg.cn[0];
8964 contentCfg = cfg.cn[1];
8968 if(this.labelWidth > 12){
8969 labelCfg.style = "width: " + this.labelWidth + 'px';
8972 if(this.labelWidth < 13 && this.labelmd == 0){
8973 this.labelmd = this.labelWidth;
8976 if(this.labellg > 0){
8977 labelCfg.cls += ' col-lg-' + this.labellg;
8978 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8981 if(this.labelmd > 0){
8982 labelCfg.cls += ' col-md-' + this.labelmd;
8983 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8986 if(this.labelsm > 0){
8987 labelCfg.cls += ' col-sm-' + this.labelsm;
8988 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8991 if(this.labelxs > 0){
8992 labelCfg.cls += ' col-xs-' + this.labelxs;
8993 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8997 } else if ( this.fieldLabel.length) {
9002 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9003 tooltip : 'This field is required'
9007 //cls : 'input-group-addon',
9008 html : this.fieldLabel
9016 if(this.indicatorpos == 'right'){
9021 //cls : 'input-group-addon',
9022 html : this.fieldLabel
9027 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9028 tooltip : 'This field is required'
9048 if (this.parentType === 'Navbar' && this.parent().bar) {
9049 cfg.cls += ' navbar-form';
9052 if (this.parentType === 'NavGroup') {
9053 cfg.cls += ' navbar-form';
9061 * return the real input element.
9063 inputEl: function ()
9065 return this.el.select('input.form-control',true).first();
9068 tooltipEl : function()
9070 return this.inputEl();
9073 indicatorEl : function()
9075 var indicator = this.el.select('i.roo-required-indicator',true).first();
9085 setDisabled : function(v)
9087 var i = this.inputEl().dom;
9089 i.removeAttribute('disabled');
9093 i.setAttribute('disabled','true');
9095 initEvents : function()
9098 this.inputEl().on("keydown" , this.fireKey, this);
9099 this.inputEl().on("focus", this.onFocus, this);
9100 this.inputEl().on("blur", this.onBlur, this);
9102 this.inputEl().relayEvent('keyup', this);
9104 this.indicator = this.indicatorEl();
9107 this.indicator.addClass('invisible');
9110 // reference to original value for reset
9111 this.originalValue = this.getValue();
9112 //Roo.form.TextField.superclass.initEvents.call(this);
9113 if(this.validationEvent == 'keyup'){
9114 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9115 this.inputEl().on('keyup', this.filterValidation, this);
9117 else if(this.validationEvent !== false){
9118 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9121 if(this.selectOnFocus){
9122 this.on("focus", this.preFocus, this);
9125 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9126 this.inputEl().on("keypress", this.filterKeys, this);
9128 this.inputEl().relayEvent('keypress', this);
9131 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9132 this.el.on("click", this.autoSize, this);
9135 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9136 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9139 if (typeof(this.before) == 'object') {
9140 this.before.render(this.el.select('.roo-input-before',true).first());
9142 if (typeof(this.after) == 'object') {
9143 this.after.render(this.el.select('.roo-input-after',true).first());
9146 this.inputEl().on('change', this.onChange, this);
9149 filterValidation : function(e){
9150 if(!e.isNavKeyPress()){
9151 this.validationTask.delay(this.validationDelay);
9155 * Validates the field value
9156 * @return {Boolean} True if the value is valid, else false
9158 validate : function(){
9159 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9160 if(this.disabled || this.validateValue(this.getRawValue())){
9171 * Validates a value according to the field's validation rules and marks the field as invalid
9172 * if the validation fails
9173 * @param {Mixed} value The value to validate
9174 * @return {Boolean} True if the value is valid, else false
9176 validateValue : function(value)
9178 if(this.getVisibilityEl().hasClass('hidden')){
9182 if(value.length < 1) { // if it's blank
9183 if(this.allowBlank){
9189 if(value.length < this.minLength){
9192 if(value.length > this.maxLength){
9196 var vt = Roo.form.VTypes;
9197 if(!vt[this.vtype](value, this)){
9201 if(typeof this.validator == "function"){
9202 var msg = this.validator(value);
9206 if (typeof(msg) == 'string') {
9207 this.invalidText = msg;
9211 if(this.regex && !this.regex.test(value)){
9219 fireKey : function(e){
9220 //Roo.log('field ' + e.getKey());
9221 if(e.isNavKeyPress()){
9222 this.fireEvent("specialkey", this, e);
9225 focus : function (selectText){
9227 this.inputEl().focus();
9228 if(selectText === true){
9229 this.inputEl().dom.select();
9235 onFocus : function(){
9236 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9237 // this.el.addClass(this.focusClass);
9240 this.hasFocus = true;
9241 this.startValue = this.getValue();
9242 this.fireEvent("focus", this);
9246 beforeBlur : Roo.emptyFn,
9250 onBlur : function(){
9252 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9253 //this.el.removeClass(this.focusClass);
9255 this.hasFocus = false;
9256 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9259 var v = this.getValue();
9260 if(String(v) !== String(this.startValue)){
9261 this.fireEvent('change', this, v, this.startValue);
9263 this.fireEvent("blur", this);
9266 onChange : function(e)
9268 var v = this.getValue();
9269 if(String(v) !== String(this.startValue)){
9270 this.fireEvent('change', this, v, this.startValue);
9276 * Resets the current field value to the originally loaded value and clears any validation messages
9279 this.setValue(this.originalValue);
9283 * Returns the name of the field
9284 * @return {Mixed} name The name field
9286 getName: function(){
9290 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9291 * @return {Mixed} value The field value
9293 getValue : function(){
9295 var v = this.inputEl().getValue();
9300 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9301 * @return {Mixed} value The field value
9303 getRawValue : function(){
9304 var v = this.inputEl().getValue();
9310 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9311 * @param {Mixed} value The value to set
9313 setRawValue : function(v){
9314 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9317 selectText : function(start, end){
9318 var v = this.getRawValue();
9320 start = start === undefined ? 0 : start;
9321 end = end === undefined ? v.length : end;
9322 var d = this.inputEl().dom;
9323 if(d.setSelectionRange){
9324 d.setSelectionRange(start, end);
9325 }else if(d.createTextRange){
9326 var range = d.createTextRange();
9327 range.moveStart("character", start);
9328 range.moveEnd("character", v.length-end);
9335 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9336 * @param {Mixed} value The value to set
9338 setValue : function(v){
9341 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9347 processValue : function(value){
9348 if(this.stripCharsRe){
9349 var newValue = value.replace(this.stripCharsRe, '');
9350 if(newValue !== value){
9351 this.setRawValue(newValue);
9358 preFocus : function(){
9360 if(this.selectOnFocus){
9361 this.inputEl().dom.select();
9364 filterKeys : function(e){
9366 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9369 var c = e.getCharCode(), cc = String.fromCharCode(c);
9370 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9373 if(!this.maskRe.test(cc)){
9378 * Clear any invalid styles/messages for this field
9380 clearInvalid : function(){
9382 if(!this.el || this.preventMark){ // not rendered
9387 this.el.removeClass(this.invalidClass);
9389 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9391 var feedback = this.el.select('.form-control-feedback', true).first();
9394 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9399 this.fireEvent('valid', this);
9403 * Mark this field as valid
9405 markValid : function()
9407 if(!this.el || this.preventMark){ // not rendered...
9411 this.el.removeClass([this.invalidClass, this.validClass]);
9413 var feedback = this.el.select('.form-control-feedback', true).first();
9416 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9420 this.indicator.removeClass('visible');
9421 this.indicator.addClass('invisible');
9428 if(this.allowBlank && !this.getRawValue().length){
9432 this.el.addClass(this.validClass);
9434 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9436 var feedback = this.el.select('.form-control-feedback', true).first();
9439 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9440 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9445 this.fireEvent('valid', this);
9449 * Mark this field as invalid
9450 * @param {String} msg The validation message
9452 markInvalid : function(msg)
9454 if(!this.el || this.preventMark){ // not rendered
9458 this.el.removeClass([this.invalidClass, this.validClass]);
9460 var feedback = this.el.select('.form-control-feedback', true).first();
9463 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9470 if(this.allowBlank && !this.getRawValue().length){
9475 this.indicator.removeClass('invisible');
9476 this.indicator.addClass('visible');
9479 this.el.addClass(this.invalidClass);
9481 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9483 var feedback = this.el.select('.form-control-feedback', true).first();
9486 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9488 if(this.getValue().length || this.forceFeedback){
9489 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9496 this.fireEvent('invalid', this, msg);
9499 SafariOnKeyDown : function(event)
9501 // this is a workaround for a password hang bug on chrome/ webkit.
9502 if (this.inputEl().dom.type != 'password') {
9506 var isSelectAll = false;
9508 if(this.inputEl().dom.selectionEnd > 0){
9509 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9511 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9512 event.preventDefault();
9517 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9519 event.preventDefault();
9520 // this is very hacky as keydown always get's upper case.
9522 var cc = String.fromCharCode(event.getCharCode());
9523 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9527 adjustWidth : function(tag, w){
9528 tag = tag.toLowerCase();
9529 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9530 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9534 if(tag == 'textarea'){
9537 }else if(Roo.isOpera){
9541 if(tag == 'textarea'){
9549 setFieldLabel : function(v)
9556 var ar = this.el.select('label > span',true);
9558 if (ar.elements.length) {
9559 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9560 this.fieldLabel = v;
9564 var br = this.el.select('label',true);
9566 if(br.elements.length) {
9567 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9568 this.fieldLabel = v;
9572 Roo.log('Cannot Found any of label > span || label in input');
9576 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9577 this.fieldLabel = v;
9592 * @class Roo.bootstrap.TextArea
9593 * @extends Roo.bootstrap.Input
9594 * Bootstrap TextArea class
9595 * @cfg {Number} cols Specifies the visible width of a text area
9596 * @cfg {Number} rows Specifies the visible number of lines in a text area
9597 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9598 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9599 * @cfg {string} html text
9602 * Create a new TextArea
9603 * @param {Object} config The config object
9606 Roo.bootstrap.TextArea = function(config){
9607 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9611 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9621 getAutoCreate : function(){
9623 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9629 if(this.inputType != 'hidden'){
9630 cfg.cls = 'form-group' //input-group
9638 value : this.value || '',
9639 html: this.html || '',
9640 cls : 'form-control',
9641 placeholder : this.placeholder || ''
9645 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9646 input.maxLength = this.maxLength;
9650 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9654 input.cols = this.cols;
9657 if (this.readOnly) {
9658 input.readonly = true;
9662 input.name = this.name;
9666 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9670 ['xs','sm','md','lg'].map(function(size){
9671 if (settings[size]) {
9672 cfg.cls += ' col-' + size + '-' + settings[size];
9676 var inputblock = input;
9678 if(this.hasFeedback && !this.allowBlank){
9682 cls: 'glyphicon form-control-feedback'
9686 cls : 'has-feedback',
9695 if (this.before || this.after) {
9698 cls : 'input-group',
9702 inputblock.cn.push({
9704 cls : 'input-group-addon',
9709 inputblock.cn.push(input);
9711 if(this.hasFeedback && !this.allowBlank){
9712 inputblock.cls += ' has-feedback';
9713 inputblock.cn.push(feedback);
9717 inputblock.cn.push({
9719 cls : 'input-group-addon',
9726 if (align ==='left' && this.fieldLabel.length) {
9731 cls : 'control-label',
9732 html : this.fieldLabel
9743 if(this.labelWidth > 12){
9744 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9747 if(this.labelWidth < 13 && this.labelmd == 0){
9748 this.labelmd = this.labelWidth;
9751 if(this.labellg > 0){
9752 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9753 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9756 if(this.labelmd > 0){
9757 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9758 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9761 if(this.labelsm > 0){
9762 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9763 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9766 if(this.labelxs > 0){
9767 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9768 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9771 } else if ( this.fieldLabel.length) {
9776 //cls : 'input-group-addon',
9777 html : this.fieldLabel
9795 if (this.disabled) {
9796 input.disabled=true;
9803 * return the real textarea element.
9805 inputEl: function ()
9807 return this.el.select('textarea.form-control',true).first();
9811 * Clear any invalid styles/messages for this field
9813 clearInvalid : function()
9816 if(!this.el || this.preventMark){ // not rendered
9820 var label = this.el.select('label', true).first();
9821 var icon = this.el.select('i.fa-star', true).first();
9827 this.el.removeClass(this.invalidClass);
9829 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9831 var feedback = this.el.select('.form-control-feedback', true).first();
9834 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9839 this.fireEvent('valid', this);
9843 * Mark this field as valid
9845 markValid : function()
9847 if(!this.el || this.preventMark){ // not rendered
9851 this.el.removeClass([this.invalidClass, this.validClass]);
9853 var feedback = this.el.select('.form-control-feedback', true).first();
9856 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9859 if(this.disabled || this.allowBlank){
9863 var label = this.el.select('label', true).first();
9864 var icon = this.el.select('i.fa-star', true).first();
9870 this.el.addClass(this.validClass);
9872 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9874 var feedback = this.el.select('.form-control-feedback', true).first();
9877 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9878 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9883 this.fireEvent('valid', this);
9887 * Mark this field as invalid
9888 * @param {String} msg The validation message
9890 markInvalid : function(msg)
9892 if(!this.el || this.preventMark){ // not rendered
9896 this.el.removeClass([this.invalidClass, this.validClass]);
9898 var feedback = this.el.select('.form-control-feedback', true).first();
9901 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9904 if(this.disabled || this.allowBlank){
9908 var label = this.el.select('label', true).first();
9909 var icon = this.el.select('i.fa-star', true).first();
9911 if(!this.getValue().length && label && !icon){
9912 this.el.createChild({
9914 cls : 'text-danger fa fa-lg fa-star',
9915 tooltip : 'This field is required',
9916 style : 'margin-right:5px;'
9920 this.el.addClass(this.invalidClass);
9922 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9924 var feedback = this.el.select('.form-control-feedback', true).first();
9927 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9929 if(this.getValue().length || this.forceFeedback){
9930 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9937 this.fireEvent('invalid', this, msg);
9945 * trigger field - base class for combo..
9950 * @class Roo.bootstrap.TriggerField
9951 * @extends Roo.bootstrap.Input
9952 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9953 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9954 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9955 * for which you can provide a custom implementation. For example:
9957 var trigger = new Roo.bootstrap.TriggerField();
9958 trigger.onTriggerClick = myTriggerFn;
9959 trigger.applyTo('my-field');
9962 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9963 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9964 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9965 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9966 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9969 * Create a new TriggerField.
9970 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9971 * to the base TextField)
9973 Roo.bootstrap.TriggerField = function(config){
9974 this.mimicing = false;
9975 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9978 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9980 * @cfg {String} triggerClass A CSS class to apply to the trigger
9983 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9988 * @cfg {Boolean} removable (true|false) special filter default false
9992 /** @cfg {Boolean} grow @hide */
9993 /** @cfg {Number} growMin @hide */
9994 /** @cfg {Number} growMax @hide */
10000 autoSize: Roo.emptyFn,
10004 deferHeight : true,
10007 actionMode : 'wrap',
10012 getAutoCreate : function(){
10014 var align = this.labelAlign || this.parentLabelAlign();
10019 cls: 'form-group' //input-group
10026 type : this.inputType,
10027 cls : 'form-control',
10028 autocomplete: 'new-password',
10029 placeholder : this.placeholder || ''
10033 input.name = this.name;
10036 input.cls += ' input-' + this.size;
10039 if (this.disabled) {
10040 input.disabled=true;
10043 var inputblock = input;
10045 if(this.hasFeedback && !this.allowBlank){
10049 cls: 'glyphicon form-control-feedback'
10052 if(this.removable && !this.editable && !this.tickable){
10054 cls : 'has-feedback',
10060 cls : 'roo-combo-removable-btn close'
10067 cls : 'has-feedback',
10076 if(this.removable && !this.editable && !this.tickable){
10078 cls : 'roo-removable',
10084 cls : 'roo-combo-removable-btn close'
10091 if (this.before || this.after) {
10094 cls : 'input-group',
10098 inputblock.cn.push({
10100 cls : 'input-group-addon',
10105 inputblock.cn.push(input);
10107 if(this.hasFeedback && !this.allowBlank){
10108 inputblock.cls += ' has-feedback';
10109 inputblock.cn.push(feedback);
10113 inputblock.cn.push({
10115 cls : 'input-group-addon',
10128 cls: 'form-hidden-field'
10142 cls: 'form-hidden-field'
10146 cls: 'roo-select2-choices',
10150 cls: 'roo-select2-search-field',
10163 cls: 'roo-select2-container input-group',
10168 // cls: 'typeahead typeahead-long dropdown-menu',
10169 // style: 'display:none'
10174 if(!this.multiple && this.showToggleBtn){
10180 if (this.caret != false) {
10183 cls: 'fa fa-' + this.caret
10190 cls : 'input-group-addon btn dropdown-toggle',
10195 cls: 'combobox-clear',
10209 combobox.cls += ' roo-select2-container-multi';
10212 if (align ==='left' && this.fieldLabel.length) {
10214 cfg.cls += ' roo-form-group-label-left';
10219 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10220 tooltip : 'This field is required'
10225 cls : 'control-label',
10226 html : this.fieldLabel
10238 var labelCfg = cfg.cn[1];
10239 var contentCfg = cfg.cn[2];
10241 if(this.indicatorpos == 'right'){
10246 cls : 'control-label',
10250 html : this.fieldLabel
10254 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10255 tooltip : 'This field is required'
10268 labelCfg = cfg.cn[0];
10269 contentCfg = cfg.cn[1];
10272 if(this.labelWidth > 12){
10273 labelCfg.style = "width: " + this.labelWidth + 'px';
10276 if(this.labelWidth < 13 && this.labelmd == 0){
10277 this.labelmd = this.labelWidth;
10280 if(this.labellg > 0){
10281 labelCfg.cls += ' col-lg-' + this.labellg;
10282 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10285 if(this.labelmd > 0){
10286 labelCfg.cls += ' col-md-' + this.labelmd;
10287 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10290 if(this.labelsm > 0){
10291 labelCfg.cls += ' col-sm-' + this.labelsm;
10292 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10295 if(this.labelxs > 0){
10296 labelCfg.cls += ' col-xs-' + this.labelxs;
10297 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10300 } else if ( this.fieldLabel.length) {
10301 // Roo.log(" label");
10305 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10306 tooltip : 'This field is required'
10310 //cls : 'input-group-addon',
10311 html : this.fieldLabel
10319 if(this.indicatorpos == 'right'){
10327 html : this.fieldLabel
10331 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10332 tooltip : 'This field is required'
10345 // Roo.log(" no label && no align");
10352 ['xs','sm','md','lg'].map(function(size){
10353 if (settings[size]) {
10354 cfg.cls += ' col-' + size + '-' + settings[size];
10365 onResize : function(w, h){
10366 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10367 // if(typeof w == 'number'){
10368 // var x = w - this.trigger.getWidth();
10369 // this.inputEl().setWidth(this.adjustWidth('input', x));
10370 // this.trigger.setStyle('left', x+'px');
10375 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10378 getResizeEl : function(){
10379 return this.inputEl();
10383 getPositionEl : function(){
10384 return this.inputEl();
10388 alignErrorIcon : function(){
10389 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10393 initEvents : function(){
10397 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10398 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10399 if(!this.multiple && this.showToggleBtn){
10400 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10401 if(this.hideTrigger){
10402 this.trigger.setDisplayed(false);
10404 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10408 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10411 if(this.removable && !this.editable && !this.tickable){
10412 var close = this.closeTriggerEl();
10415 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10416 close.on('click', this.removeBtnClick, this, close);
10420 //this.trigger.addClassOnOver('x-form-trigger-over');
10421 //this.trigger.addClassOnClick('x-form-trigger-click');
10424 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10428 closeTriggerEl : function()
10430 var close = this.el.select('.roo-combo-removable-btn', true).first();
10431 return close ? close : false;
10434 removeBtnClick : function(e, h, el)
10436 e.preventDefault();
10438 if(this.fireEvent("remove", this) !== false){
10440 this.fireEvent("afterremove", this)
10444 createList : function()
10446 this.list = Roo.get(document.body).createChild({
10448 cls: 'typeahead typeahead-long dropdown-menu',
10449 style: 'display:none'
10452 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10457 initTrigger : function(){
10462 onDestroy : function(){
10464 this.trigger.removeAllListeners();
10465 // this.trigger.remove();
10468 // this.wrap.remove();
10470 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10474 onFocus : function(){
10475 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10477 if(!this.mimicing){
10478 this.wrap.addClass('x-trigger-wrap-focus');
10479 this.mimicing = true;
10480 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10481 if(this.monitorTab){
10482 this.el.on("keydown", this.checkTab, this);
10489 checkTab : function(e){
10490 if(e.getKey() == e.TAB){
10491 this.triggerBlur();
10496 onBlur : function(){
10501 mimicBlur : function(e, t){
10503 if(!this.wrap.contains(t) && this.validateBlur()){
10504 this.triggerBlur();
10510 triggerBlur : function(){
10511 this.mimicing = false;
10512 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10513 if(this.monitorTab){
10514 this.el.un("keydown", this.checkTab, this);
10516 //this.wrap.removeClass('x-trigger-wrap-focus');
10517 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10521 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10522 validateBlur : function(e, t){
10527 onDisable : function(){
10528 this.inputEl().dom.disabled = true;
10529 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10531 // this.wrap.addClass('x-item-disabled');
10536 onEnable : function(){
10537 this.inputEl().dom.disabled = false;
10538 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10540 // this.el.removeClass('x-item-disabled');
10545 onShow : function(){
10546 var ae = this.getActionEl();
10549 ae.dom.style.display = '';
10550 ae.dom.style.visibility = 'visible';
10556 onHide : function(){
10557 var ae = this.getActionEl();
10558 ae.dom.style.display = 'none';
10562 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10563 * by an implementing function.
10565 * @param {EventObject} e
10567 onTriggerClick : Roo.emptyFn
10571 * Ext JS Library 1.1.1
10572 * Copyright(c) 2006-2007, Ext JS, LLC.
10574 * Originally Released Under LGPL - original licence link has changed is not relivant.
10577 * <script type="text/javascript">
10582 * @class Roo.data.SortTypes
10584 * Defines the default sorting (casting?) comparison functions used when sorting data.
10586 Roo.data.SortTypes = {
10588 * Default sort that does nothing
10589 * @param {Mixed} s The value being converted
10590 * @return {Mixed} The comparison value
10592 none : function(s){
10597 * The regular expression used to strip tags
10601 stripTagsRE : /<\/?[^>]+>/gi,
10604 * Strips all HTML tags to sort on text only
10605 * @param {Mixed} s The value being converted
10606 * @return {String} The comparison value
10608 asText : function(s){
10609 return String(s).replace(this.stripTagsRE, "");
10613 * Strips all HTML tags to sort on text only - Case insensitive
10614 * @param {Mixed} s The value being converted
10615 * @return {String} The comparison value
10617 asUCText : function(s){
10618 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10622 * Case insensitive string
10623 * @param {Mixed} s The value being converted
10624 * @return {String} The comparison value
10626 asUCString : function(s) {
10627 return String(s).toUpperCase();
10632 * @param {Mixed} s The value being converted
10633 * @return {Number} The comparison value
10635 asDate : function(s) {
10639 if(s instanceof Date){
10640 return s.getTime();
10642 return Date.parse(String(s));
10647 * @param {Mixed} s The value being converted
10648 * @return {Float} The comparison value
10650 asFloat : function(s) {
10651 var val = parseFloat(String(s).replace(/,/g, ""));
10660 * @param {Mixed} s The value being converted
10661 * @return {Number} The comparison value
10663 asInt : function(s) {
10664 var val = parseInt(String(s).replace(/,/g, ""));
10672 * Ext JS Library 1.1.1
10673 * Copyright(c) 2006-2007, Ext JS, LLC.
10675 * Originally Released Under LGPL - original licence link has changed is not relivant.
10678 * <script type="text/javascript">
10682 * @class Roo.data.Record
10683 * Instances of this class encapsulate both record <em>definition</em> information, and record
10684 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10685 * to access Records cached in an {@link Roo.data.Store} object.<br>
10687 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10688 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10691 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10693 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10694 * {@link #create}. The parameters are the same.
10695 * @param {Array} data An associative Array of data values keyed by the field name.
10696 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10697 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10698 * not specified an integer id is generated.
10700 Roo.data.Record = function(data, id){
10701 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10706 * Generate a constructor for a specific record layout.
10707 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10708 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10709 * Each field definition object may contain the following properties: <ul>
10710 * <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,
10711 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10712 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10713 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10714 * is being used, then this is a string containing the javascript expression to reference the data relative to
10715 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10716 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10717 * this may be omitted.</p></li>
10718 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10719 * <ul><li>auto (Default, implies no conversion)</li>
10724 * <li>date</li></ul></p></li>
10725 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10726 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10727 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10728 * by the Reader into an object that will be stored in the Record. It is passed the
10729 * following parameters:<ul>
10730 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10732 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10734 * <br>usage:<br><pre><code>
10735 var TopicRecord = Roo.data.Record.create(
10736 {name: 'title', mapping: 'topic_title'},
10737 {name: 'author', mapping: 'username'},
10738 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10739 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10740 {name: 'lastPoster', mapping: 'user2'},
10741 {name: 'excerpt', mapping: 'post_text'}
10744 var myNewRecord = new TopicRecord({
10745 title: 'Do my job please',
10748 lastPost: new Date(),
10749 lastPoster: 'Animal',
10750 excerpt: 'No way dude!'
10752 myStore.add(myNewRecord);
10757 Roo.data.Record.create = function(o){
10758 var f = function(){
10759 f.superclass.constructor.apply(this, arguments);
10761 Roo.extend(f, Roo.data.Record);
10762 var p = f.prototype;
10763 p.fields = new Roo.util.MixedCollection(false, function(field){
10766 for(var i = 0, len = o.length; i < len; i++){
10767 p.fields.add(new Roo.data.Field(o[i]));
10769 f.getField = function(name){
10770 return p.fields.get(name);
10775 Roo.data.Record.AUTO_ID = 1000;
10776 Roo.data.Record.EDIT = 'edit';
10777 Roo.data.Record.REJECT = 'reject';
10778 Roo.data.Record.COMMIT = 'commit';
10780 Roo.data.Record.prototype = {
10782 * Readonly flag - true if this record has been modified.
10791 join : function(store){
10792 this.store = store;
10796 * Set the named field to the specified value.
10797 * @param {String} name The name of the field to set.
10798 * @param {Object} value The value to set the field to.
10800 set : function(name, value){
10801 if(this.data[name] == value){
10805 if(!this.modified){
10806 this.modified = {};
10808 if(typeof this.modified[name] == 'undefined'){
10809 this.modified[name] = this.data[name];
10811 this.data[name] = value;
10812 if(!this.editing && this.store){
10813 this.store.afterEdit(this);
10818 * Get the value of the named field.
10819 * @param {String} name The name of the field to get the value of.
10820 * @return {Object} The value of the field.
10822 get : function(name){
10823 return this.data[name];
10827 beginEdit : function(){
10828 this.editing = true;
10829 this.modified = {};
10833 cancelEdit : function(){
10834 this.editing = false;
10835 delete this.modified;
10839 endEdit : function(){
10840 this.editing = false;
10841 if(this.dirty && this.store){
10842 this.store.afterEdit(this);
10847 * Usually called by the {@link Roo.data.Store} which owns the Record.
10848 * Rejects all changes made to the Record since either creation, or the last commit operation.
10849 * Modified fields are reverted to their original values.
10851 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10852 * of reject operations.
10854 reject : function(){
10855 var m = this.modified;
10857 if(typeof m[n] != "function"){
10858 this.data[n] = m[n];
10861 this.dirty = false;
10862 delete this.modified;
10863 this.editing = false;
10865 this.store.afterReject(this);
10870 * Usually called by the {@link Roo.data.Store} which owns the Record.
10871 * Commits all changes made to the Record since either creation, or the last commit operation.
10873 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10874 * of commit operations.
10876 commit : function(){
10877 this.dirty = false;
10878 delete this.modified;
10879 this.editing = false;
10881 this.store.afterCommit(this);
10886 hasError : function(){
10887 return this.error != null;
10891 clearError : function(){
10896 * Creates a copy of this record.
10897 * @param {String} id (optional) A new record id if you don't want to use this record's id
10900 copy : function(newId) {
10901 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10905 * Ext JS Library 1.1.1
10906 * Copyright(c) 2006-2007, Ext JS, LLC.
10908 * Originally Released Under LGPL - original licence link has changed is not relivant.
10911 * <script type="text/javascript">
10917 * @class Roo.data.Store
10918 * @extends Roo.util.Observable
10919 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10920 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10922 * 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
10923 * has no knowledge of the format of the data returned by the Proxy.<br>
10925 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10926 * instances from the data object. These records are cached and made available through accessor functions.
10928 * Creates a new Store.
10929 * @param {Object} config A config object containing the objects needed for the Store to access data,
10930 * and read the data into Records.
10932 Roo.data.Store = function(config){
10933 this.data = new Roo.util.MixedCollection(false);
10934 this.data.getKey = function(o){
10937 this.baseParams = {};
10939 this.paramNames = {
10944 "multisort" : "_multisort"
10947 if(config && config.data){
10948 this.inlineData = config.data;
10949 delete config.data;
10952 Roo.apply(this, config);
10954 if(this.reader){ // reader passed
10955 this.reader = Roo.factory(this.reader, Roo.data);
10956 this.reader.xmodule = this.xmodule || false;
10957 if(!this.recordType){
10958 this.recordType = this.reader.recordType;
10960 if(this.reader.onMetaChange){
10961 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10965 if(this.recordType){
10966 this.fields = this.recordType.prototype.fields;
10968 this.modified = [];
10972 * @event datachanged
10973 * Fires when the data cache has changed, and a widget which is using this Store
10974 * as a Record cache should refresh its view.
10975 * @param {Store} this
10977 datachanged : true,
10979 * @event metachange
10980 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10981 * @param {Store} this
10982 * @param {Object} meta The JSON metadata
10987 * Fires when Records have been added to the Store
10988 * @param {Store} this
10989 * @param {Roo.data.Record[]} records The array of Records added
10990 * @param {Number} index The index at which the record(s) were added
10995 * Fires when a Record has been removed from the Store
10996 * @param {Store} this
10997 * @param {Roo.data.Record} record The Record that was removed
10998 * @param {Number} index The index at which the record was removed
11003 * Fires when a Record has been updated
11004 * @param {Store} this
11005 * @param {Roo.data.Record} record The Record that was updated
11006 * @param {String} operation The update operation being performed. Value may be one of:
11008 Roo.data.Record.EDIT
11009 Roo.data.Record.REJECT
11010 Roo.data.Record.COMMIT
11016 * Fires when the data cache has been cleared.
11017 * @param {Store} this
11021 * @event beforeload
11022 * Fires before a request is made for a new data object. If the beforeload handler returns false
11023 * the load action will be canceled.
11024 * @param {Store} this
11025 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11029 * @event beforeloadadd
11030 * Fires after a new set of Records has been loaded.
11031 * @param {Store} this
11032 * @param {Roo.data.Record[]} records The Records that were loaded
11033 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11035 beforeloadadd : true,
11038 * Fires after a new set of Records has been loaded, before they are added to the store.
11039 * @param {Store} this
11040 * @param {Roo.data.Record[]} records The Records that were loaded
11041 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11042 * @params {Object} return from reader
11046 * @event loadexception
11047 * Fires if an exception occurs in the Proxy during loading.
11048 * Called with the signature of the Proxy's "loadexception" event.
11049 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11052 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11053 * @param {Object} load options
11054 * @param {Object} jsonData from your request (normally this contains the Exception)
11056 loadexception : true
11060 this.proxy = Roo.factory(this.proxy, Roo.data);
11061 this.proxy.xmodule = this.xmodule || false;
11062 this.relayEvents(this.proxy, ["loadexception"]);
11064 this.sortToggle = {};
11065 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11067 Roo.data.Store.superclass.constructor.call(this);
11069 if(this.inlineData){
11070 this.loadData(this.inlineData);
11071 delete this.inlineData;
11075 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11077 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11078 * without a remote query - used by combo/forms at present.
11082 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11085 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11088 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11089 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11092 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11093 * on any HTTP request
11096 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11099 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11103 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11104 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11106 remoteSort : false,
11109 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11110 * loaded or when a record is removed. (defaults to false).
11112 pruneModifiedRecords : false,
11115 lastOptions : null,
11118 * Add Records to the Store and fires the add event.
11119 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11121 add : function(records){
11122 records = [].concat(records);
11123 for(var i = 0, len = records.length; i < len; i++){
11124 records[i].join(this);
11126 var index = this.data.length;
11127 this.data.addAll(records);
11128 this.fireEvent("add", this, records, index);
11132 * Remove a Record from the Store and fires the remove event.
11133 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11135 remove : function(record){
11136 var index = this.data.indexOf(record);
11137 this.data.removeAt(index);
11139 if(this.pruneModifiedRecords){
11140 this.modified.remove(record);
11142 this.fireEvent("remove", this, record, index);
11146 * Remove all Records from the Store and fires the clear event.
11148 removeAll : function(){
11150 if(this.pruneModifiedRecords){
11151 this.modified = [];
11153 this.fireEvent("clear", this);
11157 * Inserts Records to the Store at the given index and fires the add event.
11158 * @param {Number} index The start index at which to insert the passed Records.
11159 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11161 insert : function(index, records){
11162 records = [].concat(records);
11163 for(var i = 0, len = records.length; i < len; i++){
11164 this.data.insert(index, records[i]);
11165 records[i].join(this);
11167 this.fireEvent("add", this, records, index);
11171 * Get the index within the cache of the passed Record.
11172 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11173 * @return {Number} The index of the passed Record. Returns -1 if not found.
11175 indexOf : function(record){
11176 return this.data.indexOf(record);
11180 * Get the index within the cache of the Record with the passed id.
11181 * @param {String} id The id of the Record to find.
11182 * @return {Number} The index of the Record. Returns -1 if not found.
11184 indexOfId : function(id){
11185 return this.data.indexOfKey(id);
11189 * Get the Record with the specified id.
11190 * @param {String} id The id of the Record to find.
11191 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11193 getById : function(id){
11194 return this.data.key(id);
11198 * Get the Record at the specified index.
11199 * @param {Number} index The index of the Record to find.
11200 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11202 getAt : function(index){
11203 return this.data.itemAt(index);
11207 * Returns a range of Records between specified indices.
11208 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11209 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11210 * @return {Roo.data.Record[]} An array of Records
11212 getRange : function(start, end){
11213 return this.data.getRange(start, end);
11217 storeOptions : function(o){
11218 o = Roo.apply({}, o);
11221 this.lastOptions = o;
11225 * Loads the Record cache from the configured Proxy using the configured Reader.
11227 * If using remote paging, then the first load call must specify the <em>start</em>
11228 * and <em>limit</em> properties in the options.params property to establish the initial
11229 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11231 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11232 * and this call will return before the new data has been loaded. Perform any post-processing
11233 * in a callback function, or in a "load" event handler.</strong>
11235 * @param {Object} options An object containing properties which control loading options:<ul>
11236 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11237 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11238 * passed the following arguments:<ul>
11239 * <li>r : Roo.data.Record[]</li>
11240 * <li>options: Options object from the load call</li>
11241 * <li>success: Boolean success indicator</li></ul></li>
11242 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11243 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11246 load : function(options){
11247 options = options || {};
11248 if(this.fireEvent("beforeload", this, options) !== false){
11249 this.storeOptions(options);
11250 var p = Roo.apply(options.params || {}, this.baseParams);
11251 // if meta was not loaded from remote source.. try requesting it.
11252 if (!this.reader.metaFromRemote) {
11253 p._requestMeta = 1;
11255 if(this.sortInfo && this.remoteSort){
11256 var pn = this.paramNames;
11257 p[pn["sort"]] = this.sortInfo.field;
11258 p[pn["dir"]] = this.sortInfo.direction;
11260 if (this.multiSort) {
11261 var pn = this.paramNames;
11262 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11265 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11270 * Reloads the Record cache from the configured Proxy using the configured Reader and
11271 * the options from the last load operation performed.
11272 * @param {Object} options (optional) An object containing properties which may override the options
11273 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11274 * the most recently used options are reused).
11276 reload : function(options){
11277 this.load(Roo.applyIf(options||{}, this.lastOptions));
11281 // Called as a callback by the Reader during a load operation.
11282 loadRecords : function(o, options, success){
11283 if(!o || success === false){
11284 if(success !== false){
11285 this.fireEvent("load", this, [], options, o);
11287 if(options.callback){
11288 options.callback.call(options.scope || this, [], options, false);
11292 // if data returned failure - throw an exception.
11293 if (o.success === false) {
11294 // show a message if no listener is registered.
11295 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11296 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11298 // loadmask wil be hooked into this..
11299 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11302 var r = o.records, t = o.totalRecords || r.length;
11304 this.fireEvent("beforeloadadd", this, r, options, o);
11306 if(!options || options.add !== true){
11307 if(this.pruneModifiedRecords){
11308 this.modified = [];
11310 for(var i = 0, len = r.length; i < len; i++){
11314 this.data = this.snapshot;
11315 delete this.snapshot;
11318 this.data.addAll(r);
11319 this.totalLength = t;
11321 this.fireEvent("datachanged", this);
11323 this.totalLength = Math.max(t, this.data.length+r.length);
11327 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11329 var e = new Roo.data.Record({});
11331 e.set(this.parent.displayField, this.parent.emptyTitle);
11332 e.set(this.parent.valueField, '');
11337 this.fireEvent("load", this, r, options, o);
11338 if(options.callback){
11339 options.callback.call(options.scope || this, r, options, true);
11345 * Loads data from a passed data block. A Reader which understands the format of the data
11346 * must have been configured in the constructor.
11347 * @param {Object} data The data block from which to read the Records. The format of the data expected
11348 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11349 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11351 loadData : function(o, append){
11352 var r = this.reader.readRecords(o);
11353 this.loadRecords(r, {add: append}, true);
11357 * Gets the number of cached records.
11359 * <em>If using paging, this may not be the total size of the dataset. If the data object
11360 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11361 * the data set size</em>
11363 getCount : function(){
11364 return this.data.length || 0;
11368 * Gets the total number of records in the dataset as returned by the server.
11370 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11371 * the dataset size</em>
11373 getTotalCount : function(){
11374 return this.totalLength || 0;
11378 * Returns the sort state of the Store as an object with two properties:
11380 field {String} The name of the field by which the Records are sorted
11381 direction {String} The sort order, "ASC" or "DESC"
11384 getSortState : function(){
11385 return this.sortInfo;
11389 applySort : function(){
11390 if(this.sortInfo && !this.remoteSort){
11391 var s = this.sortInfo, f = s.field;
11392 var st = this.fields.get(f).sortType;
11393 var fn = function(r1, r2){
11394 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11395 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11397 this.data.sort(s.direction, fn);
11398 if(this.snapshot && this.snapshot != this.data){
11399 this.snapshot.sort(s.direction, fn);
11405 * Sets the default sort column and order to be used by the next load operation.
11406 * @param {String} fieldName The name of the field to sort by.
11407 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11409 setDefaultSort : function(field, dir){
11410 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11414 * Sort the Records.
11415 * If remote sorting is used, the sort is performed on the server, and the cache is
11416 * reloaded. If local sorting is used, the cache is sorted internally.
11417 * @param {String} fieldName The name of the field to sort by.
11418 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11420 sort : function(fieldName, dir){
11421 var f = this.fields.get(fieldName);
11423 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11425 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11426 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11431 this.sortToggle[f.name] = dir;
11432 this.sortInfo = {field: f.name, direction: dir};
11433 if(!this.remoteSort){
11435 this.fireEvent("datachanged", this);
11437 this.load(this.lastOptions);
11442 * Calls the specified function for each of the Records in the cache.
11443 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11444 * Returning <em>false</em> aborts and exits the iteration.
11445 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11447 each : function(fn, scope){
11448 this.data.each(fn, scope);
11452 * Gets all records modified since the last commit. Modified records are persisted across load operations
11453 * (e.g., during paging).
11454 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11456 getModifiedRecords : function(){
11457 return this.modified;
11461 createFilterFn : function(property, value, anyMatch){
11462 if(!value.exec){ // not a regex
11463 value = String(value);
11464 if(value.length == 0){
11467 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11469 return function(r){
11470 return value.test(r.data[property]);
11475 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11476 * @param {String} property A field on your records
11477 * @param {Number} start The record index to start at (defaults to 0)
11478 * @param {Number} end The last record index to include (defaults to length - 1)
11479 * @return {Number} The sum
11481 sum : function(property, start, end){
11482 var rs = this.data.items, v = 0;
11483 start = start || 0;
11484 end = (end || end === 0) ? end : rs.length-1;
11486 for(var i = start; i <= end; i++){
11487 v += (rs[i].data[property] || 0);
11493 * Filter the records by a specified property.
11494 * @param {String} field A field on your records
11495 * @param {String/RegExp} value Either a string that the field
11496 * should start with or a RegExp to test against the field
11497 * @param {Boolean} anyMatch True to match any part not just the beginning
11499 filter : function(property, value, anyMatch){
11500 var fn = this.createFilterFn(property, value, anyMatch);
11501 return fn ? this.filterBy(fn) : this.clearFilter();
11505 * Filter by a function. The specified function will be called with each
11506 * record in this data source. If the function returns true the record is included,
11507 * otherwise it is filtered.
11508 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11509 * @param {Object} scope (optional) The scope of the function (defaults to this)
11511 filterBy : function(fn, scope){
11512 this.snapshot = this.snapshot || this.data;
11513 this.data = this.queryBy(fn, scope||this);
11514 this.fireEvent("datachanged", this);
11518 * Query the records by a specified property.
11519 * @param {String} field A field on your records
11520 * @param {String/RegExp} value Either a string that the field
11521 * should start with or a RegExp to test against the field
11522 * @param {Boolean} anyMatch True to match any part not just the beginning
11523 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11525 query : function(property, value, anyMatch){
11526 var fn = this.createFilterFn(property, value, anyMatch);
11527 return fn ? this.queryBy(fn) : this.data.clone();
11531 * Query by a function. The specified function will be called with each
11532 * record in this data source. If the function returns true the record is included
11534 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11535 * @param {Object} scope (optional) The scope of the function (defaults to this)
11536 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11538 queryBy : function(fn, scope){
11539 var data = this.snapshot || this.data;
11540 return data.filterBy(fn, scope||this);
11544 * Collects unique values for a particular dataIndex from this store.
11545 * @param {String} dataIndex The property to collect
11546 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11547 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11548 * @return {Array} An array of the unique values
11550 collect : function(dataIndex, allowNull, bypassFilter){
11551 var d = (bypassFilter === true && this.snapshot) ?
11552 this.snapshot.items : this.data.items;
11553 var v, sv, r = [], l = {};
11554 for(var i = 0, len = d.length; i < len; i++){
11555 v = d[i].data[dataIndex];
11557 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11566 * Revert to a view of the Record cache with no filtering applied.
11567 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11569 clearFilter : function(suppressEvent){
11570 if(this.snapshot && this.snapshot != this.data){
11571 this.data = this.snapshot;
11572 delete this.snapshot;
11573 if(suppressEvent !== true){
11574 this.fireEvent("datachanged", this);
11580 afterEdit : function(record){
11581 if(this.modified.indexOf(record) == -1){
11582 this.modified.push(record);
11584 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11588 afterReject : function(record){
11589 this.modified.remove(record);
11590 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11594 afterCommit : function(record){
11595 this.modified.remove(record);
11596 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11600 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11601 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11603 commitChanges : function(){
11604 var m = this.modified.slice(0);
11605 this.modified = [];
11606 for(var i = 0, len = m.length; i < len; i++){
11612 * Cancel outstanding changes on all changed records.
11614 rejectChanges : function(){
11615 var m = this.modified.slice(0);
11616 this.modified = [];
11617 for(var i = 0, len = m.length; i < len; i++){
11622 onMetaChange : function(meta, rtype, o){
11623 this.recordType = rtype;
11624 this.fields = rtype.prototype.fields;
11625 delete this.snapshot;
11626 this.sortInfo = meta.sortInfo || this.sortInfo;
11627 this.modified = [];
11628 this.fireEvent('metachange', this, this.reader.meta);
11631 moveIndex : function(data, type)
11633 var index = this.indexOf(data);
11635 var newIndex = index + type;
11639 this.insert(newIndex, data);
11644 * Ext JS Library 1.1.1
11645 * Copyright(c) 2006-2007, Ext JS, LLC.
11647 * Originally Released Under LGPL - original licence link has changed is not relivant.
11650 * <script type="text/javascript">
11654 * @class Roo.data.SimpleStore
11655 * @extends Roo.data.Store
11656 * Small helper class to make creating Stores from Array data easier.
11657 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11658 * @cfg {Array} fields An array of field definition objects, or field name strings.
11659 * @cfg {Array} data The multi-dimensional array of data
11661 * @param {Object} config
11663 Roo.data.SimpleStore = function(config){
11664 Roo.data.SimpleStore.superclass.constructor.call(this, {
11666 reader: new Roo.data.ArrayReader({
11669 Roo.data.Record.create(config.fields)
11671 proxy : new Roo.data.MemoryProxy(config.data)
11675 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11677 * Ext JS Library 1.1.1
11678 * Copyright(c) 2006-2007, Ext JS, LLC.
11680 * Originally Released Under LGPL - original licence link has changed is not relivant.
11683 * <script type="text/javascript">
11688 * @extends Roo.data.Store
11689 * @class Roo.data.JsonStore
11690 * Small helper class to make creating Stores for JSON data easier. <br/>
11692 var store = new Roo.data.JsonStore({
11693 url: 'get-images.php',
11695 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11698 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11699 * JsonReader and HttpProxy (unless inline data is provided).</b>
11700 * @cfg {Array} fields An array of field definition objects, or field name strings.
11702 * @param {Object} config
11704 Roo.data.JsonStore = function(c){
11705 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11706 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11707 reader: new Roo.data.JsonReader(c, c.fields)
11710 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11712 * Ext JS Library 1.1.1
11713 * Copyright(c) 2006-2007, Ext JS, LLC.
11715 * Originally Released Under LGPL - original licence link has changed is not relivant.
11718 * <script type="text/javascript">
11722 Roo.data.Field = function(config){
11723 if(typeof config == "string"){
11724 config = {name: config};
11726 Roo.apply(this, config);
11729 this.type = "auto";
11732 var st = Roo.data.SortTypes;
11733 // named sortTypes are supported, here we look them up
11734 if(typeof this.sortType == "string"){
11735 this.sortType = st[this.sortType];
11738 // set default sortType for strings and dates
11739 if(!this.sortType){
11742 this.sortType = st.asUCString;
11745 this.sortType = st.asDate;
11748 this.sortType = st.none;
11753 var stripRe = /[\$,%]/g;
11755 // prebuilt conversion function for this field, instead of
11756 // switching every time we're reading a value
11758 var cv, dateFormat = this.dateFormat;
11763 cv = function(v){ return v; };
11766 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11770 return v !== undefined && v !== null && v !== '' ?
11771 parseInt(String(v).replace(stripRe, ""), 10) : '';
11776 return v !== undefined && v !== null && v !== '' ?
11777 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11782 cv = function(v){ return v === true || v === "true" || v == 1; };
11789 if(v instanceof Date){
11793 if(dateFormat == "timestamp"){
11794 return new Date(v*1000);
11796 return Date.parseDate(v, dateFormat);
11798 var parsed = Date.parse(v);
11799 return parsed ? new Date(parsed) : null;
11808 Roo.data.Field.prototype = {
11816 * Ext JS Library 1.1.1
11817 * Copyright(c) 2006-2007, Ext JS, LLC.
11819 * Originally Released Under LGPL - original licence link has changed is not relivant.
11822 * <script type="text/javascript">
11825 // Base class for reading structured data from a data source. This class is intended to be
11826 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11829 * @class Roo.data.DataReader
11830 * Base class for reading structured data from a data source. This class is intended to be
11831 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11834 Roo.data.DataReader = function(meta, recordType){
11838 this.recordType = recordType instanceof Array ?
11839 Roo.data.Record.create(recordType) : recordType;
11842 Roo.data.DataReader.prototype = {
11844 * Create an empty record
11845 * @param {Object} data (optional) - overlay some values
11846 * @return {Roo.data.Record} record created.
11848 newRow : function(d) {
11850 this.recordType.prototype.fields.each(function(c) {
11852 case 'int' : da[c.name] = 0; break;
11853 case 'date' : da[c.name] = new Date(); break;
11854 case 'float' : da[c.name] = 0.0; break;
11855 case 'boolean' : da[c.name] = false; break;
11856 default : da[c.name] = ""; break;
11860 return new this.recordType(Roo.apply(da, d));
11865 * Ext JS Library 1.1.1
11866 * Copyright(c) 2006-2007, Ext JS, LLC.
11868 * Originally Released Under LGPL - original licence link has changed is not relivant.
11871 * <script type="text/javascript">
11875 * @class Roo.data.DataProxy
11876 * @extends Roo.data.Observable
11877 * This class is an abstract base class for implementations which provide retrieval of
11878 * unformatted data objects.<br>
11880 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11881 * (of the appropriate type which knows how to parse the data object) to provide a block of
11882 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11884 * Custom implementations must implement the load method as described in
11885 * {@link Roo.data.HttpProxy#load}.
11887 Roo.data.DataProxy = function(){
11890 * @event beforeload
11891 * Fires before a network request is made to retrieve a data object.
11892 * @param {Object} This DataProxy object.
11893 * @param {Object} params The params parameter to the load function.
11898 * Fires before the load method's callback is called.
11899 * @param {Object} This DataProxy object.
11900 * @param {Object} o The data object.
11901 * @param {Object} arg The callback argument object passed to the load function.
11905 * @event loadexception
11906 * Fires if an Exception occurs during data retrieval.
11907 * @param {Object} This DataProxy object.
11908 * @param {Object} o The data object.
11909 * @param {Object} arg The callback argument object passed to the load function.
11910 * @param {Object} e The Exception.
11912 loadexception : true
11914 Roo.data.DataProxy.superclass.constructor.call(this);
11917 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11920 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11924 * Ext JS Library 1.1.1
11925 * Copyright(c) 2006-2007, Ext JS, LLC.
11927 * Originally Released Under LGPL - original licence link has changed is not relivant.
11930 * <script type="text/javascript">
11933 * @class Roo.data.MemoryProxy
11934 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11935 * to the Reader when its load method is called.
11937 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11939 Roo.data.MemoryProxy = function(data){
11943 Roo.data.MemoryProxy.superclass.constructor.call(this);
11947 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11950 * Load data from the requested source (in this case an in-memory
11951 * data object passed to the constructor), read the data object into
11952 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11953 * process that block using the passed callback.
11954 * @param {Object} params This parameter is not used by the MemoryProxy class.
11955 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11956 * object into a block of Roo.data.Records.
11957 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11958 * The function must be passed <ul>
11959 * <li>The Record block object</li>
11960 * <li>The "arg" argument from the load function</li>
11961 * <li>A boolean success indicator</li>
11963 * @param {Object} scope The scope in which to call the callback
11964 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11966 load : function(params, reader, callback, scope, arg){
11967 params = params || {};
11970 result = reader.readRecords(this.data);
11972 this.fireEvent("loadexception", this, arg, null, e);
11973 callback.call(scope, null, arg, false);
11976 callback.call(scope, result, arg, true);
11980 update : function(params, records){
11985 * Ext JS Library 1.1.1
11986 * Copyright(c) 2006-2007, Ext JS, LLC.
11988 * Originally Released Under LGPL - original licence link has changed is not relivant.
11991 * <script type="text/javascript">
11994 * @class Roo.data.HttpProxy
11995 * @extends Roo.data.DataProxy
11996 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11997 * configured to reference a certain URL.<br><br>
11999 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12000 * from which the running page was served.<br><br>
12002 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12004 * Be aware that to enable the browser to parse an XML document, the server must set
12005 * the Content-Type header in the HTTP response to "text/xml".
12007 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12008 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12009 * will be used to make the request.
12011 Roo.data.HttpProxy = function(conn){
12012 Roo.data.HttpProxy.superclass.constructor.call(this);
12013 // is conn a conn config or a real conn?
12015 this.useAjax = !conn || !conn.events;
12019 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12020 // thse are take from connection...
12023 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12026 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12027 * extra parameters to each request made by this object. (defaults to undefined)
12030 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12031 * to each request made by this object. (defaults to undefined)
12034 * @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)
12037 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12040 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12046 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12050 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12051 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12052 * a finer-grained basis than the DataProxy events.
12054 getConnection : function(){
12055 return this.useAjax ? Roo.Ajax : this.conn;
12059 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12060 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12061 * process that block using the passed callback.
12062 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12063 * for the request to the remote server.
12064 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12065 * object into a block of Roo.data.Records.
12066 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12067 * The function must be passed <ul>
12068 * <li>The Record block object</li>
12069 * <li>The "arg" argument from the load function</li>
12070 * <li>A boolean success indicator</li>
12072 * @param {Object} scope The scope in which to call the callback
12073 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12075 load : function(params, reader, callback, scope, arg){
12076 if(this.fireEvent("beforeload", this, params) !== false){
12078 params : params || {},
12080 callback : callback,
12085 callback : this.loadResponse,
12089 Roo.applyIf(o, this.conn);
12090 if(this.activeRequest){
12091 Roo.Ajax.abort(this.activeRequest);
12093 this.activeRequest = Roo.Ajax.request(o);
12095 this.conn.request(o);
12098 callback.call(scope||this, null, arg, false);
12103 loadResponse : function(o, success, response){
12104 delete this.activeRequest;
12106 this.fireEvent("loadexception", this, o, response);
12107 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12112 result = o.reader.read(response);
12114 this.fireEvent("loadexception", this, o, response, e);
12115 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12119 this.fireEvent("load", this, o, o.request.arg);
12120 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12124 update : function(dataSet){
12129 updateResponse : function(dataSet){
12134 * Ext JS Library 1.1.1
12135 * Copyright(c) 2006-2007, Ext JS, LLC.
12137 * Originally Released Under LGPL - original licence link has changed is not relivant.
12140 * <script type="text/javascript">
12144 * @class Roo.data.ScriptTagProxy
12145 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12146 * other than the originating domain of the running page.<br><br>
12148 * <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
12149 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12151 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12152 * source code that is used as the source inside a <script> tag.<br><br>
12154 * In order for the browser to process the returned data, the server must wrap the data object
12155 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12156 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12157 * depending on whether the callback name was passed:
12160 boolean scriptTag = false;
12161 String cb = request.getParameter("callback");
12164 response.setContentType("text/javascript");
12166 response.setContentType("application/x-json");
12168 Writer out = response.getWriter();
12170 out.write(cb + "(");
12172 out.print(dataBlock.toJsonString());
12179 * @param {Object} config A configuration object.
12181 Roo.data.ScriptTagProxy = function(config){
12182 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12183 Roo.apply(this, config);
12184 this.head = document.getElementsByTagName("head")[0];
12187 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12189 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12191 * @cfg {String} url The URL from which to request the data object.
12194 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12198 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12199 * the server the name of the callback function set up by the load call to process the returned data object.
12200 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12201 * javascript output which calls this named function passing the data object as its only parameter.
12203 callbackParam : "callback",
12205 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12206 * name to the request.
12211 * Load data from the configured URL, read the data object into
12212 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12213 * process that block using the passed callback.
12214 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12215 * for the request to the remote server.
12216 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12217 * object into a block of Roo.data.Records.
12218 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12219 * The function must be passed <ul>
12220 * <li>The Record block object</li>
12221 * <li>The "arg" argument from the load function</li>
12222 * <li>A boolean success indicator</li>
12224 * @param {Object} scope The scope in which to call the callback
12225 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12227 load : function(params, reader, callback, scope, arg){
12228 if(this.fireEvent("beforeload", this, params) !== false){
12230 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12232 var url = this.url;
12233 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12235 url += "&_dc=" + (new Date().getTime());
12237 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12240 cb : "stcCallback"+transId,
12241 scriptId : "stcScript"+transId,
12245 callback : callback,
12251 window[trans.cb] = function(o){
12252 conn.handleResponse(o, trans);
12255 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12257 if(this.autoAbort !== false){
12261 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12263 var script = document.createElement("script");
12264 script.setAttribute("src", url);
12265 script.setAttribute("type", "text/javascript");
12266 script.setAttribute("id", trans.scriptId);
12267 this.head.appendChild(script);
12269 this.trans = trans;
12271 callback.call(scope||this, null, arg, false);
12276 isLoading : function(){
12277 return this.trans ? true : false;
12281 * Abort the current server request.
12283 abort : function(){
12284 if(this.isLoading()){
12285 this.destroyTrans(this.trans);
12290 destroyTrans : function(trans, isLoaded){
12291 this.head.removeChild(document.getElementById(trans.scriptId));
12292 clearTimeout(trans.timeoutId);
12294 window[trans.cb] = undefined;
12296 delete window[trans.cb];
12299 // if hasn't been loaded, wait for load to remove it to prevent script error
12300 window[trans.cb] = function(){
12301 window[trans.cb] = undefined;
12303 delete window[trans.cb];
12310 handleResponse : function(o, trans){
12311 this.trans = false;
12312 this.destroyTrans(trans, true);
12315 result = trans.reader.readRecords(o);
12317 this.fireEvent("loadexception", this, o, trans.arg, e);
12318 trans.callback.call(trans.scope||window, null, trans.arg, false);
12321 this.fireEvent("load", this, o, trans.arg);
12322 trans.callback.call(trans.scope||window, result, trans.arg, true);
12326 handleFailure : function(trans){
12327 this.trans = false;
12328 this.destroyTrans(trans, false);
12329 this.fireEvent("loadexception", this, null, trans.arg);
12330 trans.callback.call(trans.scope||window, null, trans.arg, false);
12334 * Ext JS Library 1.1.1
12335 * Copyright(c) 2006-2007, Ext JS, LLC.
12337 * Originally Released Under LGPL - original licence link has changed is not relivant.
12340 * <script type="text/javascript">
12344 * @class Roo.data.JsonReader
12345 * @extends Roo.data.DataReader
12346 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12347 * based on mappings in a provided Roo.data.Record constructor.
12349 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12350 * in the reply previously.
12355 var RecordDef = Roo.data.Record.create([
12356 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12357 {name: 'occupation'} // This field will use "occupation" as the mapping.
12359 var myReader = new Roo.data.JsonReader({
12360 totalProperty: "results", // The property which contains the total dataset size (optional)
12361 root: "rows", // The property which contains an Array of row objects
12362 id: "id" // The property within each row object that provides an ID for the record (optional)
12366 * This would consume a JSON file like this:
12368 { 'results': 2, 'rows': [
12369 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12370 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12373 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12374 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12375 * paged from the remote server.
12376 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12377 * @cfg {String} root name of the property which contains the Array of row objects.
12378 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12379 * @cfg {Array} fields Array of field definition objects
12381 * Create a new JsonReader
12382 * @param {Object} meta Metadata configuration options
12383 * @param {Object} recordType Either an Array of field definition objects,
12384 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12386 Roo.data.JsonReader = function(meta, recordType){
12389 // set some defaults:
12390 Roo.applyIf(meta, {
12391 totalProperty: 'total',
12392 successProperty : 'success',
12397 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12399 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12402 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12403 * Used by Store query builder to append _requestMeta to params.
12406 metaFromRemote : false,
12408 * This method is only used by a DataProxy which has retrieved data from a remote server.
12409 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12410 * @return {Object} data A data block which is used by an Roo.data.Store object as
12411 * a cache of Roo.data.Records.
12413 read : function(response){
12414 var json = response.responseText;
12416 var o = /* eval:var:o */ eval("("+json+")");
12418 throw {message: "JsonReader.read: Json object not found"};
12424 this.metaFromRemote = true;
12425 this.meta = o.metaData;
12426 this.recordType = Roo.data.Record.create(o.metaData.fields);
12427 this.onMetaChange(this.meta, this.recordType, o);
12429 return this.readRecords(o);
12432 // private function a store will implement
12433 onMetaChange : function(meta, recordType, o){
12440 simpleAccess: function(obj, subsc) {
12447 getJsonAccessor: function(){
12449 return function(expr) {
12451 return(re.test(expr))
12452 ? new Function("obj", "return obj." + expr)
12457 return Roo.emptyFn;
12462 * Create a data block containing Roo.data.Records from an XML document.
12463 * @param {Object} o An object which contains an Array of row objects in the property specified
12464 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12465 * which contains the total size of the dataset.
12466 * @return {Object} data A data block which is used by an Roo.data.Store object as
12467 * a cache of Roo.data.Records.
12469 readRecords : function(o){
12471 * After any data loads, the raw JSON data is available for further custom processing.
12475 var s = this.meta, Record = this.recordType,
12476 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12478 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12480 if(s.totalProperty) {
12481 this.getTotal = this.getJsonAccessor(s.totalProperty);
12483 if(s.successProperty) {
12484 this.getSuccess = this.getJsonAccessor(s.successProperty);
12486 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12488 var g = this.getJsonAccessor(s.id);
12489 this.getId = function(rec) {
12491 return (r === undefined || r === "") ? null : r;
12494 this.getId = function(){return null;};
12497 for(var jj = 0; jj < fl; jj++){
12499 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12500 this.ef[jj] = this.getJsonAccessor(map);
12504 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12505 if(s.totalProperty){
12506 var vt = parseInt(this.getTotal(o), 10);
12511 if(s.successProperty){
12512 var vs = this.getSuccess(o);
12513 if(vs === false || vs === 'false'){
12518 for(var i = 0; i < c; i++){
12521 var id = this.getId(n);
12522 for(var j = 0; j < fl; j++){
12524 var v = this.ef[j](n);
12526 Roo.log('missing convert for ' + f.name);
12530 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12532 var record = new Record(values, id);
12534 records[i] = record;
12540 totalRecords : totalRecords
12545 * Ext JS Library 1.1.1
12546 * Copyright(c) 2006-2007, Ext JS, LLC.
12548 * Originally Released Under LGPL - original licence link has changed is not relivant.
12551 * <script type="text/javascript">
12555 * @class Roo.data.ArrayReader
12556 * @extends Roo.data.DataReader
12557 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12558 * Each element of that Array represents a row of data fields. The
12559 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12560 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12564 var RecordDef = Roo.data.Record.create([
12565 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12566 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12568 var myReader = new Roo.data.ArrayReader({
12569 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12573 * This would consume an Array like this:
12575 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12577 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12579 * Create a new JsonReader
12580 * @param {Object} meta Metadata configuration options.
12581 * @param {Object} recordType Either an Array of field definition objects
12582 * as specified to {@link Roo.data.Record#create},
12583 * or an {@link Roo.data.Record} object
12584 * created using {@link Roo.data.Record#create}.
12586 Roo.data.ArrayReader = function(meta, recordType){
12587 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12590 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12592 * Create a data block containing Roo.data.Records from an XML document.
12593 * @param {Object} o An Array of row objects which represents the dataset.
12594 * @return {Object} data A data block which is used by an Roo.data.Store object as
12595 * a cache of Roo.data.Records.
12597 readRecords : function(o){
12598 var sid = this.meta ? this.meta.id : null;
12599 var recordType = this.recordType, fields = recordType.prototype.fields;
12602 for(var i = 0; i < root.length; i++){
12605 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12606 for(var j = 0, jlen = fields.length; j < jlen; j++){
12607 var f = fields.items[j];
12608 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12609 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12611 values[f.name] = v;
12613 var record = new recordType(values, id);
12615 records[records.length] = record;
12619 totalRecords : records.length
12628 * @class Roo.bootstrap.ComboBox
12629 * @extends Roo.bootstrap.TriggerField
12630 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12631 * @cfg {Boolean} append (true|false) default false
12632 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12633 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12634 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12635 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12636 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12637 * @cfg {Boolean} animate default true
12638 * @cfg {Boolean} emptyResultText only for touch device
12639 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12640 * @cfg {String} emptyTitle default ''
12642 * Create a new ComboBox.
12643 * @param {Object} config Configuration options
12645 Roo.bootstrap.ComboBox = function(config){
12646 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12650 * Fires when the dropdown list is expanded
12651 * @param {Roo.bootstrap.ComboBox} combo This combo box
12656 * Fires when the dropdown list is collapsed
12657 * @param {Roo.bootstrap.ComboBox} combo This combo box
12661 * @event beforeselect
12662 * Fires before a list item is selected. Return false to cancel the selection.
12663 * @param {Roo.bootstrap.ComboBox} combo This combo box
12664 * @param {Roo.data.Record} record The data record returned from the underlying store
12665 * @param {Number} index The index of the selected item in the dropdown list
12667 'beforeselect' : true,
12670 * Fires when a list item is selected
12671 * @param {Roo.bootstrap.ComboBox} combo This combo box
12672 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12673 * @param {Number} index The index of the selected item in the dropdown list
12677 * @event beforequery
12678 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12679 * The event object passed has these properties:
12680 * @param {Roo.bootstrap.ComboBox} combo This combo box
12681 * @param {String} query The query
12682 * @param {Boolean} forceAll true to force "all" query
12683 * @param {Boolean} cancel true to cancel the query
12684 * @param {Object} e The query event object
12686 'beforequery': true,
12689 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12690 * @param {Roo.bootstrap.ComboBox} combo This combo box
12695 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12696 * @param {Roo.bootstrap.ComboBox} combo This combo box
12697 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12702 * Fires when the remove value from the combobox array
12703 * @param {Roo.bootstrap.ComboBox} combo This combo box
12707 * @event afterremove
12708 * Fires when the remove value from the combobox array
12709 * @param {Roo.bootstrap.ComboBox} combo This combo box
12711 'afterremove' : true,
12713 * @event specialfilter
12714 * Fires when specialfilter
12715 * @param {Roo.bootstrap.ComboBox} combo This combo box
12717 'specialfilter' : true,
12720 * Fires when tick the element
12721 * @param {Roo.bootstrap.ComboBox} combo This combo box
12725 * @event touchviewdisplay
12726 * Fires when touch view require special display (default is using displayField)
12727 * @param {Roo.bootstrap.ComboBox} combo This combo box
12728 * @param {Object} cfg set html .
12730 'touchviewdisplay' : true
12735 this.tickItems = [];
12737 this.selectedIndex = -1;
12738 if(this.mode == 'local'){
12739 if(config.queryDelay === undefined){
12740 this.queryDelay = 10;
12742 if(config.minChars === undefined){
12748 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12751 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12752 * rendering into an Roo.Editor, defaults to false)
12755 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12756 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12759 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12762 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12763 * the dropdown list (defaults to undefined, with no header element)
12767 * @cfg {String/Roo.Template} tpl The template to use to render the output
12771 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12773 listWidth: undefined,
12775 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12776 * mode = 'remote' or 'text' if mode = 'local')
12778 displayField: undefined,
12781 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12782 * mode = 'remote' or 'value' if mode = 'local').
12783 * Note: use of a valueField requires the user make a selection
12784 * in order for a value to be mapped.
12786 valueField: undefined,
12788 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12793 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12794 * field's data value (defaults to the underlying DOM element's name)
12796 hiddenName: undefined,
12798 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12802 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12804 selectedClass: 'active',
12807 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12811 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12812 * anchor positions (defaults to 'tl-bl')
12814 listAlign: 'tl-bl?',
12816 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12820 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12821 * query specified by the allQuery config option (defaults to 'query')
12823 triggerAction: 'query',
12825 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12826 * (defaults to 4, does not apply if editable = false)
12830 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12831 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12835 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12836 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12840 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12841 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12845 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12846 * when editable = true (defaults to false)
12848 selectOnFocus:false,
12850 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12852 queryParam: 'query',
12854 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12855 * when mode = 'remote' (defaults to 'Loading...')
12857 loadingText: 'Loading...',
12859 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12863 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12867 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12868 * traditional select (defaults to true)
12872 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12876 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12880 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12881 * listWidth has a higher value)
12885 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12886 * allow the user to set arbitrary text into the field (defaults to false)
12888 forceSelection:false,
12890 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12891 * if typeAhead = true (defaults to 250)
12893 typeAheadDelay : 250,
12895 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12896 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12898 valueNotFoundText : undefined,
12900 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12902 blockFocus : false,
12905 * @cfg {Boolean} disableClear Disable showing of clear button.
12907 disableClear : false,
12909 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12911 alwaysQuery : false,
12914 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12919 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12921 invalidClass : "has-warning",
12924 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12926 validClass : "has-success",
12929 * @cfg {Boolean} specialFilter (true|false) special filter default false
12931 specialFilter : false,
12934 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12936 mobileTouchView : true,
12939 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12941 useNativeIOS : false,
12943 ios_options : false,
12955 btnPosition : 'right',
12956 triggerList : true,
12957 showToggleBtn : true,
12959 emptyResultText: 'Empty',
12960 triggerText : 'Select',
12963 // element that contains real text value.. (when hidden is used..)
12965 getAutoCreate : function()
12970 * Render classic select for iso
12973 if(Roo.isIOS && this.useNativeIOS){
12974 cfg = this.getAutoCreateNativeIOS();
12982 if(Roo.isTouch && this.mobileTouchView){
12983 cfg = this.getAutoCreateTouchView();
12990 if(!this.tickable){
12991 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12996 * ComboBox with tickable selections
12999 var align = this.labelAlign || this.parentLabelAlign();
13002 cls : 'form-group roo-combobox-tickable' //input-group
13005 var btn_text_select = '';
13006 var btn_text_done = '';
13007 var btn_text_cancel = '';
13009 if (this.btn_text_show) {
13010 btn_text_select = 'Select';
13011 btn_text_done = 'Done';
13012 btn_text_cancel = 'Cancel';
13017 cls : 'tickable-buttons',
13022 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13023 //html : this.triggerText
13024 html: btn_text_select
13030 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13032 html: btn_text_done
13038 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13040 html: btn_text_cancel
13046 buttons.cn.unshift({
13048 cls: 'roo-select2-search-field-input'
13054 Roo.each(buttons.cn, function(c){
13056 c.cls += ' btn-' + _this.size;
13059 if (_this.disabled) {
13070 cls: 'form-hidden-field'
13074 cls: 'roo-select2-choices',
13078 cls: 'roo-select2-search-field',
13089 cls: 'roo-select2-container input-group roo-select2-container-multi',
13094 // cls: 'typeahead typeahead-long dropdown-menu',
13095 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13100 if(this.hasFeedback && !this.allowBlank){
13104 cls: 'glyphicon form-control-feedback'
13107 combobox.cn.push(feedback);
13111 if (align ==='left' && this.fieldLabel.length) {
13113 cfg.cls += ' roo-form-group-label-left';
13118 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13119 tooltip : 'This field is required'
13124 cls : 'control-label',
13125 html : this.fieldLabel
13137 var labelCfg = cfg.cn[1];
13138 var contentCfg = cfg.cn[2];
13141 if(this.indicatorpos == 'right'){
13147 cls : 'control-label',
13151 html : this.fieldLabel
13155 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13156 tooltip : 'This field is required'
13171 labelCfg = cfg.cn[0];
13172 contentCfg = cfg.cn[1];
13176 if(this.labelWidth > 12){
13177 labelCfg.style = "width: " + this.labelWidth + 'px';
13180 if(this.labelWidth < 13 && this.labelmd == 0){
13181 this.labelmd = this.labelWidth;
13184 if(this.labellg > 0){
13185 labelCfg.cls += ' col-lg-' + this.labellg;
13186 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13189 if(this.labelmd > 0){
13190 labelCfg.cls += ' col-md-' + this.labelmd;
13191 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13194 if(this.labelsm > 0){
13195 labelCfg.cls += ' col-sm-' + this.labelsm;
13196 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13199 if(this.labelxs > 0){
13200 labelCfg.cls += ' col-xs-' + this.labelxs;
13201 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13205 } else if ( this.fieldLabel.length) {
13206 // Roo.log(" label");
13210 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13211 tooltip : 'This field is required'
13215 //cls : 'input-group-addon',
13216 html : this.fieldLabel
13221 if(this.indicatorpos == 'right'){
13225 //cls : 'input-group-addon',
13226 html : this.fieldLabel
13230 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13231 tooltip : 'This field is required'
13240 // Roo.log(" no label && no align");
13247 ['xs','sm','md','lg'].map(function(size){
13248 if (settings[size]) {
13249 cfg.cls += ' col-' + size + '-' + settings[size];
13257 _initEventsCalled : false,
13260 initEvents: function()
13262 if (this._initEventsCalled) { // as we call render... prevent looping...
13265 this._initEventsCalled = true;
13268 throw "can not find store for combo";
13271 this.indicator = this.indicatorEl();
13273 this.store = Roo.factory(this.store, Roo.data);
13274 this.store.parent = this;
13276 // if we are building from html. then this element is so complex, that we can not really
13277 // use the rendered HTML.
13278 // so we have to trash and replace the previous code.
13279 if (Roo.XComponent.build_from_html) {
13280 // remove this element....
13281 var e = this.el.dom, k=0;
13282 while (e ) { e = e.previousSibling; ++k;}
13287 this.rendered = false;
13289 this.render(this.parent().getChildContainer(true), k);
13292 if(Roo.isIOS && this.useNativeIOS){
13293 this.initIOSView();
13301 if(Roo.isTouch && this.mobileTouchView){
13302 this.initTouchView();
13307 this.initTickableEvents();
13311 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13313 if(this.hiddenName){
13315 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13317 this.hiddenField.dom.value =
13318 this.hiddenValue !== undefined ? this.hiddenValue :
13319 this.value !== undefined ? this.value : '';
13321 // prevent input submission
13322 this.el.dom.removeAttribute('name');
13323 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13328 // this.el.dom.setAttribute('autocomplete', 'off');
13331 var cls = 'x-combo-list';
13333 //this.list = new Roo.Layer({
13334 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13340 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13341 _this.list.setWidth(lw);
13344 this.list.on('mouseover', this.onViewOver, this);
13345 this.list.on('mousemove', this.onViewMove, this);
13346 this.list.on('scroll', this.onViewScroll, this);
13349 this.list.swallowEvent('mousewheel');
13350 this.assetHeight = 0;
13353 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13354 this.assetHeight += this.header.getHeight();
13357 this.innerList = this.list.createChild({cls:cls+'-inner'});
13358 this.innerList.on('mouseover', this.onViewOver, this);
13359 this.innerList.on('mousemove', this.onViewMove, this);
13360 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13362 if(this.allowBlank && !this.pageSize && !this.disableClear){
13363 this.footer = this.list.createChild({cls:cls+'-ft'});
13364 this.pageTb = new Roo.Toolbar(this.footer);
13368 this.footer = this.list.createChild({cls:cls+'-ft'});
13369 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13370 {pageSize: this.pageSize});
13374 if (this.pageTb && this.allowBlank && !this.disableClear) {
13376 this.pageTb.add(new Roo.Toolbar.Fill(), {
13377 cls: 'x-btn-icon x-btn-clear',
13379 handler: function()
13382 _this.clearValue();
13383 _this.onSelect(false, -1);
13388 this.assetHeight += this.footer.getHeight();
13393 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13396 this.view = new Roo.View(this.list, this.tpl, {
13397 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13399 //this.view.wrapEl.setDisplayed(false);
13400 this.view.on('click', this.onViewClick, this);
13403 this.store.on('beforeload', this.onBeforeLoad, this);
13404 this.store.on('load', this.onLoad, this);
13405 this.store.on('loadexception', this.onLoadException, this);
13407 if(this.resizable){
13408 this.resizer = new Roo.Resizable(this.list, {
13409 pinned:true, handles:'se'
13411 this.resizer.on('resize', function(r, w, h){
13412 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13413 this.listWidth = w;
13414 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13415 this.restrictHeight();
13417 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13420 if(!this.editable){
13421 this.editable = true;
13422 this.setEditable(false);
13427 if (typeof(this.events.add.listeners) != 'undefined') {
13429 this.addicon = this.wrap.createChild(
13430 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13432 this.addicon.on('click', function(e) {
13433 this.fireEvent('add', this);
13436 if (typeof(this.events.edit.listeners) != 'undefined') {
13438 this.editicon = this.wrap.createChild(
13439 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13440 if (this.addicon) {
13441 this.editicon.setStyle('margin-left', '40px');
13443 this.editicon.on('click', function(e) {
13445 // we fire even if inothing is selected..
13446 this.fireEvent('edit', this, this.lastData );
13452 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13453 "up" : function(e){
13454 this.inKeyMode = true;
13458 "down" : function(e){
13459 if(!this.isExpanded()){
13460 this.onTriggerClick();
13462 this.inKeyMode = true;
13467 "enter" : function(e){
13468 // this.onViewClick();
13472 if(this.fireEvent("specialkey", this, e)){
13473 this.onViewClick(false);
13479 "esc" : function(e){
13483 "tab" : function(e){
13486 if(this.fireEvent("specialkey", this, e)){
13487 this.onViewClick(false);
13495 doRelay : function(foo, bar, hname){
13496 if(hname == 'down' || this.scope.isExpanded()){
13497 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13506 this.queryDelay = Math.max(this.queryDelay || 10,
13507 this.mode == 'local' ? 10 : 250);
13510 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13512 if(this.typeAhead){
13513 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13515 if(this.editable !== false){
13516 this.inputEl().on("keyup", this.onKeyUp, this);
13518 if(this.forceSelection){
13519 this.inputEl().on('blur', this.doForce, this);
13523 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13524 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13528 initTickableEvents: function()
13532 if(this.hiddenName){
13534 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13536 this.hiddenField.dom.value =
13537 this.hiddenValue !== undefined ? this.hiddenValue :
13538 this.value !== undefined ? this.value : '';
13540 // prevent input submission
13541 this.el.dom.removeAttribute('name');
13542 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13547 // this.list = this.el.select('ul.dropdown-menu',true).first();
13549 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13550 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13551 if(this.triggerList){
13552 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13555 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13556 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13558 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13559 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13561 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13562 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13564 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13565 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13566 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13569 this.cancelBtn.hide();
13574 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13575 _this.list.setWidth(lw);
13578 this.list.on('mouseover', this.onViewOver, this);
13579 this.list.on('mousemove', this.onViewMove, this);
13581 this.list.on('scroll', this.onViewScroll, this);
13584 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>';
13587 this.view = new Roo.View(this.list, this.tpl, {
13588 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13591 //this.view.wrapEl.setDisplayed(false);
13592 this.view.on('click', this.onViewClick, this);
13596 this.store.on('beforeload', this.onBeforeLoad, this);
13597 this.store.on('load', this.onLoad, this);
13598 this.store.on('loadexception', this.onLoadException, this);
13601 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13602 "up" : function(e){
13603 this.inKeyMode = true;
13607 "down" : function(e){
13608 this.inKeyMode = true;
13612 "enter" : function(e){
13613 if(this.fireEvent("specialkey", this, e)){
13614 this.onViewClick(false);
13620 "esc" : function(e){
13621 this.onTickableFooterButtonClick(e, false, false);
13624 "tab" : function(e){
13625 this.fireEvent("specialkey", this, e);
13627 this.onTickableFooterButtonClick(e, false, false);
13634 doRelay : function(e, fn, key){
13635 if(this.scope.isExpanded()){
13636 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13645 this.queryDelay = Math.max(this.queryDelay || 10,
13646 this.mode == 'local' ? 10 : 250);
13649 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13651 if(this.typeAhead){
13652 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13655 if(this.editable !== false){
13656 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13659 this.indicator = this.indicatorEl();
13661 if(this.indicator){
13662 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13663 this.indicator.hide();
13668 onDestroy : function(){
13670 this.view.setStore(null);
13671 this.view.el.removeAllListeners();
13672 this.view.el.remove();
13673 this.view.purgeListeners();
13676 this.list.dom.innerHTML = '';
13680 this.store.un('beforeload', this.onBeforeLoad, this);
13681 this.store.un('load', this.onLoad, this);
13682 this.store.un('loadexception', this.onLoadException, this);
13684 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13688 fireKey : function(e){
13689 if(e.isNavKeyPress() && !this.list.isVisible()){
13690 this.fireEvent("specialkey", this, e);
13695 onResize: function(w, h){
13696 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13698 // if(typeof w != 'number'){
13699 // // we do not handle it!?!?
13702 // var tw = this.trigger.getWidth();
13703 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13704 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13706 // this.inputEl().setWidth( this.adjustWidth('input', x));
13708 // //this.trigger.setStyle('left', x+'px');
13710 // if(this.list && this.listWidth === undefined){
13711 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13712 // this.list.setWidth(lw);
13713 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13721 * Allow or prevent the user from directly editing the field text. If false is passed,
13722 * the user will only be able to select from the items defined in the dropdown list. This method
13723 * is the runtime equivalent of setting the 'editable' config option at config time.
13724 * @param {Boolean} value True to allow the user to directly edit the field text
13726 setEditable : function(value){
13727 if(value == this.editable){
13730 this.editable = value;
13732 this.inputEl().dom.setAttribute('readOnly', true);
13733 this.inputEl().on('mousedown', this.onTriggerClick, this);
13734 this.inputEl().addClass('x-combo-noedit');
13736 this.inputEl().dom.setAttribute('readOnly', false);
13737 this.inputEl().un('mousedown', this.onTriggerClick, this);
13738 this.inputEl().removeClass('x-combo-noedit');
13744 onBeforeLoad : function(combo,opts){
13745 if(!this.hasFocus){
13749 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13751 this.restrictHeight();
13752 this.selectedIndex = -1;
13756 onLoad : function(){
13758 this.hasQuery = false;
13760 if(!this.hasFocus){
13764 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13765 this.loading.hide();
13768 if(this.store.getCount() > 0){
13771 this.restrictHeight();
13772 if(this.lastQuery == this.allQuery){
13773 if(this.editable && !this.tickable){
13774 this.inputEl().dom.select();
13778 !this.selectByValue(this.value, true) &&
13781 !this.store.lastOptions ||
13782 typeof(this.store.lastOptions.add) == 'undefined' ||
13783 this.store.lastOptions.add != true
13786 this.select(0, true);
13789 if(this.autoFocus){
13792 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13793 this.taTask.delay(this.typeAheadDelay);
13797 this.onEmptyResults();
13803 onLoadException : function()
13805 this.hasQuery = false;
13807 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13808 this.loading.hide();
13811 if(this.tickable && this.editable){
13816 // only causes errors at present
13817 //Roo.log(this.store.reader.jsonData);
13818 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13820 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13826 onTypeAhead : function(){
13827 if(this.store.getCount() > 0){
13828 var r = this.store.getAt(0);
13829 var newValue = r.data[this.displayField];
13830 var len = newValue.length;
13831 var selStart = this.getRawValue().length;
13833 if(selStart != len){
13834 this.setRawValue(newValue);
13835 this.selectText(selStart, newValue.length);
13841 onSelect : function(record, index){
13843 if(this.fireEvent('beforeselect', this, record, index) !== false){
13845 this.setFromData(index > -1 ? record.data : false);
13848 this.fireEvent('select', this, record, index);
13853 * Returns the currently selected field value or empty string if no value is set.
13854 * @return {String} value The selected value
13856 getValue : function()
13858 if(Roo.isIOS && this.useNativeIOS){
13859 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13863 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13866 if(this.valueField){
13867 return typeof this.value != 'undefined' ? this.value : '';
13869 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13873 getRawValue : function()
13875 if(Roo.isIOS && this.useNativeIOS){
13876 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13879 var v = this.inputEl().getValue();
13885 * Clears any text/value currently set in the field
13887 clearValue : function(){
13889 if(this.hiddenField){
13890 this.hiddenField.dom.value = '';
13893 this.setRawValue('');
13894 this.lastSelectionText = '';
13895 this.lastData = false;
13897 var close = this.closeTriggerEl();
13908 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13909 * will be displayed in the field. If the value does not match the data value of an existing item,
13910 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13911 * Otherwise the field will be blank (although the value will still be set).
13912 * @param {String} value The value to match
13914 setValue : function(v)
13916 if(Roo.isIOS && this.useNativeIOS){
13917 this.setIOSValue(v);
13927 if(this.valueField){
13928 var r = this.findRecord(this.valueField, v);
13930 text = r.data[this.displayField];
13931 }else if(this.valueNotFoundText !== undefined){
13932 text = this.valueNotFoundText;
13935 this.lastSelectionText = text;
13936 if(this.hiddenField){
13937 this.hiddenField.dom.value = v;
13939 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13942 var close = this.closeTriggerEl();
13945 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13951 * @property {Object} the last set data for the element
13956 * Sets the value of the field based on a object which is related to the record format for the store.
13957 * @param {Object} value the value to set as. or false on reset?
13959 setFromData : function(o){
13966 var dv = ''; // display value
13967 var vv = ''; // value value..
13969 if (this.displayField) {
13970 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13972 // this is an error condition!!!
13973 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13976 if(this.valueField){
13977 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13980 var close = this.closeTriggerEl();
13983 if(dv.length || vv * 1 > 0){
13985 this.blockFocus=true;
13991 if(this.hiddenField){
13992 this.hiddenField.dom.value = vv;
13994 this.lastSelectionText = dv;
13995 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13999 // no hidden field.. - we store the value in 'value', but still display
14000 // display field!!!!
14001 this.lastSelectionText = dv;
14002 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14009 reset : function(){
14010 // overridden so that last data is reset..
14017 this.setValue(this.originalValue);
14018 //this.clearInvalid();
14019 this.lastData = false;
14021 this.view.clearSelections();
14027 findRecord : function(prop, value){
14029 if(this.store.getCount() > 0){
14030 this.store.each(function(r){
14031 if(r.data[prop] == value){
14041 getName: function()
14043 // returns hidden if it's set..
14044 if (!this.rendered) {return ''};
14045 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14049 onViewMove : function(e, t){
14050 this.inKeyMode = false;
14054 onViewOver : function(e, t){
14055 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14058 var item = this.view.findItemFromChild(t);
14061 var index = this.view.indexOf(item);
14062 this.select(index, false);
14067 onViewClick : function(view, doFocus, el, e)
14069 var index = this.view.getSelectedIndexes()[0];
14071 var r = this.store.getAt(index);
14075 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14082 Roo.each(this.tickItems, function(v,k){
14084 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14086 _this.tickItems.splice(k, 1);
14088 if(typeof(e) == 'undefined' && view == false){
14089 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14101 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14102 this.tickItems.push(r.data);
14105 if(typeof(e) == 'undefined' && view == false){
14106 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14113 this.onSelect(r, index);
14115 if(doFocus !== false && !this.blockFocus){
14116 this.inputEl().focus();
14121 restrictHeight : function(){
14122 //this.innerList.dom.style.height = '';
14123 //var inner = this.innerList.dom;
14124 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14125 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14126 //this.list.beginUpdate();
14127 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14128 this.list.alignTo(this.inputEl(), this.listAlign);
14129 this.list.alignTo(this.inputEl(), this.listAlign);
14130 //this.list.endUpdate();
14134 onEmptyResults : function(){
14136 if(this.tickable && this.editable){
14137 this.hasFocus = false;
14138 this.restrictHeight();
14146 * Returns true if the dropdown list is expanded, else false.
14148 isExpanded : function(){
14149 return this.list.isVisible();
14153 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14154 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14155 * @param {String} value The data value of the item to select
14156 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14157 * selected item if it is not currently in view (defaults to true)
14158 * @return {Boolean} True if the value matched an item in the list, else false
14160 selectByValue : function(v, scrollIntoView){
14161 if(v !== undefined && v !== null){
14162 var r = this.findRecord(this.valueField || this.displayField, v);
14164 this.select(this.store.indexOf(r), scrollIntoView);
14172 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14173 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14174 * @param {Number} index The zero-based index of the list item to select
14175 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14176 * selected item if it is not currently in view (defaults to true)
14178 select : function(index, scrollIntoView){
14179 this.selectedIndex = index;
14180 this.view.select(index);
14181 if(scrollIntoView !== false){
14182 var el = this.view.getNode(index);
14184 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14187 this.list.scrollChildIntoView(el, false);
14193 selectNext : function(){
14194 var ct = this.store.getCount();
14196 if(this.selectedIndex == -1){
14198 }else if(this.selectedIndex < ct-1){
14199 this.select(this.selectedIndex+1);
14205 selectPrev : function(){
14206 var ct = this.store.getCount();
14208 if(this.selectedIndex == -1){
14210 }else if(this.selectedIndex != 0){
14211 this.select(this.selectedIndex-1);
14217 onKeyUp : function(e){
14218 if(this.editable !== false && !e.isSpecialKey()){
14219 this.lastKey = e.getKey();
14220 this.dqTask.delay(this.queryDelay);
14225 validateBlur : function(){
14226 return !this.list || !this.list.isVisible();
14230 initQuery : function(){
14232 var v = this.getRawValue();
14234 if(this.tickable && this.editable){
14235 v = this.tickableInputEl().getValue();
14242 doForce : function(){
14243 if(this.inputEl().dom.value.length > 0){
14244 this.inputEl().dom.value =
14245 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14251 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14252 * query allowing the query action to be canceled if needed.
14253 * @param {String} query The SQL query to execute
14254 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14255 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14256 * saved in the current store (defaults to false)
14258 doQuery : function(q, forceAll){
14260 if(q === undefined || q === null){
14265 forceAll: forceAll,
14269 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14274 forceAll = qe.forceAll;
14275 if(forceAll === true || (q.length >= this.minChars)){
14277 this.hasQuery = true;
14279 if(this.lastQuery != q || this.alwaysQuery){
14280 this.lastQuery = q;
14281 if(this.mode == 'local'){
14282 this.selectedIndex = -1;
14284 this.store.clearFilter();
14287 if(this.specialFilter){
14288 this.fireEvent('specialfilter', this);
14293 this.store.filter(this.displayField, q);
14296 this.store.fireEvent("datachanged", this.store);
14303 this.store.baseParams[this.queryParam] = q;
14305 var options = {params : this.getParams(q)};
14308 options.add = true;
14309 options.params.start = this.page * this.pageSize;
14312 this.store.load(options);
14315 * this code will make the page width larger, at the beginning, the list not align correctly,
14316 * we should expand the list on onLoad
14317 * so command out it
14322 this.selectedIndex = -1;
14327 this.loadNext = false;
14331 getParams : function(q){
14333 //p[this.queryParam] = q;
14337 p.limit = this.pageSize;
14343 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14345 collapse : function(){
14346 if(!this.isExpanded()){
14352 this.hasFocus = false;
14356 this.cancelBtn.hide();
14357 this.trigger.show();
14360 this.tickableInputEl().dom.value = '';
14361 this.tickableInputEl().blur();
14366 Roo.get(document).un('mousedown', this.collapseIf, this);
14367 Roo.get(document).un('mousewheel', this.collapseIf, this);
14368 if (!this.editable) {
14369 Roo.get(document).un('keydown', this.listKeyPress, this);
14371 this.fireEvent('collapse', this);
14377 collapseIf : function(e){
14378 var in_combo = e.within(this.el);
14379 var in_list = e.within(this.list);
14380 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14382 if (in_combo || in_list || is_list) {
14383 //e.stopPropagation();
14388 this.onTickableFooterButtonClick(e, false, false);
14396 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14398 expand : function(){
14400 if(this.isExpanded() || !this.hasFocus){
14404 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14405 this.list.setWidth(lw);
14411 this.restrictHeight();
14415 this.tickItems = Roo.apply([], this.item);
14418 this.cancelBtn.show();
14419 this.trigger.hide();
14422 this.tickableInputEl().focus();
14427 Roo.get(document).on('mousedown', this.collapseIf, this);
14428 Roo.get(document).on('mousewheel', this.collapseIf, this);
14429 if (!this.editable) {
14430 Roo.get(document).on('keydown', this.listKeyPress, this);
14433 this.fireEvent('expand', this);
14437 // Implements the default empty TriggerField.onTriggerClick function
14438 onTriggerClick : function(e)
14440 Roo.log('trigger click');
14442 if(this.disabled || !this.triggerList){
14447 this.loadNext = false;
14449 if(this.isExpanded()){
14451 if (!this.blockFocus) {
14452 this.inputEl().focus();
14456 this.hasFocus = true;
14457 if(this.triggerAction == 'all') {
14458 this.doQuery(this.allQuery, true);
14460 this.doQuery(this.getRawValue());
14462 if (!this.blockFocus) {
14463 this.inputEl().focus();
14468 onTickableTriggerClick : function(e)
14475 this.loadNext = false;
14476 this.hasFocus = true;
14478 if(this.triggerAction == 'all') {
14479 this.doQuery(this.allQuery, true);
14481 this.doQuery(this.getRawValue());
14485 onSearchFieldClick : function(e)
14487 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14488 this.onTickableFooterButtonClick(e, false, false);
14492 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14497 this.loadNext = false;
14498 this.hasFocus = true;
14500 if(this.triggerAction == 'all') {
14501 this.doQuery(this.allQuery, true);
14503 this.doQuery(this.getRawValue());
14507 listKeyPress : function(e)
14509 //Roo.log('listkeypress');
14510 // scroll to first matching element based on key pres..
14511 if (e.isSpecialKey()) {
14514 var k = String.fromCharCode(e.getKey()).toUpperCase();
14517 var csel = this.view.getSelectedNodes();
14518 var cselitem = false;
14520 var ix = this.view.indexOf(csel[0]);
14521 cselitem = this.store.getAt(ix);
14522 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14528 this.store.each(function(v) {
14530 // start at existing selection.
14531 if (cselitem.id == v.id) {
14537 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14538 match = this.store.indexOf(v);
14544 if (match === false) {
14545 return true; // no more action?
14548 this.view.select(match);
14549 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14550 sn.scrollIntoView(sn.dom.parentNode, false);
14553 onViewScroll : function(e, t){
14555 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){
14559 this.hasQuery = true;
14561 this.loading = this.list.select('.loading', true).first();
14563 if(this.loading === null){
14564 this.list.createChild({
14566 cls: 'loading roo-select2-more-results roo-select2-active',
14567 html: 'Loading more results...'
14570 this.loading = this.list.select('.loading', true).first();
14572 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14574 this.loading.hide();
14577 this.loading.show();
14582 this.loadNext = true;
14584 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14589 addItem : function(o)
14591 var dv = ''; // display value
14593 if (this.displayField) {
14594 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14596 // this is an error condition!!!
14597 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14604 var choice = this.choices.createChild({
14606 cls: 'roo-select2-search-choice',
14615 cls: 'roo-select2-search-choice-close fa fa-times',
14620 }, this.searchField);
14622 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14624 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14632 this.inputEl().dom.value = '';
14637 onRemoveItem : function(e, _self, o)
14639 e.preventDefault();
14641 this.lastItem = Roo.apply([], this.item);
14643 var index = this.item.indexOf(o.data) * 1;
14646 Roo.log('not this item?!');
14650 this.item.splice(index, 1);
14655 this.fireEvent('remove', this, e);
14661 syncValue : function()
14663 if(!this.item.length){
14670 Roo.each(this.item, function(i){
14671 if(_this.valueField){
14672 value.push(i[_this.valueField]);
14679 this.value = value.join(',');
14681 if(this.hiddenField){
14682 this.hiddenField.dom.value = this.value;
14685 this.store.fireEvent("datachanged", this.store);
14690 clearItem : function()
14692 if(!this.multiple){
14698 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14706 if(this.tickable && !Roo.isTouch){
14707 this.view.refresh();
14711 inputEl: function ()
14713 if(Roo.isIOS && this.useNativeIOS){
14714 return this.el.select('select.roo-ios-select', true).first();
14717 if(Roo.isTouch && this.mobileTouchView){
14718 return this.el.select('input.form-control',true).first();
14722 return this.searchField;
14725 return this.el.select('input.form-control',true).first();
14728 onTickableFooterButtonClick : function(e, btn, el)
14730 e.preventDefault();
14732 this.lastItem = Roo.apply([], this.item);
14734 if(btn && btn.name == 'cancel'){
14735 this.tickItems = Roo.apply([], this.item);
14744 Roo.each(this.tickItems, function(o){
14752 validate : function()
14754 if(this.getVisibilityEl().hasClass('hidden')){
14758 var v = this.getRawValue();
14761 v = this.getValue();
14764 if(this.disabled || this.allowBlank || v.length){
14769 this.markInvalid();
14773 tickableInputEl : function()
14775 if(!this.tickable || !this.editable){
14776 return this.inputEl();
14779 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14783 getAutoCreateTouchView : function()
14788 cls: 'form-group' //input-group
14794 type : this.inputType,
14795 cls : 'form-control x-combo-noedit',
14796 autocomplete: 'new-password',
14797 placeholder : this.placeholder || '',
14802 input.name = this.name;
14806 input.cls += ' input-' + this.size;
14809 if (this.disabled) {
14810 input.disabled = true;
14821 inputblock.cls += ' input-group';
14823 inputblock.cn.unshift({
14825 cls : 'input-group-addon',
14830 if(this.removable && !this.multiple){
14831 inputblock.cls += ' roo-removable';
14833 inputblock.cn.push({
14836 cls : 'roo-combo-removable-btn close'
14840 if(this.hasFeedback && !this.allowBlank){
14842 inputblock.cls += ' has-feedback';
14844 inputblock.cn.push({
14846 cls: 'glyphicon form-control-feedback'
14853 inputblock.cls += (this.before) ? '' : ' input-group';
14855 inputblock.cn.push({
14857 cls : 'input-group-addon',
14868 cls: 'form-hidden-field'
14882 cls: 'form-hidden-field'
14886 cls: 'roo-select2-choices',
14890 cls: 'roo-select2-search-field',
14903 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14909 if(!this.multiple && this.showToggleBtn){
14916 if (this.caret != false) {
14919 cls: 'fa fa-' + this.caret
14926 cls : 'input-group-addon btn dropdown-toggle',
14931 cls: 'combobox-clear',
14945 combobox.cls += ' roo-select2-container-multi';
14948 var align = this.labelAlign || this.parentLabelAlign();
14950 if (align ==='left' && this.fieldLabel.length) {
14955 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14956 tooltip : 'This field is required'
14960 cls : 'control-label',
14961 html : this.fieldLabel
14972 var labelCfg = cfg.cn[1];
14973 var contentCfg = cfg.cn[2];
14976 if(this.indicatorpos == 'right'){
14981 cls : 'control-label',
14985 html : this.fieldLabel
14989 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14990 tooltip : 'This field is required'
15003 labelCfg = cfg.cn[0];
15004 contentCfg = cfg.cn[1];
15009 if(this.labelWidth > 12){
15010 labelCfg.style = "width: " + this.labelWidth + 'px';
15013 if(this.labelWidth < 13 && this.labelmd == 0){
15014 this.labelmd = this.labelWidth;
15017 if(this.labellg > 0){
15018 labelCfg.cls += ' col-lg-' + this.labellg;
15019 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15022 if(this.labelmd > 0){
15023 labelCfg.cls += ' col-md-' + this.labelmd;
15024 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15027 if(this.labelsm > 0){
15028 labelCfg.cls += ' col-sm-' + this.labelsm;
15029 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15032 if(this.labelxs > 0){
15033 labelCfg.cls += ' col-xs-' + this.labelxs;
15034 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15038 } else if ( this.fieldLabel.length) {
15042 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15043 tooltip : 'This field is required'
15047 cls : 'control-label',
15048 html : this.fieldLabel
15059 if(this.indicatorpos == 'right'){
15063 cls : 'control-label',
15064 html : this.fieldLabel,
15068 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15069 tooltip : 'This field is required'
15086 var settings = this;
15088 ['xs','sm','md','lg'].map(function(size){
15089 if (settings[size]) {
15090 cfg.cls += ' col-' + size + '-' + settings[size];
15097 initTouchView : function()
15099 this.renderTouchView();
15101 this.touchViewEl.on('scroll', function(){
15102 this.el.dom.scrollTop = 0;
15105 this.originalValue = this.getValue();
15107 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15109 this.inputEl().on("click", this.showTouchView, this);
15110 if (this.triggerEl) {
15111 this.triggerEl.on("click", this.showTouchView, this);
15115 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15116 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15118 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15120 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15121 this.store.on('load', this.onTouchViewLoad, this);
15122 this.store.on('loadexception', this.onTouchViewLoadException, this);
15124 if(this.hiddenName){
15126 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15128 this.hiddenField.dom.value =
15129 this.hiddenValue !== undefined ? this.hiddenValue :
15130 this.value !== undefined ? this.value : '';
15132 this.el.dom.removeAttribute('name');
15133 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15137 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15138 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15141 if(this.removable && !this.multiple){
15142 var close = this.closeTriggerEl();
15144 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15145 close.on('click', this.removeBtnClick, this, close);
15149 * fix the bug in Safari iOS8
15151 this.inputEl().on("focus", function(e){
15152 document.activeElement.blur();
15160 renderTouchView : function()
15162 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15163 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15165 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15166 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15168 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15169 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15170 this.touchViewBodyEl.setStyle('overflow', 'auto');
15172 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15173 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15176 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15180 showTouchView : function()
15186 this.touchViewHeaderEl.hide();
15188 if(this.modalTitle.length){
15189 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15190 this.touchViewHeaderEl.show();
15193 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15194 this.touchViewEl.show();
15196 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15198 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15199 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15201 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15203 if(this.modalTitle.length){
15204 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15207 this.touchViewBodyEl.setHeight(bodyHeight);
15211 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15213 this.touchViewEl.addClass('in');
15216 this.doTouchViewQuery();
15220 hideTouchView : function()
15222 this.touchViewEl.removeClass('in');
15226 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15228 this.touchViewEl.setStyle('display', 'none');
15233 setTouchViewValue : function()
15240 Roo.each(this.tickItems, function(o){
15245 this.hideTouchView();
15248 doTouchViewQuery : function()
15257 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15261 if(!this.alwaysQuery || this.mode == 'local'){
15262 this.onTouchViewLoad();
15269 onTouchViewBeforeLoad : function(combo,opts)
15275 onTouchViewLoad : function()
15277 if(this.store.getCount() < 1){
15278 this.onTouchViewEmptyResults();
15282 this.clearTouchView();
15284 var rawValue = this.getRawValue();
15286 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15288 this.tickItems = [];
15290 this.store.data.each(function(d, rowIndex){
15291 var row = this.touchViewListGroup.createChild(template);
15293 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15294 row.addClass(d.data.cls);
15297 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15300 html : d.data[this.displayField]
15303 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15304 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15307 row.removeClass('selected');
15308 if(!this.multiple && this.valueField &&
15309 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15312 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15313 row.addClass('selected');
15316 if(this.multiple && this.valueField &&
15317 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15321 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15322 this.tickItems.push(d.data);
15325 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15329 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15331 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15333 if(this.modalTitle.length){
15334 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15337 var listHeight = this.touchViewListGroup.getHeight();
15341 if(firstChecked && listHeight > bodyHeight){
15342 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15347 onTouchViewLoadException : function()
15349 this.hideTouchView();
15352 onTouchViewEmptyResults : function()
15354 this.clearTouchView();
15356 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15358 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15362 clearTouchView : function()
15364 this.touchViewListGroup.dom.innerHTML = '';
15367 onTouchViewClick : function(e, el, o)
15369 e.preventDefault();
15372 var rowIndex = o.rowIndex;
15374 var r = this.store.getAt(rowIndex);
15376 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15378 if(!this.multiple){
15379 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15380 c.dom.removeAttribute('checked');
15383 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15385 this.setFromData(r.data);
15387 var close = this.closeTriggerEl();
15393 this.hideTouchView();
15395 this.fireEvent('select', this, r, rowIndex);
15400 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15401 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15402 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15406 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15407 this.addItem(r.data);
15408 this.tickItems.push(r.data);
15412 getAutoCreateNativeIOS : function()
15415 cls: 'form-group' //input-group,
15420 cls : 'roo-ios-select'
15424 combobox.name = this.name;
15427 if (this.disabled) {
15428 combobox.disabled = true;
15431 var settings = this;
15433 ['xs','sm','md','lg'].map(function(size){
15434 if (settings[size]) {
15435 cfg.cls += ' col-' + size + '-' + settings[size];
15445 initIOSView : function()
15447 this.store.on('load', this.onIOSViewLoad, this);
15452 onIOSViewLoad : function()
15454 if(this.store.getCount() < 1){
15458 this.clearIOSView();
15460 if(this.allowBlank) {
15462 var default_text = '-- SELECT --';
15464 if(this.placeholder.length){
15465 default_text = this.placeholder;
15468 if(this.emptyTitle.length){
15469 default_text += ' - ' + this.emptyTitle + ' -';
15472 var opt = this.inputEl().createChild({
15475 html : default_text
15479 o[this.valueField] = 0;
15480 o[this.displayField] = default_text;
15482 this.ios_options.push({
15489 this.store.data.each(function(d, rowIndex){
15493 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15494 html = d.data[this.displayField];
15499 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15500 value = d.data[this.valueField];
15509 if(this.value == d.data[this.valueField]){
15510 option['selected'] = true;
15513 var opt = this.inputEl().createChild(option);
15515 this.ios_options.push({
15522 this.inputEl().on('change', function(){
15523 this.fireEvent('select', this);
15528 clearIOSView: function()
15530 this.inputEl().dom.innerHTML = '';
15532 this.ios_options = [];
15535 setIOSValue: function(v)
15539 if(!this.ios_options){
15543 Roo.each(this.ios_options, function(opts){
15545 opts.el.dom.removeAttribute('selected');
15547 if(opts.data[this.valueField] != v){
15551 opts.el.dom.setAttribute('selected', true);
15557 * @cfg {Boolean} grow
15561 * @cfg {Number} growMin
15565 * @cfg {Number} growMax
15574 Roo.apply(Roo.bootstrap.ComboBox, {
15578 cls: 'modal-header',
15600 cls: 'list-group-item',
15604 cls: 'roo-combobox-list-group-item-value'
15608 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15622 listItemCheckbox : {
15624 cls: 'list-group-item',
15628 cls: 'roo-combobox-list-group-item-value'
15632 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15648 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15653 cls: 'modal-footer',
15661 cls: 'col-xs-6 text-left',
15664 cls: 'btn btn-danger roo-touch-view-cancel',
15670 cls: 'col-xs-6 text-right',
15673 cls: 'btn btn-success roo-touch-view-ok',
15684 Roo.apply(Roo.bootstrap.ComboBox, {
15686 touchViewTemplate : {
15688 cls: 'modal fade roo-combobox-touch-view',
15692 cls: 'modal-dialog',
15693 style : 'position:fixed', // we have to fix position....
15697 cls: 'modal-content',
15699 Roo.bootstrap.ComboBox.header,
15700 Roo.bootstrap.ComboBox.body,
15701 Roo.bootstrap.ComboBox.footer
15710 * Ext JS Library 1.1.1
15711 * Copyright(c) 2006-2007, Ext JS, LLC.
15713 * Originally Released Under LGPL - original licence link has changed is not relivant.
15716 * <script type="text/javascript">
15721 * @extends Roo.util.Observable
15722 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15723 * This class also supports single and multi selection modes. <br>
15724 * Create a data model bound view:
15726 var store = new Roo.data.Store(...);
15728 var view = new Roo.View({
15730 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15732 singleSelect: true,
15733 selectedClass: "ydataview-selected",
15737 // listen for node click?
15738 view.on("click", function(vw, index, node, e){
15739 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15743 dataModel.load("foobar.xml");
15745 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15747 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15748 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15750 * Note: old style constructor is still suported (container, template, config)
15753 * Create a new View
15754 * @param {Object} config The config object
15757 Roo.View = function(config, depreciated_tpl, depreciated_config){
15759 this.parent = false;
15761 if (typeof(depreciated_tpl) == 'undefined') {
15762 // new way.. - universal constructor.
15763 Roo.apply(this, config);
15764 this.el = Roo.get(this.el);
15767 this.el = Roo.get(config);
15768 this.tpl = depreciated_tpl;
15769 Roo.apply(this, depreciated_config);
15771 this.wrapEl = this.el.wrap().wrap();
15772 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15775 if(typeof(this.tpl) == "string"){
15776 this.tpl = new Roo.Template(this.tpl);
15778 // support xtype ctors..
15779 this.tpl = new Roo.factory(this.tpl, Roo);
15783 this.tpl.compile();
15788 * @event beforeclick
15789 * Fires before a click is processed. Returns false to cancel the default action.
15790 * @param {Roo.View} this
15791 * @param {Number} index The index of the target node
15792 * @param {HTMLElement} node The target node
15793 * @param {Roo.EventObject} e The raw event object
15795 "beforeclick" : true,
15798 * Fires when a template node is clicked.
15799 * @param {Roo.View} this
15800 * @param {Number} index The index of the target node
15801 * @param {HTMLElement} node The target node
15802 * @param {Roo.EventObject} e The raw event object
15807 * Fires when a template node is double clicked.
15808 * @param {Roo.View} this
15809 * @param {Number} index The index of the target node
15810 * @param {HTMLElement} node The target node
15811 * @param {Roo.EventObject} e The raw event object
15815 * @event contextmenu
15816 * Fires when a template node is right clicked.
15817 * @param {Roo.View} this
15818 * @param {Number} index The index of the target node
15819 * @param {HTMLElement} node The target node
15820 * @param {Roo.EventObject} e The raw event object
15822 "contextmenu" : true,
15824 * @event selectionchange
15825 * Fires when the selected nodes change.
15826 * @param {Roo.View} this
15827 * @param {Array} selections Array of the selected nodes
15829 "selectionchange" : true,
15832 * @event beforeselect
15833 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15834 * @param {Roo.View} this
15835 * @param {HTMLElement} node The node to be selected
15836 * @param {Array} selections Array of currently selected nodes
15838 "beforeselect" : true,
15840 * @event preparedata
15841 * Fires on every row to render, to allow you to change the data.
15842 * @param {Roo.View} this
15843 * @param {Object} data to be rendered (change this)
15845 "preparedata" : true
15853 "click": this.onClick,
15854 "dblclick": this.onDblClick,
15855 "contextmenu": this.onContextMenu,
15859 this.selections = [];
15861 this.cmp = new Roo.CompositeElementLite([]);
15863 this.store = Roo.factory(this.store, Roo.data);
15864 this.setStore(this.store, true);
15867 if ( this.footer && this.footer.xtype) {
15869 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15871 this.footer.dataSource = this.store;
15872 this.footer.container = fctr;
15873 this.footer = Roo.factory(this.footer, Roo);
15874 fctr.insertFirst(this.el);
15876 // this is a bit insane - as the paging toolbar seems to detach the el..
15877 // dom.parentNode.parentNode.parentNode
15878 // they get detached?
15882 Roo.View.superclass.constructor.call(this);
15887 Roo.extend(Roo.View, Roo.util.Observable, {
15890 * @cfg {Roo.data.Store} store Data store to load data from.
15895 * @cfg {String|Roo.Element} el The container element.
15900 * @cfg {String|Roo.Template} tpl The template used by this View
15904 * @cfg {String} dataName the named area of the template to use as the data area
15905 * Works with domtemplates roo-name="name"
15909 * @cfg {String} selectedClass The css class to add to selected nodes
15911 selectedClass : "x-view-selected",
15913 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15918 * @cfg {String} text to display on mask (default Loading)
15922 * @cfg {Boolean} multiSelect Allow multiple selection
15924 multiSelect : false,
15926 * @cfg {Boolean} singleSelect Allow single selection
15928 singleSelect: false,
15931 * @cfg {Boolean} toggleSelect - selecting
15933 toggleSelect : false,
15936 * @cfg {Boolean} tickable - selecting
15941 * Returns the element this view is bound to.
15942 * @return {Roo.Element}
15944 getEl : function(){
15945 return this.wrapEl;
15951 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15953 refresh : function(){
15954 //Roo.log('refresh');
15957 // if we are using something like 'domtemplate', then
15958 // the what gets used is:
15959 // t.applySubtemplate(NAME, data, wrapping data..)
15960 // the outer template then get' applied with
15961 // the store 'extra data'
15962 // and the body get's added to the
15963 // roo-name="data" node?
15964 // <span class='roo-tpl-{name}'></span> ?????
15968 this.clearSelections();
15969 this.el.update("");
15971 var records = this.store.getRange();
15972 if(records.length < 1) {
15974 // is this valid?? = should it render a template??
15976 this.el.update(this.emptyText);
15980 if (this.dataName) {
15981 this.el.update(t.apply(this.store.meta)); //????
15982 el = this.el.child('.roo-tpl-' + this.dataName);
15985 for(var i = 0, len = records.length; i < len; i++){
15986 var data = this.prepareData(records[i].data, i, records[i]);
15987 this.fireEvent("preparedata", this, data, i, records[i]);
15989 var d = Roo.apply({}, data);
15992 Roo.apply(d, {'roo-id' : Roo.id()});
15996 Roo.each(this.parent.item, function(item){
15997 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16000 Roo.apply(d, {'roo-data-checked' : 'checked'});
16004 html[html.length] = Roo.util.Format.trim(
16006 t.applySubtemplate(this.dataName, d, this.store.meta) :
16013 el.update(html.join(""));
16014 this.nodes = el.dom.childNodes;
16015 this.updateIndexes(0);
16020 * Function to override to reformat the data that is sent to
16021 * the template for each node.
16022 * DEPRICATED - use the preparedata event handler.
16023 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16024 * a JSON object for an UpdateManager bound view).
16026 prepareData : function(data, index, record)
16028 this.fireEvent("preparedata", this, data, index, record);
16032 onUpdate : function(ds, record){
16033 // Roo.log('on update');
16034 this.clearSelections();
16035 var index = this.store.indexOf(record);
16036 var n = this.nodes[index];
16037 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16038 n.parentNode.removeChild(n);
16039 this.updateIndexes(index, index);
16045 onAdd : function(ds, records, index)
16047 //Roo.log(['on Add', ds, records, index] );
16048 this.clearSelections();
16049 if(this.nodes.length == 0){
16053 var n = this.nodes[index];
16054 for(var i = 0, len = records.length; i < len; i++){
16055 var d = this.prepareData(records[i].data, i, records[i]);
16057 this.tpl.insertBefore(n, d);
16060 this.tpl.append(this.el, d);
16063 this.updateIndexes(index);
16066 onRemove : function(ds, record, index){
16067 // Roo.log('onRemove');
16068 this.clearSelections();
16069 var el = this.dataName ?
16070 this.el.child('.roo-tpl-' + this.dataName) :
16073 el.dom.removeChild(this.nodes[index]);
16074 this.updateIndexes(index);
16078 * Refresh an individual node.
16079 * @param {Number} index
16081 refreshNode : function(index){
16082 this.onUpdate(this.store, this.store.getAt(index));
16085 updateIndexes : function(startIndex, endIndex){
16086 var ns = this.nodes;
16087 startIndex = startIndex || 0;
16088 endIndex = endIndex || ns.length - 1;
16089 for(var i = startIndex; i <= endIndex; i++){
16090 ns[i].nodeIndex = i;
16095 * Changes the data store this view uses and refresh the view.
16096 * @param {Store} store
16098 setStore : function(store, initial){
16099 if(!initial && this.store){
16100 this.store.un("datachanged", this.refresh);
16101 this.store.un("add", this.onAdd);
16102 this.store.un("remove", this.onRemove);
16103 this.store.un("update", this.onUpdate);
16104 this.store.un("clear", this.refresh);
16105 this.store.un("beforeload", this.onBeforeLoad);
16106 this.store.un("load", this.onLoad);
16107 this.store.un("loadexception", this.onLoad);
16111 store.on("datachanged", this.refresh, this);
16112 store.on("add", this.onAdd, this);
16113 store.on("remove", this.onRemove, this);
16114 store.on("update", this.onUpdate, this);
16115 store.on("clear", this.refresh, this);
16116 store.on("beforeload", this.onBeforeLoad, this);
16117 store.on("load", this.onLoad, this);
16118 store.on("loadexception", this.onLoad, this);
16126 * onbeforeLoad - masks the loading area.
16129 onBeforeLoad : function(store,opts)
16131 //Roo.log('onBeforeLoad');
16133 this.el.update("");
16135 this.el.mask(this.mask ? this.mask : "Loading" );
16137 onLoad : function ()
16144 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16145 * @param {HTMLElement} node
16146 * @return {HTMLElement} The template node
16148 findItemFromChild : function(node){
16149 var el = this.dataName ?
16150 this.el.child('.roo-tpl-' + this.dataName,true) :
16153 if(!node || node.parentNode == el){
16156 var p = node.parentNode;
16157 while(p && p != el){
16158 if(p.parentNode == el){
16167 onClick : function(e){
16168 var item = this.findItemFromChild(e.getTarget());
16170 var index = this.indexOf(item);
16171 if(this.onItemClick(item, index, e) !== false){
16172 this.fireEvent("click", this, index, item, e);
16175 this.clearSelections();
16180 onContextMenu : function(e){
16181 var item = this.findItemFromChild(e.getTarget());
16183 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16188 onDblClick : function(e){
16189 var item = this.findItemFromChild(e.getTarget());
16191 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16195 onItemClick : function(item, index, e)
16197 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16200 if (this.toggleSelect) {
16201 var m = this.isSelected(item) ? 'unselect' : 'select';
16204 _t[m](item, true, false);
16207 if(this.multiSelect || this.singleSelect){
16208 if(this.multiSelect && e.shiftKey && this.lastSelection){
16209 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16211 this.select(item, this.multiSelect && e.ctrlKey);
16212 this.lastSelection = item;
16215 if(!this.tickable){
16216 e.preventDefault();
16224 * Get the number of selected nodes.
16227 getSelectionCount : function(){
16228 return this.selections.length;
16232 * Get the currently selected nodes.
16233 * @return {Array} An array of HTMLElements
16235 getSelectedNodes : function(){
16236 return this.selections;
16240 * Get the indexes of the selected nodes.
16243 getSelectedIndexes : function(){
16244 var indexes = [], s = this.selections;
16245 for(var i = 0, len = s.length; i < len; i++){
16246 indexes.push(s[i].nodeIndex);
16252 * Clear all selections
16253 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16255 clearSelections : function(suppressEvent){
16256 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16257 this.cmp.elements = this.selections;
16258 this.cmp.removeClass(this.selectedClass);
16259 this.selections = [];
16260 if(!suppressEvent){
16261 this.fireEvent("selectionchange", this, this.selections);
16267 * Returns true if the passed node is selected
16268 * @param {HTMLElement/Number} node The node or node index
16269 * @return {Boolean}
16271 isSelected : function(node){
16272 var s = this.selections;
16276 node = this.getNode(node);
16277 return s.indexOf(node) !== -1;
16282 * @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
16283 * @param {Boolean} keepExisting (optional) true to keep existing selections
16284 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16286 select : function(nodeInfo, keepExisting, suppressEvent){
16287 if(nodeInfo instanceof Array){
16289 this.clearSelections(true);
16291 for(var i = 0, len = nodeInfo.length; i < len; i++){
16292 this.select(nodeInfo[i], true, true);
16296 var node = this.getNode(nodeInfo);
16297 if(!node || this.isSelected(node)){
16298 return; // already selected.
16301 this.clearSelections(true);
16304 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16305 Roo.fly(node).addClass(this.selectedClass);
16306 this.selections.push(node);
16307 if(!suppressEvent){
16308 this.fireEvent("selectionchange", this, this.selections);
16316 * @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
16317 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16318 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16320 unselect : function(nodeInfo, keepExisting, suppressEvent)
16322 if(nodeInfo instanceof Array){
16323 Roo.each(this.selections, function(s) {
16324 this.unselect(s, nodeInfo);
16328 var node = this.getNode(nodeInfo);
16329 if(!node || !this.isSelected(node)){
16330 //Roo.log("not selected");
16331 return; // not selected.
16335 Roo.each(this.selections, function(s) {
16337 Roo.fly(node).removeClass(this.selectedClass);
16344 this.selections= ns;
16345 this.fireEvent("selectionchange", this, this.selections);
16349 * Gets a template node.
16350 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16351 * @return {HTMLElement} The node or null if it wasn't found
16353 getNode : function(nodeInfo){
16354 if(typeof nodeInfo == "string"){
16355 return document.getElementById(nodeInfo);
16356 }else if(typeof nodeInfo == "number"){
16357 return this.nodes[nodeInfo];
16363 * Gets a range template nodes.
16364 * @param {Number} startIndex
16365 * @param {Number} endIndex
16366 * @return {Array} An array of nodes
16368 getNodes : function(start, end){
16369 var ns = this.nodes;
16370 start = start || 0;
16371 end = typeof end == "undefined" ? ns.length - 1 : end;
16374 for(var i = start; i <= end; i++){
16378 for(var i = start; i >= end; i--){
16386 * Finds the index of the passed node
16387 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16388 * @return {Number} The index of the node or -1
16390 indexOf : function(node){
16391 node = this.getNode(node);
16392 if(typeof node.nodeIndex == "number"){
16393 return node.nodeIndex;
16395 var ns = this.nodes;
16396 for(var i = 0, len = ns.length; i < len; i++){
16407 * based on jquery fullcalendar
16411 Roo.bootstrap = Roo.bootstrap || {};
16413 * @class Roo.bootstrap.Calendar
16414 * @extends Roo.bootstrap.Component
16415 * Bootstrap Calendar class
16416 * @cfg {Boolean} loadMask (true|false) default false
16417 * @cfg {Object} header generate the user specific header of the calendar, default false
16420 * Create a new Container
16421 * @param {Object} config The config object
16426 Roo.bootstrap.Calendar = function(config){
16427 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16431 * Fires when a date is selected
16432 * @param {DatePicker} this
16433 * @param {Date} date The selected date
16437 * @event monthchange
16438 * Fires when the displayed month changes
16439 * @param {DatePicker} this
16440 * @param {Date} date The selected month
16442 'monthchange': true,
16444 * @event evententer
16445 * Fires when mouse over an event
16446 * @param {Calendar} this
16447 * @param {event} Event
16449 'evententer': true,
16451 * @event eventleave
16452 * Fires when the mouse leaves an
16453 * @param {Calendar} this
16456 'eventleave': true,
16458 * @event eventclick
16459 * Fires when the mouse click an
16460 * @param {Calendar} this
16469 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16472 * @cfg {Number} startDay
16473 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16481 getAutoCreate : function(){
16484 var fc_button = function(name, corner, style, content ) {
16485 return Roo.apply({},{
16487 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16489 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16492 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16503 style : 'width:100%',
16510 cls : 'fc-header-left',
16512 fc_button('prev', 'left', 'arrow', '‹' ),
16513 fc_button('next', 'right', 'arrow', '›' ),
16514 { tag: 'span', cls: 'fc-header-space' },
16515 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16523 cls : 'fc-header-center',
16527 cls: 'fc-header-title',
16530 html : 'month / year'
16538 cls : 'fc-header-right',
16540 /* fc_button('month', 'left', '', 'month' ),
16541 fc_button('week', '', '', 'week' ),
16542 fc_button('day', 'right', '', 'day' )
16554 header = this.header;
16557 var cal_heads = function() {
16559 // fixme - handle this.
16561 for (var i =0; i < Date.dayNames.length; i++) {
16562 var d = Date.dayNames[i];
16565 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16566 html : d.substring(0,3)
16570 ret[0].cls += ' fc-first';
16571 ret[6].cls += ' fc-last';
16574 var cal_cell = function(n) {
16577 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16582 cls: 'fc-day-number',
16586 cls: 'fc-day-content',
16590 style: 'position: relative;' // height: 17px;
16602 var cal_rows = function() {
16605 for (var r = 0; r < 6; r++) {
16612 for (var i =0; i < Date.dayNames.length; i++) {
16613 var d = Date.dayNames[i];
16614 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16617 row.cn[0].cls+=' fc-first';
16618 row.cn[0].cn[0].style = 'min-height:90px';
16619 row.cn[6].cls+=' fc-last';
16623 ret[0].cls += ' fc-first';
16624 ret[4].cls += ' fc-prev-last';
16625 ret[5].cls += ' fc-last';
16632 cls: 'fc-border-separate',
16633 style : 'width:100%',
16641 cls : 'fc-first fc-last',
16659 cls : 'fc-content',
16660 style : "position: relative;",
16663 cls : 'fc-view fc-view-month fc-grid',
16664 style : 'position: relative',
16665 unselectable : 'on',
16668 cls : 'fc-event-container',
16669 style : 'position:absolute;z-index:8;top:0;left:0;'
16687 initEvents : function()
16690 throw "can not find store for calendar";
16696 style: "text-align:center",
16700 style: "background-color:white;width:50%;margin:250 auto",
16704 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16715 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16717 var size = this.el.select('.fc-content', true).first().getSize();
16718 this.maskEl.setSize(size.width, size.height);
16719 this.maskEl.enableDisplayMode("block");
16720 if(!this.loadMask){
16721 this.maskEl.hide();
16724 this.store = Roo.factory(this.store, Roo.data);
16725 this.store.on('load', this.onLoad, this);
16726 this.store.on('beforeload', this.onBeforeLoad, this);
16730 this.cells = this.el.select('.fc-day',true);
16731 //Roo.log(this.cells);
16732 this.textNodes = this.el.query('.fc-day-number');
16733 this.cells.addClassOnOver('fc-state-hover');
16735 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16736 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16737 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16738 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16740 this.on('monthchange', this.onMonthChange, this);
16742 this.update(new Date().clearTime());
16745 resize : function() {
16746 var sz = this.el.getSize();
16748 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16749 this.el.select('.fc-day-content div',true).setHeight(34);
16754 showPrevMonth : function(e){
16755 this.update(this.activeDate.add("mo", -1));
16757 showToday : function(e){
16758 this.update(new Date().clearTime());
16761 showNextMonth : function(e){
16762 this.update(this.activeDate.add("mo", 1));
16766 showPrevYear : function(){
16767 this.update(this.activeDate.add("y", -1));
16771 showNextYear : function(){
16772 this.update(this.activeDate.add("y", 1));
16777 update : function(date)
16779 var vd = this.activeDate;
16780 this.activeDate = date;
16781 // if(vd && this.el){
16782 // var t = date.getTime();
16783 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16784 // Roo.log('using add remove');
16786 // this.fireEvent('monthchange', this, date);
16788 // this.cells.removeClass("fc-state-highlight");
16789 // this.cells.each(function(c){
16790 // if(c.dateValue == t){
16791 // c.addClass("fc-state-highlight");
16792 // setTimeout(function(){
16793 // try{c.dom.firstChild.focus();}catch(e){}
16803 var days = date.getDaysInMonth();
16805 var firstOfMonth = date.getFirstDateOfMonth();
16806 var startingPos = firstOfMonth.getDay()-this.startDay;
16808 if(startingPos < this.startDay){
16812 var pm = date.add(Date.MONTH, -1);
16813 var prevStart = pm.getDaysInMonth()-startingPos;
16815 this.cells = this.el.select('.fc-day',true);
16816 this.textNodes = this.el.query('.fc-day-number');
16817 this.cells.addClassOnOver('fc-state-hover');
16819 var cells = this.cells.elements;
16820 var textEls = this.textNodes;
16822 Roo.each(cells, function(cell){
16823 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16826 days += startingPos;
16828 // convert everything to numbers so it's fast
16829 var day = 86400000;
16830 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16833 //Roo.log(prevStart);
16835 var today = new Date().clearTime().getTime();
16836 var sel = date.clearTime().getTime();
16837 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16838 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16839 var ddMatch = this.disabledDatesRE;
16840 var ddText = this.disabledDatesText;
16841 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16842 var ddaysText = this.disabledDaysText;
16843 var format = this.format;
16845 var setCellClass = function(cal, cell){
16849 //Roo.log('set Cell Class');
16851 var t = d.getTime();
16855 cell.dateValue = t;
16857 cell.className += " fc-today";
16858 cell.className += " fc-state-highlight";
16859 cell.title = cal.todayText;
16862 // disable highlight in other month..
16863 //cell.className += " fc-state-highlight";
16868 cell.className = " fc-state-disabled";
16869 cell.title = cal.minText;
16873 cell.className = " fc-state-disabled";
16874 cell.title = cal.maxText;
16878 if(ddays.indexOf(d.getDay()) != -1){
16879 cell.title = ddaysText;
16880 cell.className = " fc-state-disabled";
16883 if(ddMatch && format){
16884 var fvalue = d.dateFormat(format);
16885 if(ddMatch.test(fvalue)){
16886 cell.title = ddText.replace("%0", fvalue);
16887 cell.className = " fc-state-disabled";
16891 if (!cell.initialClassName) {
16892 cell.initialClassName = cell.dom.className;
16895 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16900 for(; i < startingPos; i++) {
16901 textEls[i].innerHTML = (++prevStart);
16902 d.setDate(d.getDate()+1);
16904 cells[i].className = "fc-past fc-other-month";
16905 setCellClass(this, cells[i]);
16910 for(; i < days; i++){
16911 intDay = i - startingPos + 1;
16912 textEls[i].innerHTML = (intDay);
16913 d.setDate(d.getDate()+1);
16915 cells[i].className = ''; // "x-date-active";
16916 setCellClass(this, cells[i]);
16920 for(; i < 42; i++) {
16921 textEls[i].innerHTML = (++extraDays);
16922 d.setDate(d.getDate()+1);
16924 cells[i].className = "fc-future fc-other-month";
16925 setCellClass(this, cells[i]);
16928 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16930 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16932 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16933 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16935 if(totalRows != 6){
16936 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16937 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16940 this.fireEvent('monthchange', this, date);
16944 if(!this.internalRender){
16945 var main = this.el.dom.firstChild;
16946 var w = main.offsetWidth;
16947 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16948 Roo.fly(main).setWidth(w);
16949 this.internalRender = true;
16950 // opera does not respect the auto grow header center column
16951 // then, after it gets a width opera refuses to recalculate
16952 // without a second pass
16953 if(Roo.isOpera && !this.secondPass){
16954 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16955 this.secondPass = true;
16956 this.update.defer(10, this, [date]);
16963 findCell : function(dt) {
16964 dt = dt.clearTime().getTime();
16966 this.cells.each(function(c){
16967 //Roo.log("check " +c.dateValue + '?=' + dt);
16968 if(c.dateValue == dt){
16978 findCells : function(ev) {
16979 var s = ev.start.clone().clearTime().getTime();
16981 var e= ev.end.clone().clearTime().getTime();
16984 this.cells.each(function(c){
16985 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16987 if(c.dateValue > e){
16990 if(c.dateValue < s){
16999 // findBestRow: function(cells)
17003 // for (var i =0 ; i < cells.length;i++) {
17004 // ret = Math.max(cells[i].rows || 0,ret);
17011 addItem : function(ev)
17013 // look for vertical location slot in
17014 var cells = this.findCells(ev);
17016 // ev.row = this.findBestRow(cells);
17018 // work out the location.
17022 for(var i =0; i < cells.length; i++) {
17024 cells[i].row = cells[0].row;
17027 cells[i].row = cells[i].row + 1;
17037 if (crow.start.getY() == cells[i].getY()) {
17039 crow.end = cells[i];
17056 cells[0].events.push(ev);
17058 this.calevents.push(ev);
17061 clearEvents: function() {
17063 if(!this.calevents){
17067 Roo.each(this.cells.elements, function(c){
17073 Roo.each(this.calevents, function(e) {
17074 Roo.each(e.els, function(el) {
17075 el.un('mouseenter' ,this.onEventEnter, this);
17076 el.un('mouseleave' ,this.onEventLeave, this);
17081 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17087 renderEvents: function()
17091 this.cells.each(function(c) {
17100 if(c.row != c.events.length){
17101 r = 4 - (4 - (c.row - c.events.length));
17104 c.events = ev.slice(0, r);
17105 c.more = ev.slice(r);
17107 if(c.more.length && c.more.length == 1){
17108 c.events.push(c.more.pop());
17111 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17115 this.cells.each(function(c) {
17117 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17120 for (var e = 0; e < c.events.length; e++){
17121 var ev = c.events[e];
17122 var rows = ev.rows;
17124 for(var i = 0; i < rows.length; i++) {
17126 // how many rows should it span..
17129 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17130 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17132 unselectable : "on",
17135 cls: 'fc-event-inner',
17139 // cls: 'fc-event-time',
17140 // html : cells.length > 1 ? '' : ev.time
17144 cls: 'fc-event-title',
17145 html : String.format('{0}', ev.title)
17152 cls: 'ui-resizable-handle ui-resizable-e',
17153 html : '  '
17160 cfg.cls += ' fc-event-start';
17162 if ((i+1) == rows.length) {
17163 cfg.cls += ' fc-event-end';
17166 var ctr = _this.el.select('.fc-event-container',true).first();
17167 var cg = ctr.createChild(cfg);
17169 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17170 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17172 var r = (c.more.length) ? 1 : 0;
17173 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17174 cg.setWidth(ebox.right - sbox.x -2);
17176 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17177 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17178 cg.on('click', _this.onEventClick, _this, ev);
17189 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17190 style : 'position: absolute',
17191 unselectable : "on",
17194 cls: 'fc-event-inner',
17198 cls: 'fc-event-title',
17206 cls: 'ui-resizable-handle ui-resizable-e',
17207 html : '  '
17213 var ctr = _this.el.select('.fc-event-container',true).first();
17214 var cg = ctr.createChild(cfg);
17216 var sbox = c.select('.fc-day-content',true).first().getBox();
17217 var ebox = c.select('.fc-day-content',true).first().getBox();
17219 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17220 cg.setWidth(ebox.right - sbox.x -2);
17222 cg.on('click', _this.onMoreEventClick, _this, c.more);
17232 onEventEnter: function (e, el,event,d) {
17233 this.fireEvent('evententer', this, el, event);
17236 onEventLeave: function (e, el,event,d) {
17237 this.fireEvent('eventleave', this, el, event);
17240 onEventClick: function (e, el,event,d) {
17241 this.fireEvent('eventclick', this, el, event);
17244 onMonthChange: function () {
17248 onMoreEventClick: function(e, el, more)
17252 this.calpopover.placement = 'right';
17253 this.calpopover.setTitle('More');
17255 this.calpopover.setContent('');
17257 var ctr = this.calpopover.el.select('.popover-content', true).first();
17259 Roo.each(more, function(m){
17261 cls : 'fc-event-hori fc-event-draggable',
17264 var cg = ctr.createChild(cfg);
17266 cg.on('click', _this.onEventClick, _this, m);
17269 this.calpopover.show(el);
17274 onLoad: function ()
17276 this.calevents = [];
17279 if(this.store.getCount() > 0){
17280 this.store.data.each(function(d){
17283 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17284 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17285 time : d.data.start_time,
17286 title : d.data.title,
17287 description : d.data.description,
17288 venue : d.data.venue
17293 this.renderEvents();
17295 if(this.calevents.length && this.loadMask){
17296 this.maskEl.hide();
17300 onBeforeLoad: function()
17302 this.clearEvents();
17304 this.maskEl.show();
17318 * @class Roo.bootstrap.Popover
17319 * @extends Roo.bootstrap.Component
17320 * Bootstrap Popover class
17321 * @cfg {String} html contents of the popover (or false to use children..)
17322 * @cfg {String} title of popover (or false to hide)
17323 * @cfg {String} placement how it is placed
17324 * @cfg {String} trigger click || hover (or false to trigger manually)
17325 * @cfg {String} over what (parent or false to trigger manually.)
17326 * @cfg {Number} delay - delay before showing
17329 * Create a new Popover
17330 * @param {Object} config The config object
17333 Roo.bootstrap.Popover = function(config){
17334 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17340 * After the popover show
17342 * @param {Roo.bootstrap.Popover} this
17347 * After the popover hide
17349 * @param {Roo.bootstrap.Popover} this
17355 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17357 title: 'Fill in a title',
17360 placement : 'right',
17361 trigger : 'hover', // hover
17367 can_build_overlaid : false,
17369 getChildContainer : function()
17371 return this.el.select('.popover-content',true).first();
17374 getAutoCreate : function(){
17377 cls : 'popover roo-dynamic',
17378 style: 'display:block',
17384 cls : 'popover-inner',
17388 cls: 'popover-title',
17392 cls : 'popover-content',
17403 setTitle: function(str)
17406 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17408 setContent: function(str)
17411 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17413 // as it get's added to the bottom of the page.
17414 onRender : function(ct, position)
17416 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17418 var cfg = Roo.apply({}, this.getAutoCreate());
17422 cfg.cls += ' ' + this.cls;
17425 cfg.style = this.style;
17427 //Roo.log("adding to ");
17428 this.el = Roo.get(document.body).createChild(cfg, position);
17429 // Roo.log(this.el);
17434 initEvents : function()
17436 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17437 this.el.enableDisplayMode('block');
17439 if (this.over === false) {
17442 if (this.triggers === false) {
17445 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17446 var triggers = this.trigger ? this.trigger.split(' ') : [];
17447 Roo.each(triggers, function(trigger) {
17449 if (trigger == 'click') {
17450 on_el.on('click', this.toggle, this);
17451 } else if (trigger != 'manual') {
17452 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17453 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17455 on_el.on(eventIn ,this.enter, this);
17456 on_el.on(eventOut, this.leave, this);
17467 toggle : function () {
17468 this.hoverState == 'in' ? this.leave() : this.enter();
17471 enter : function () {
17473 clearTimeout(this.timeout);
17475 this.hoverState = 'in';
17477 if (!this.delay || !this.delay.show) {
17482 this.timeout = setTimeout(function () {
17483 if (_t.hoverState == 'in') {
17486 }, this.delay.show)
17489 leave : function() {
17490 clearTimeout(this.timeout);
17492 this.hoverState = 'out';
17494 if (!this.delay || !this.delay.hide) {
17499 this.timeout = setTimeout(function () {
17500 if (_t.hoverState == 'out') {
17503 }, this.delay.hide)
17506 show : function (on_el)
17509 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17513 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17514 if (this.html !== false) {
17515 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17517 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17518 if (!this.title.length) {
17519 this.el.select('.popover-title',true).hide();
17522 var placement = typeof this.placement == 'function' ?
17523 this.placement.call(this, this.el, on_el) :
17526 var autoToken = /\s?auto?\s?/i;
17527 var autoPlace = autoToken.test(placement);
17529 placement = placement.replace(autoToken, '') || 'top';
17533 //this.el.setXY([0,0]);
17535 this.el.dom.style.display='block';
17536 this.el.addClass(placement);
17538 //this.el.appendTo(on_el);
17540 var p = this.getPosition();
17541 var box = this.el.getBox();
17546 var align = Roo.bootstrap.Popover.alignment[placement];
17549 this.el.alignTo(on_el, align[0],align[1]);
17550 //var arrow = this.el.select('.arrow',true).first();
17551 //arrow.set(align[2],
17553 this.el.addClass('in');
17556 if (this.el.hasClass('fade')) {
17560 this.hoverState = 'in';
17562 this.fireEvent('show', this);
17567 this.el.setXY([0,0]);
17568 this.el.removeClass('in');
17570 this.hoverState = null;
17572 this.fireEvent('hide', this);
17577 Roo.bootstrap.Popover.alignment = {
17578 'left' : ['r-l', [-10,0], 'right'],
17579 'right' : ['l-r', [10,0], 'left'],
17580 'bottom' : ['t-b', [0,10], 'top'],
17581 'top' : [ 'b-t', [0,-10], 'bottom']
17592 * @class Roo.bootstrap.Progress
17593 * @extends Roo.bootstrap.Component
17594 * Bootstrap Progress class
17595 * @cfg {Boolean} striped striped of the progress bar
17596 * @cfg {Boolean} active animated of the progress bar
17600 * Create a new Progress
17601 * @param {Object} config The config object
17604 Roo.bootstrap.Progress = function(config){
17605 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17608 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17613 getAutoCreate : function(){
17621 cfg.cls += ' progress-striped';
17625 cfg.cls += ' active';
17644 * @class Roo.bootstrap.ProgressBar
17645 * @extends Roo.bootstrap.Component
17646 * Bootstrap ProgressBar class
17647 * @cfg {Number} aria_valuenow aria-value now
17648 * @cfg {Number} aria_valuemin aria-value min
17649 * @cfg {Number} aria_valuemax aria-value max
17650 * @cfg {String} label label for the progress bar
17651 * @cfg {String} panel (success | info | warning | danger )
17652 * @cfg {String} role role of the progress bar
17653 * @cfg {String} sr_only text
17657 * Create a new ProgressBar
17658 * @param {Object} config The config object
17661 Roo.bootstrap.ProgressBar = function(config){
17662 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17665 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17669 aria_valuemax : 100,
17675 getAutoCreate : function()
17680 cls: 'progress-bar',
17681 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17693 cfg.role = this.role;
17696 if(this.aria_valuenow){
17697 cfg['aria-valuenow'] = this.aria_valuenow;
17700 if(this.aria_valuemin){
17701 cfg['aria-valuemin'] = this.aria_valuemin;
17704 if(this.aria_valuemax){
17705 cfg['aria-valuemax'] = this.aria_valuemax;
17708 if(this.label && !this.sr_only){
17709 cfg.html = this.label;
17713 cfg.cls += ' progress-bar-' + this.panel;
17719 update : function(aria_valuenow)
17721 this.aria_valuenow = aria_valuenow;
17723 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17738 * @class Roo.bootstrap.TabGroup
17739 * @extends Roo.bootstrap.Column
17740 * Bootstrap Column class
17741 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17742 * @cfg {Boolean} carousel true to make the group behave like a carousel
17743 * @cfg {Boolean} bullets show bullets for the panels
17744 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17745 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17746 * @cfg {Boolean} showarrow (true|false) show arrow default true
17749 * Create a new TabGroup
17750 * @param {Object} config The config object
17753 Roo.bootstrap.TabGroup = function(config){
17754 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17756 this.navId = Roo.id();
17759 Roo.bootstrap.TabGroup.register(this);
17763 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17766 transition : false,
17771 slideOnTouch : false,
17774 getAutoCreate : function()
17776 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17778 cfg.cls += ' tab-content';
17780 if (this.carousel) {
17781 cfg.cls += ' carousel slide';
17784 cls : 'carousel-inner',
17788 if(this.bullets && !Roo.isTouch){
17791 cls : 'carousel-bullets',
17795 if(this.bullets_cls){
17796 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17803 cfg.cn[0].cn.push(bullets);
17806 if(this.showarrow){
17807 cfg.cn[0].cn.push({
17809 class : 'carousel-arrow',
17813 class : 'carousel-prev',
17817 class : 'fa fa-chevron-left'
17823 class : 'carousel-next',
17827 class : 'fa fa-chevron-right'
17840 initEvents: function()
17842 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17843 // this.el.on("touchstart", this.onTouchStart, this);
17846 if(this.autoslide){
17849 this.slideFn = window.setInterval(function() {
17850 _this.showPanelNext();
17854 if(this.showarrow){
17855 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17856 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17862 // onTouchStart : function(e, el, o)
17864 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17868 // this.showPanelNext();
17872 getChildContainer : function()
17874 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17878 * register a Navigation item
17879 * @param {Roo.bootstrap.NavItem} the navitem to add
17881 register : function(item)
17883 this.tabs.push( item);
17884 item.navId = this.navId; // not really needed..
17889 getActivePanel : function()
17892 Roo.each(this.tabs, function(t) {
17902 getPanelByName : function(n)
17905 Roo.each(this.tabs, function(t) {
17906 if (t.tabId == n) {
17914 indexOfPanel : function(p)
17917 Roo.each(this.tabs, function(t,i) {
17918 if (t.tabId == p.tabId) {
17927 * show a specific panel
17928 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17929 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17931 showPanel : function (pan)
17933 if(this.transition || typeof(pan) == 'undefined'){
17934 Roo.log("waiting for the transitionend");
17938 if (typeof(pan) == 'number') {
17939 pan = this.tabs[pan];
17942 if (typeof(pan) == 'string') {
17943 pan = this.getPanelByName(pan);
17946 var cur = this.getActivePanel();
17949 Roo.log('pan or acitve pan is undefined');
17953 if (pan.tabId == this.getActivePanel().tabId) {
17957 if (false === cur.fireEvent('beforedeactivate')) {
17961 if(this.bullets > 0 && !Roo.isTouch){
17962 this.setActiveBullet(this.indexOfPanel(pan));
17965 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17967 this.transition = true;
17968 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17969 var lr = dir == 'next' ? 'left' : 'right';
17970 pan.el.addClass(dir); // or prev
17971 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17972 cur.el.addClass(lr); // or right
17973 pan.el.addClass(lr);
17976 cur.el.on('transitionend', function() {
17977 Roo.log("trans end?");
17979 pan.el.removeClass([lr,dir]);
17980 pan.setActive(true);
17982 cur.el.removeClass([lr]);
17983 cur.setActive(false);
17985 _this.transition = false;
17987 }, this, { single: true } );
17992 cur.setActive(false);
17993 pan.setActive(true);
17998 showPanelNext : function()
18000 var i = this.indexOfPanel(this.getActivePanel());
18002 if (i >= this.tabs.length - 1 && !this.autoslide) {
18006 if (i >= this.tabs.length - 1 && this.autoslide) {
18010 this.showPanel(this.tabs[i+1]);
18013 showPanelPrev : function()
18015 var i = this.indexOfPanel(this.getActivePanel());
18017 if (i < 1 && !this.autoslide) {
18021 if (i < 1 && this.autoslide) {
18022 i = this.tabs.length;
18025 this.showPanel(this.tabs[i-1]);
18029 addBullet: function()
18031 if(!this.bullets || Roo.isTouch){
18034 var ctr = this.el.select('.carousel-bullets',true).first();
18035 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18036 var bullet = ctr.createChild({
18037 cls : 'bullet bullet-' + i
18038 },ctr.dom.lastChild);
18043 bullet.on('click', (function(e, el, o, ii, t){
18045 e.preventDefault();
18047 this.showPanel(ii);
18049 if(this.autoslide && this.slideFn){
18050 clearInterval(this.slideFn);
18051 this.slideFn = window.setInterval(function() {
18052 _this.showPanelNext();
18056 }).createDelegate(this, [i, bullet], true));
18061 setActiveBullet : function(i)
18067 Roo.each(this.el.select('.bullet', true).elements, function(el){
18068 el.removeClass('selected');
18071 var bullet = this.el.select('.bullet-' + i, true).first();
18077 bullet.addClass('selected');
18088 Roo.apply(Roo.bootstrap.TabGroup, {
18092 * register a Navigation Group
18093 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18095 register : function(navgrp)
18097 this.groups[navgrp.navId] = navgrp;
18101 * fetch a Navigation Group based on the navigation ID
18102 * if one does not exist , it will get created.
18103 * @param {string} the navgroup to add
18104 * @returns {Roo.bootstrap.NavGroup} the navgroup
18106 get: function(navId) {
18107 if (typeof(this.groups[navId]) == 'undefined') {
18108 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18110 return this.groups[navId] ;
18125 * @class Roo.bootstrap.TabPanel
18126 * @extends Roo.bootstrap.Component
18127 * Bootstrap TabPanel class
18128 * @cfg {Boolean} active panel active
18129 * @cfg {String} html panel content
18130 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18131 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18132 * @cfg {String} href click to link..
18136 * Create a new TabPanel
18137 * @param {Object} config The config object
18140 Roo.bootstrap.TabPanel = function(config){
18141 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18145 * Fires when the active status changes
18146 * @param {Roo.bootstrap.TabPanel} this
18147 * @param {Boolean} state the new state
18152 * @event beforedeactivate
18153 * Fires before a tab is de-activated - can be used to do validation on a form.
18154 * @param {Roo.bootstrap.TabPanel} this
18155 * @return {Boolean} false if there is an error
18158 'beforedeactivate': true
18161 this.tabId = this.tabId || Roo.id();
18165 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18173 getAutoCreate : function(){
18176 // item is needed for carousel - not sure if it has any effect otherwise
18177 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18178 html: this.html || ''
18182 cfg.cls += ' active';
18186 cfg.tabId = this.tabId;
18193 initEvents: function()
18195 var p = this.parent();
18197 this.navId = this.navId || p.navId;
18199 if (typeof(this.navId) != 'undefined') {
18200 // not really needed.. but just in case.. parent should be a NavGroup.
18201 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18205 var i = tg.tabs.length - 1;
18207 if(this.active && tg.bullets > 0 && i < tg.bullets){
18208 tg.setActiveBullet(i);
18212 this.el.on('click', this.onClick, this);
18215 this.el.on("touchstart", this.onTouchStart, this);
18216 this.el.on("touchmove", this.onTouchMove, this);
18217 this.el.on("touchend", this.onTouchEnd, this);
18222 onRender : function(ct, position)
18224 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18227 setActive : function(state)
18229 Roo.log("panel - set active " + this.tabId + "=" + state);
18231 this.active = state;
18233 this.el.removeClass('active');
18235 } else if (!this.el.hasClass('active')) {
18236 this.el.addClass('active');
18239 this.fireEvent('changed', this, state);
18242 onClick : function(e)
18244 e.preventDefault();
18246 if(!this.href.length){
18250 window.location.href = this.href;
18259 onTouchStart : function(e)
18261 this.swiping = false;
18263 this.startX = e.browserEvent.touches[0].clientX;
18264 this.startY = e.browserEvent.touches[0].clientY;
18267 onTouchMove : function(e)
18269 this.swiping = true;
18271 this.endX = e.browserEvent.touches[0].clientX;
18272 this.endY = e.browserEvent.touches[0].clientY;
18275 onTouchEnd : function(e)
18282 var tabGroup = this.parent();
18284 if(this.endX > this.startX){ // swiping right
18285 tabGroup.showPanelPrev();
18289 if(this.startX > this.endX){ // swiping left
18290 tabGroup.showPanelNext();
18309 * @class Roo.bootstrap.DateField
18310 * @extends Roo.bootstrap.Input
18311 * Bootstrap DateField class
18312 * @cfg {Number} weekStart default 0
18313 * @cfg {String} viewMode default empty, (months|years)
18314 * @cfg {String} minViewMode default empty, (months|years)
18315 * @cfg {Number} startDate default -Infinity
18316 * @cfg {Number} endDate default Infinity
18317 * @cfg {Boolean} todayHighlight default false
18318 * @cfg {Boolean} todayBtn default false
18319 * @cfg {Boolean} calendarWeeks default false
18320 * @cfg {Object} daysOfWeekDisabled default empty
18321 * @cfg {Boolean} singleMode default false (true | false)
18323 * @cfg {Boolean} keyboardNavigation default true
18324 * @cfg {String} language default en
18327 * Create a new DateField
18328 * @param {Object} config The config object
18331 Roo.bootstrap.DateField = function(config){
18332 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18336 * Fires when this field show.
18337 * @param {Roo.bootstrap.DateField} this
18338 * @param {Mixed} date The date value
18343 * Fires when this field hide.
18344 * @param {Roo.bootstrap.DateField} this
18345 * @param {Mixed} date The date value
18350 * Fires when select a date.
18351 * @param {Roo.bootstrap.DateField} this
18352 * @param {Mixed} date The date value
18356 * @event beforeselect
18357 * Fires when before select a date.
18358 * @param {Roo.bootstrap.DateField} this
18359 * @param {Mixed} date The date value
18361 beforeselect : true
18365 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18368 * @cfg {String} format
18369 * The default date format string which can be overriden for localization support. The format must be
18370 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18374 * @cfg {String} altFormats
18375 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18376 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18378 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18386 todayHighlight : false,
18392 keyboardNavigation: true,
18394 calendarWeeks: false,
18396 startDate: -Infinity,
18400 daysOfWeekDisabled: [],
18404 singleMode : false,
18406 UTCDate: function()
18408 return new Date(Date.UTC.apply(Date, arguments));
18411 UTCToday: function()
18413 var today = new Date();
18414 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18417 getDate: function() {
18418 var d = this.getUTCDate();
18419 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18422 getUTCDate: function() {
18426 setDate: function(d) {
18427 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18430 setUTCDate: function(d) {
18432 this.setValue(this.formatDate(this.date));
18435 onRender: function(ct, position)
18438 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18440 this.language = this.language || 'en';
18441 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18442 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18444 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18445 this.format = this.format || 'm/d/y';
18446 this.isInline = false;
18447 this.isInput = true;
18448 this.component = this.el.select('.add-on', true).first() || false;
18449 this.component = (this.component && this.component.length === 0) ? false : this.component;
18450 this.hasInput = this.component && this.inputEl().length;
18452 if (typeof(this.minViewMode === 'string')) {
18453 switch (this.minViewMode) {
18455 this.minViewMode = 1;
18458 this.minViewMode = 2;
18461 this.minViewMode = 0;
18466 if (typeof(this.viewMode === 'string')) {
18467 switch (this.viewMode) {
18480 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18482 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18484 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18486 this.picker().on('mousedown', this.onMousedown, this);
18487 this.picker().on('click', this.onClick, this);
18489 this.picker().addClass('datepicker-dropdown');
18491 this.startViewMode = this.viewMode;
18493 if(this.singleMode){
18494 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18495 v.setVisibilityMode(Roo.Element.DISPLAY);
18499 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18500 v.setStyle('width', '189px');
18504 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18505 if(!this.calendarWeeks){
18510 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18511 v.attr('colspan', function(i, val){
18512 return parseInt(val) + 1;
18517 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18519 this.setStartDate(this.startDate);
18520 this.setEndDate(this.endDate);
18522 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18529 if(this.isInline) {
18534 picker : function()
18536 return this.pickerEl;
18537 // return this.el.select('.datepicker', true).first();
18540 fillDow: function()
18542 var dowCnt = this.weekStart;
18551 if(this.calendarWeeks){
18559 while (dowCnt < this.weekStart + 7) {
18563 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18567 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18570 fillMonths: function()
18573 var months = this.picker().select('>.datepicker-months td', true).first();
18575 months.dom.innerHTML = '';
18581 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18584 months.createChild(month);
18591 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;
18593 if (this.date < this.startDate) {
18594 this.viewDate = new Date(this.startDate);
18595 } else if (this.date > this.endDate) {
18596 this.viewDate = new Date(this.endDate);
18598 this.viewDate = new Date(this.date);
18606 var d = new Date(this.viewDate),
18607 year = d.getUTCFullYear(),
18608 month = d.getUTCMonth(),
18609 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18610 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18611 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18612 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18613 currentDate = this.date && this.date.valueOf(),
18614 today = this.UTCToday();
18616 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18618 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18620 // this.picker.select('>tfoot th.today').
18621 // .text(dates[this.language].today)
18622 // .toggle(this.todayBtn !== false);
18624 this.updateNavArrows();
18627 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18629 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18631 prevMonth.setUTCDate(day);
18633 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18635 var nextMonth = new Date(prevMonth);
18637 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18639 nextMonth = nextMonth.valueOf();
18641 var fillMonths = false;
18643 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18645 while(prevMonth.valueOf() < nextMonth) {
18648 if (prevMonth.getUTCDay() === this.weekStart) {
18650 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18658 if(this.calendarWeeks){
18659 // ISO 8601: First week contains first thursday.
18660 // ISO also states week starts on Monday, but we can be more abstract here.
18662 // Start of current week: based on weekstart/current date
18663 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18664 // Thursday of this week
18665 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18666 // First Thursday of year, year from thursday
18667 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18668 // Calendar week: ms between thursdays, div ms per day, div 7 days
18669 calWeek = (th - yth) / 864e5 / 7 + 1;
18671 fillMonths.cn.push({
18679 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18681 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18684 if (this.todayHighlight &&
18685 prevMonth.getUTCFullYear() == today.getFullYear() &&
18686 prevMonth.getUTCMonth() == today.getMonth() &&
18687 prevMonth.getUTCDate() == today.getDate()) {
18688 clsName += ' today';
18691 if (currentDate && prevMonth.valueOf() === currentDate) {
18692 clsName += ' active';
18695 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18696 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18697 clsName += ' disabled';
18700 fillMonths.cn.push({
18702 cls: 'day ' + clsName,
18703 html: prevMonth.getDate()
18706 prevMonth.setDate(prevMonth.getDate()+1);
18709 var currentYear = this.date && this.date.getUTCFullYear();
18710 var currentMonth = this.date && this.date.getUTCMonth();
18712 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18714 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18715 v.removeClass('active');
18717 if(currentYear === year && k === currentMonth){
18718 v.addClass('active');
18721 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18722 v.addClass('disabled');
18728 year = parseInt(year/10, 10) * 10;
18730 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18732 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18735 for (var i = -1; i < 11; i++) {
18736 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18738 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18746 showMode: function(dir)
18749 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18752 Roo.each(this.picker().select('>div',true).elements, function(v){
18753 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18756 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18761 if(this.isInline) {
18765 this.picker().removeClass(['bottom', 'top']);
18767 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18769 * place to the top of element!
18773 this.picker().addClass('top');
18774 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18779 this.picker().addClass('bottom');
18781 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18784 parseDate : function(value)
18786 if(!value || value instanceof Date){
18789 var v = Date.parseDate(value, this.format);
18790 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18791 v = Date.parseDate(value, 'Y-m-d');
18793 if(!v && this.altFormats){
18794 if(!this.altFormatsArray){
18795 this.altFormatsArray = this.altFormats.split("|");
18797 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18798 v = Date.parseDate(value, this.altFormatsArray[i]);
18804 formatDate : function(date, fmt)
18806 return (!date || !(date instanceof Date)) ?
18807 date : date.dateFormat(fmt || this.format);
18810 onFocus : function()
18812 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18816 onBlur : function()
18818 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18820 var d = this.inputEl().getValue();
18829 this.picker().show();
18833 this.fireEvent('show', this, this.date);
18838 if(this.isInline) {
18841 this.picker().hide();
18842 this.viewMode = this.startViewMode;
18845 this.fireEvent('hide', this, this.date);
18849 onMousedown: function(e)
18851 e.stopPropagation();
18852 e.preventDefault();
18857 Roo.bootstrap.DateField.superclass.keyup.call(this);
18861 setValue: function(v)
18863 if(this.fireEvent('beforeselect', this, v) !== false){
18864 var d = new Date(this.parseDate(v) ).clearTime();
18866 if(isNaN(d.getTime())){
18867 this.date = this.viewDate = '';
18868 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18872 v = this.formatDate(d);
18874 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18876 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18880 this.fireEvent('select', this, this.date);
18884 getValue: function()
18886 return this.formatDate(this.date);
18889 fireKey: function(e)
18891 if (!this.picker().isVisible()){
18892 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18898 var dateChanged = false,
18900 newDate, newViewDate;
18905 e.preventDefault();
18909 if (!this.keyboardNavigation) {
18912 dir = e.keyCode == 37 ? -1 : 1;
18915 newDate = this.moveYear(this.date, dir);
18916 newViewDate = this.moveYear(this.viewDate, dir);
18917 } else if (e.shiftKey){
18918 newDate = this.moveMonth(this.date, dir);
18919 newViewDate = this.moveMonth(this.viewDate, dir);
18921 newDate = new Date(this.date);
18922 newDate.setUTCDate(this.date.getUTCDate() + dir);
18923 newViewDate = new Date(this.viewDate);
18924 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18926 if (this.dateWithinRange(newDate)){
18927 this.date = newDate;
18928 this.viewDate = newViewDate;
18929 this.setValue(this.formatDate(this.date));
18931 e.preventDefault();
18932 dateChanged = true;
18937 if (!this.keyboardNavigation) {
18940 dir = e.keyCode == 38 ? -1 : 1;
18942 newDate = this.moveYear(this.date, dir);
18943 newViewDate = this.moveYear(this.viewDate, dir);
18944 } else if (e.shiftKey){
18945 newDate = this.moveMonth(this.date, dir);
18946 newViewDate = this.moveMonth(this.viewDate, dir);
18948 newDate = new Date(this.date);
18949 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18950 newViewDate = new Date(this.viewDate);
18951 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18953 if (this.dateWithinRange(newDate)){
18954 this.date = newDate;
18955 this.viewDate = newViewDate;
18956 this.setValue(this.formatDate(this.date));
18958 e.preventDefault();
18959 dateChanged = true;
18963 this.setValue(this.formatDate(this.date));
18965 e.preventDefault();
18968 this.setValue(this.formatDate(this.date));
18982 onClick: function(e)
18984 e.stopPropagation();
18985 e.preventDefault();
18987 var target = e.getTarget();
18989 if(target.nodeName.toLowerCase() === 'i'){
18990 target = Roo.get(target).dom.parentNode;
18993 var nodeName = target.nodeName;
18994 var className = target.className;
18995 var html = target.innerHTML;
18996 //Roo.log(nodeName);
18998 switch(nodeName.toLowerCase()) {
19000 switch(className) {
19006 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19007 switch(this.viewMode){
19009 this.viewDate = this.moveMonth(this.viewDate, dir);
19013 this.viewDate = this.moveYear(this.viewDate, dir);
19019 var date = new Date();
19020 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19022 this.setValue(this.formatDate(this.date));
19029 if (className.indexOf('disabled') < 0) {
19030 this.viewDate.setUTCDate(1);
19031 if (className.indexOf('month') > -1) {
19032 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19034 var year = parseInt(html, 10) || 0;
19035 this.viewDate.setUTCFullYear(year);
19039 if(this.singleMode){
19040 this.setValue(this.formatDate(this.viewDate));
19051 //Roo.log(className);
19052 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19053 var day = parseInt(html, 10) || 1;
19054 var year = this.viewDate.getUTCFullYear(),
19055 month = this.viewDate.getUTCMonth();
19057 if (className.indexOf('old') > -1) {
19064 } else if (className.indexOf('new') > -1) {
19072 //Roo.log([year,month,day]);
19073 this.date = this.UTCDate(year, month, day,0,0,0,0);
19074 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19076 //Roo.log(this.formatDate(this.date));
19077 this.setValue(this.formatDate(this.date));
19084 setStartDate: function(startDate)
19086 this.startDate = startDate || -Infinity;
19087 if (this.startDate !== -Infinity) {
19088 this.startDate = this.parseDate(this.startDate);
19091 this.updateNavArrows();
19094 setEndDate: function(endDate)
19096 this.endDate = endDate || Infinity;
19097 if (this.endDate !== Infinity) {
19098 this.endDate = this.parseDate(this.endDate);
19101 this.updateNavArrows();
19104 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19106 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19107 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19108 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19110 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19111 return parseInt(d, 10);
19114 this.updateNavArrows();
19117 updateNavArrows: function()
19119 if(this.singleMode){
19123 var d = new Date(this.viewDate),
19124 year = d.getUTCFullYear(),
19125 month = d.getUTCMonth();
19127 Roo.each(this.picker().select('.prev', true).elements, function(v){
19129 switch (this.viewMode) {
19132 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19138 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19145 Roo.each(this.picker().select('.next', true).elements, function(v){
19147 switch (this.viewMode) {
19150 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19156 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19164 moveMonth: function(date, dir)
19169 var new_date = new Date(date.valueOf()),
19170 day = new_date.getUTCDate(),
19171 month = new_date.getUTCMonth(),
19172 mag = Math.abs(dir),
19174 dir = dir > 0 ? 1 : -1;
19177 // If going back one month, make sure month is not current month
19178 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19180 return new_date.getUTCMonth() == month;
19182 // If going forward one month, make sure month is as expected
19183 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19185 return new_date.getUTCMonth() != new_month;
19187 new_month = month + dir;
19188 new_date.setUTCMonth(new_month);
19189 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19190 if (new_month < 0 || new_month > 11) {
19191 new_month = (new_month + 12) % 12;
19194 // For magnitudes >1, move one month at a time...
19195 for (var i=0; i<mag; i++) {
19196 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19197 new_date = this.moveMonth(new_date, dir);
19199 // ...then reset the day, keeping it in the new month
19200 new_month = new_date.getUTCMonth();
19201 new_date.setUTCDate(day);
19203 return new_month != new_date.getUTCMonth();
19206 // Common date-resetting loop -- if date is beyond end of month, make it
19209 new_date.setUTCDate(--day);
19210 new_date.setUTCMonth(new_month);
19215 moveYear: function(date, dir)
19217 return this.moveMonth(date, dir*12);
19220 dateWithinRange: function(date)
19222 return date >= this.startDate && date <= this.endDate;
19228 this.picker().remove();
19231 validateValue : function(value)
19233 if(this.getVisibilityEl().hasClass('hidden')){
19237 if(value.length < 1) {
19238 if(this.allowBlank){
19244 if(value.length < this.minLength){
19247 if(value.length > this.maxLength){
19251 var vt = Roo.form.VTypes;
19252 if(!vt[this.vtype](value, this)){
19256 if(typeof this.validator == "function"){
19257 var msg = this.validator(value);
19263 if(this.regex && !this.regex.test(value)){
19267 if(typeof(this.parseDate(value)) == 'undefined'){
19271 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19275 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19283 setVisible : function(visible)
19289 this.getEl().removeClass('hidden');
19295 this.getEl().addClass('hidden');
19300 Roo.apply(Roo.bootstrap.DateField, {
19311 html: '<i class="fa fa-arrow-left"/>'
19321 html: '<i class="fa fa-arrow-right"/>'
19363 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19364 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19365 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19366 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19367 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19380 navFnc: 'FullYear',
19385 navFnc: 'FullYear',
19390 Roo.apply(Roo.bootstrap.DateField, {
19394 cls: 'datepicker dropdown-menu roo-dynamic',
19398 cls: 'datepicker-days',
19402 cls: 'table-condensed',
19404 Roo.bootstrap.DateField.head,
19408 Roo.bootstrap.DateField.footer
19415 cls: 'datepicker-months',
19419 cls: 'table-condensed',
19421 Roo.bootstrap.DateField.head,
19422 Roo.bootstrap.DateField.content,
19423 Roo.bootstrap.DateField.footer
19430 cls: 'datepicker-years',
19434 cls: 'table-condensed',
19436 Roo.bootstrap.DateField.head,
19437 Roo.bootstrap.DateField.content,
19438 Roo.bootstrap.DateField.footer
19457 * @class Roo.bootstrap.TimeField
19458 * @extends Roo.bootstrap.Input
19459 * Bootstrap DateField class
19463 * Create a new TimeField
19464 * @param {Object} config The config object
19467 Roo.bootstrap.TimeField = function(config){
19468 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19472 * Fires when this field show.
19473 * @param {Roo.bootstrap.DateField} thisthis
19474 * @param {Mixed} date The date value
19479 * Fires when this field hide.
19480 * @param {Roo.bootstrap.DateField} this
19481 * @param {Mixed} date The date value
19486 * Fires when select a date.
19487 * @param {Roo.bootstrap.DateField} this
19488 * @param {Mixed} date The date value
19494 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19497 * @cfg {String} format
19498 * The default time format string which can be overriden for localization support. The format must be
19499 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19503 onRender: function(ct, position)
19506 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19508 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19510 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19512 this.pop = this.picker().select('>.datepicker-time',true).first();
19513 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19515 this.picker().on('mousedown', this.onMousedown, this);
19516 this.picker().on('click', this.onClick, this);
19518 this.picker().addClass('datepicker-dropdown');
19523 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19524 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19525 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19526 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19527 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19528 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19532 fireKey: function(e){
19533 if (!this.picker().isVisible()){
19534 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19540 e.preventDefault();
19548 this.onTogglePeriod();
19551 this.onIncrementMinutes();
19554 this.onDecrementMinutes();
19563 onClick: function(e) {
19564 e.stopPropagation();
19565 e.preventDefault();
19568 picker : function()
19570 return this.el.select('.datepicker', true).first();
19573 fillTime: function()
19575 var time = this.pop.select('tbody', true).first();
19577 time.dom.innerHTML = '';
19592 cls: 'hours-up glyphicon glyphicon-chevron-up'
19612 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19633 cls: 'timepicker-hour',
19648 cls: 'timepicker-minute',
19663 cls: 'btn btn-primary period',
19685 cls: 'hours-down glyphicon glyphicon-chevron-down'
19705 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19723 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19730 var hours = this.time.getHours();
19731 var minutes = this.time.getMinutes();
19744 hours = hours - 12;
19748 hours = '0' + hours;
19752 minutes = '0' + minutes;
19755 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19756 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19757 this.pop.select('button', true).first().dom.innerHTML = period;
19763 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19765 var cls = ['bottom'];
19767 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19774 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19779 this.picker().addClass(cls.join('-'));
19783 Roo.each(cls, function(c){
19785 _this.picker().setTop(_this.inputEl().getHeight());
19789 _this.picker().setTop(0 - _this.picker().getHeight());
19794 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19798 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19805 onFocus : function()
19807 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19811 onBlur : function()
19813 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19819 this.picker().show();
19824 this.fireEvent('show', this, this.date);
19829 this.picker().hide();
19832 this.fireEvent('hide', this, this.date);
19835 setTime : function()
19838 this.setValue(this.time.format(this.format));
19840 this.fireEvent('select', this, this.date);
19845 onMousedown: function(e){
19846 e.stopPropagation();
19847 e.preventDefault();
19850 onIncrementHours: function()
19852 Roo.log('onIncrementHours');
19853 this.time = this.time.add(Date.HOUR, 1);
19858 onDecrementHours: function()
19860 Roo.log('onDecrementHours');
19861 this.time = this.time.add(Date.HOUR, -1);
19865 onIncrementMinutes: function()
19867 Roo.log('onIncrementMinutes');
19868 this.time = this.time.add(Date.MINUTE, 1);
19872 onDecrementMinutes: function()
19874 Roo.log('onDecrementMinutes');
19875 this.time = this.time.add(Date.MINUTE, -1);
19879 onTogglePeriod: function()
19881 Roo.log('onTogglePeriod');
19882 this.time = this.time.add(Date.HOUR, 12);
19889 Roo.apply(Roo.bootstrap.TimeField, {
19919 cls: 'btn btn-info ok',
19931 Roo.apply(Roo.bootstrap.TimeField, {
19935 cls: 'datepicker dropdown-menu',
19939 cls: 'datepicker-time',
19943 cls: 'table-condensed',
19945 Roo.bootstrap.TimeField.content,
19946 Roo.bootstrap.TimeField.footer
19965 * @class Roo.bootstrap.MonthField
19966 * @extends Roo.bootstrap.Input
19967 * Bootstrap MonthField class
19969 * @cfg {String} language default en
19972 * Create a new MonthField
19973 * @param {Object} config The config object
19976 Roo.bootstrap.MonthField = function(config){
19977 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19982 * Fires when this field show.
19983 * @param {Roo.bootstrap.MonthField} this
19984 * @param {Mixed} date The date value
19989 * Fires when this field hide.
19990 * @param {Roo.bootstrap.MonthField} this
19991 * @param {Mixed} date The date value
19996 * Fires when select a date.
19997 * @param {Roo.bootstrap.MonthField} this
19998 * @param {String} oldvalue The old value
19999 * @param {String} newvalue The new value
20005 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20007 onRender: function(ct, position)
20010 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20012 this.language = this.language || 'en';
20013 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20014 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20016 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20017 this.isInline = false;
20018 this.isInput = true;
20019 this.component = this.el.select('.add-on', true).first() || false;
20020 this.component = (this.component && this.component.length === 0) ? false : this.component;
20021 this.hasInput = this.component && this.inputEL().length;
20023 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20025 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20027 this.picker().on('mousedown', this.onMousedown, this);
20028 this.picker().on('click', this.onClick, this);
20030 this.picker().addClass('datepicker-dropdown');
20032 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20033 v.setStyle('width', '189px');
20040 if(this.isInline) {
20046 setValue: function(v, suppressEvent)
20048 var o = this.getValue();
20050 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20054 if(suppressEvent !== true){
20055 this.fireEvent('select', this, o, v);
20060 getValue: function()
20065 onClick: function(e)
20067 e.stopPropagation();
20068 e.preventDefault();
20070 var target = e.getTarget();
20072 if(target.nodeName.toLowerCase() === 'i'){
20073 target = Roo.get(target).dom.parentNode;
20076 var nodeName = target.nodeName;
20077 var className = target.className;
20078 var html = target.innerHTML;
20080 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20084 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20086 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20092 picker : function()
20094 return this.pickerEl;
20097 fillMonths: function()
20100 var months = this.picker().select('>.datepicker-months td', true).first();
20102 months.dom.innerHTML = '';
20108 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20111 months.createChild(month);
20120 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20121 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20124 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20125 e.removeClass('active');
20127 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20128 e.addClass('active');
20135 if(this.isInline) {
20139 this.picker().removeClass(['bottom', 'top']);
20141 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20143 * place to the top of element!
20147 this.picker().addClass('top');
20148 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20153 this.picker().addClass('bottom');
20155 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20158 onFocus : function()
20160 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20164 onBlur : function()
20166 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20168 var d = this.inputEl().getValue();
20177 this.picker().show();
20178 this.picker().select('>.datepicker-months', true).first().show();
20182 this.fireEvent('show', this, this.date);
20187 if(this.isInline) {
20190 this.picker().hide();
20191 this.fireEvent('hide', this, this.date);
20195 onMousedown: function(e)
20197 e.stopPropagation();
20198 e.preventDefault();
20203 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20207 fireKey: function(e)
20209 if (!this.picker().isVisible()){
20210 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20221 e.preventDefault();
20225 dir = e.keyCode == 37 ? -1 : 1;
20227 this.vIndex = this.vIndex + dir;
20229 if(this.vIndex < 0){
20233 if(this.vIndex > 11){
20237 if(isNaN(this.vIndex)){
20241 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20247 dir = e.keyCode == 38 ? -1 : 1;
20249 this.vIndex = this.vIndex + dir * 4;
20251 if(this.vIndex < 0){
20255 if(this.vIndex > 11){
20259 if(isNaN(this.vIndex)){
20263 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20268 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20269 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20273 e.preventDefault();
20276 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20277 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20293 this.picker().remove();
20298 Roo.apply(Roo.bootstrap.MonthField, {
20317 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20318 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20323 Roo.apply(Roo.bootstrap.MonthField, {
20327 cls: 'datepicker dropdown-menu roo-dynamic',
20331 cls: 'datepicker-months',
20335 cls: 'table-condensed',
20337 Roo.bootstrap.DateField.content
20357 * @class Roo.bootstrap.CheckBox
20358 * @extends Roo.bootstrap.Input
20359 * Bootstrap CheckBox class
20361 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20362 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20363 * @cfg {String} boxLabel The text that appears beside the checkbox
20364 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20365 * @cfg {Boolean} checked initnal the element
20366 * @cfg {Boolean} inline inline the element (default false)
20367 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20368 * @cfg {String} tooltip label tooltip
20371 * Create a new CheckBox
20372 * @param {Object} config The config object
20375 Roo.bootstrap.CheckBox = function(config){
20376 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20381 * Fires when the element is checked or unchecked.
20382 * @param {Roo.bootstrap.CheckBox} this This input
20383 * @param {Boolean} checked The new checked value
20388 * Fires when the element is click.
20389 * @param {Roo.bootstrap.CheckBox} this This input
20396 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20398 inputType: 'checkbox',
20407 getAutoCreate : function()
20409 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20415 cfg.cls = 'form-group ' + this.inputType; //input-group
20418 cfg.cls += ' ' + this.inputType + '-inline';
20424 type : this.inputType,
20425 value : this.inputValue,
20426 cls : 'roo-' + this.inputType, //'form-box',
20427 placeholder : this.placeholder || ''
20431 if(this.inputType != 'radio'){
20435 cls : 'roo-hidden-value',
20436 value : this.checked ? this.inputValue : this.valueOff
20441 if (this.weight) { // Validity check?
20442 cfg.cls += " " + this.inputType + "-" + this.weight;
20445 if (this.disabled) {
20446 input.disabled=true;
20450 input.checked = this.checked;
20455 input.name = this.name;
20457 if(this.inputType != 'radio'){
20458 hidden.name = this.name;
20459 input.name = '_hidden_' + this.name;
20464 input.cls += ' input-' + this.size;
20469 ['xs','sm','md','lg'].map(function(size){
20470 if (settings[size]) {
20471 cfg.cls += ' col-' + size + '-' + settings[size];
20475 var inputblock = input;
20477 if (this.before || this.after) {
20480 cls : 'input-group',
20485 inputblock.cn.push({
20487 cls : 'input-group-addon',
20492 inputblock.cn.push(input);
20494 if(this.inputType != 'radio'){
20495 inputblock.cn.push(hidden);
20499 inputblock.cn.push({
20501 cls : 'input-group-addon',
20508 if (align ==='left' && this.fieldLabel.length) {
20509 // Roo.log("left and has label");
20514 cls : 'control-label',
20515 html : this.fieldLabel
20525 if(this.labelWidth > 12){
20526 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20529 if(this.labelWidth < 13 && this.labelmd == 0){
20530 this.labelmd = this.labelWidth;
20533 if(this.labellg > 0){
20534 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20535 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20538 if(this.labelmd > 0){
20539 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20540 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20543 if(this.labelsm > 0){
20544 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20545 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20548 if(this.labelxs > 0){
20549 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20550 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20553 } else if ( this.fieldLabel.length) {
20554 // Roo.log(" label");
20558 tag: this.boxLabel ? 'span' : 'label',
20560 cls: 'control-label box-input-label',
20561 //cls : 'input-group-addon',
20562 html : this.fieldLabel
20571 // Roo.log(" no label && no align");
20572 cfg.cn = [ inputblock ] ;
20578 var boxLabelCfg = {
20580 //'for': id, // box label is handled by onclick - so no for...
20582 html: this.boxLabel
20586 boxLabelCfg.tooltip = this.tooltip;
20589 cfg.cn.push(boxLabelCfg);
20592 if(this.inputType != 'radio'){
20593 cfg.cn.push(hidden);
20601 * return the real input element.
20603 inputEl: function ()
20605 return this.el.select('input.roo-' + this.inputType,true).first();
20607 hiddenEl: function ()
20609 return this.el.select('input.roo-hidden-value',true).first();
20612 labelEl: function()
20614 return this.el.select('label.control-label',true).first();
20616 /* depricated... */
20620 return this.labelEl();
20623 boxLabelEl: function()
20625 return this.el.select('label.box-label',true).first();
20628 initEvents : function()
20630 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20632 this.inputEl().on('click', this.onClick, this);
20634 if (this.boxLabel) {
20635 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20638 this.startValue = this.getValue();
20641 Roo.bootstrap.CheckBox.register(this);
20645 onClick : function(e)
20647 if(this.fireEvent('click', this, e) !== false){
20648 this.setChecked(!this.checked);
20653 setChecked : function(state,suppressEvent)
20655 this.startValue = this.getValue();
20657 if(this.inputType == 'radio'){
20659 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20660 e.dom.checked = false;
20663 this.inputEl().dom.checked = true;
20665 this.inputEl().dom.value = this.inputValue;
20667 if(suppressEvent !== true){
20668 this.fireEvent('check', this, true);
20676 this.checked = state;
20678 this.inputEl().dom.checked = state;
20681 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20683 if(suppressEvent !== true){
20684 this.fireEvent('check', this, state);
20690 getValue : function()
20692 if(this.inputType == 'radio'){
20693 return this.getGroupValue();
20696 return this.hiddenEl().dom.value;
20700 getGroupValue : function()
20702 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20706 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20709 setValue : function(v,suppressEvent)
20711 if(this.inputType == 'radio'){
20712 this.setGroupValue(v, suppressEvent);
20716 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20721 setGroupValue : function(v, suppressEvent)
20723 this.startValue = this.getValue();
20725 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20726 e.dom.checked = false;
20728 if(e.dom.value == v){
20729 e.dom.checked = true;
20733 if(suppressEvent !== true){
20734 this.fireEvent('check', this, true);
20742 validate : function()
20744 if(this.getVisibilityEl().hasClass('hidden')){
20750 (this.inputType == 'radio' && this.validateRadio()) ||
20751 (this.inputType == 'checkbox' && this.validateCheckbox())
20757 this.markInvalid();
20761 validateRadio : function()
20763 if(this.getVisibilityEl().hasClass('hidden')){
20767 if(this.allowBlank){
20773 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20774 if(!e.dom.checked){
20786 validateCheckbox : function()
20789 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20790 //return (this.getValue() == this.inputValue) ? true : false;
20793 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20801 for(var i in group){
20802 if(group[i].el.isVisible(true)){
20810 for(var i in group){
20815 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20822 * Mark this field as valid
20824 markValid : function()
20828 this.fireEvent('valid', this);
20830 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20833 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20840 if(this.inputType == 'radio'){
20841 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20842 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20843 e.findParent('.form-group', false, true).addClass(_this.validClass);
20850 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20851 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20855 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20861 for(var i in group){
20862 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20863 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20868 * Mark this field as invalid
20869 * @param {String} msg The validation message
20871 markInvalid : function(msg)
20873 if(this.allowBlank){
20879 this.fireEvent('invalid', this, msg);
20881 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20884 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20888 label.markInvalid();
20891 if(this.inputType == 'radio'){
20892 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20893 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20894 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20901 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20902 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20906 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20912 for(var i in group){
20913 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20914 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20919 clearInvalid : function()
20921 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20923 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20925 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20927 if (label && label.iconEl) {
20928 label.iconEl.removeClass(label.validClass);
20929 label.iconEl.removeClass(label.invalidClass);
20933 disable : function()
20935 if(this.inputType != 'radio'){
20936 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20943 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944 _this.getActionEl().addClass(this.disabledClass);
20945 e.dom.disabled = true;
20949 this.disabled = true;
20950 this.fireEvent("disable", this);
20954 enable : function()
20956 if(this.inputType != 'radio'){
20957 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20964 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20965 _this.getActionEl().removeClass(this.disabledClass);
20966 e.dom.disabled = false;
20970 this.disabled = false;
20971 this.fireEvent("enable", this);
20975 setBoxLabel : function(v)
20980 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20986 Roo.apply(Roo.bootstrap.CheckBox, {
20991 * register a CheckBox Group
20992 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20994 register : function(checkbox)
20996 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20997 this.groups[checkbox.groupId] = {};
21000 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21004 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21008 * fetch a CheckBox Group based on the group ID
21009 * @param {string} the group ID
21010 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21012 get: function(groupId) {
21013 if (typeof(this.groups[groupId]) == 'undefined') {
21017 return this.groups[groupId] ;
21030 * @class Roo.bootstrap.Radio
21031 * @extends Roo.bootstrap.Component
21032 * Bootstrap Radio class
21033 * @cfg {String} boxLabel - the label associated
21034 * @cfg {String} value - the value of radio
21037 * Create a new Radio
21038 * @param {Object} config The config object
21040 Roo.bootstrap.Radio = function(config){
21041 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21045 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21051 getAutoCreate : function()
21055 cls : 'form-group radio',
21060 html : this.boxLabel
21068 initEvents : function()
21070 this.parent().register(this);
21072 this.el.on('click', this.onClick, this);
21076 onClick : function(e)
21078 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21079 this.setChecked(true);
21083 setChecked : function(state, suppressEvent)
21085 this.parent().setValue(this.value, suppressEvent);
21089 setBoxLabel : function(v)
21094 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21109 * @class Roo.bootstrap.SecurePass
21110 * @extends Roo.bootstrap.Input
21111 * Bootstrap SecurePass class
21115 * Create a new SecurePass
21116 * @param {Object} config The config object
21119 Roo.bootstrap.SecurePass = function (config) {
21120 // these go here, so the translation tool can replace them..
21122 PwdEmpty: "Please type a password, and then retype it to confirm.",
21123 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21124 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21125 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21126 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21127 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21128 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21129 TooWeak: "Your password is Too Weak."
21131 this.meterLabel = "Password strength:";
21132 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21133 this.meterClass = [
21134 "roo-password-meter-tooweak",
21135 "roo-password-meter-weak",
21136 "roo-password-meter-medium",
21137 "roo-password-meter-strong",
21138 "roo-password-meter-grey"
21143 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21146 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21148 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21150 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21151 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21152 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21153 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21154 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21155 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21156 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21166 * @cfg {String/Object} Label for the strength meter (defaults to
21167 * 'Password strength:')
21172 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21173 * ['Weak', 'Medium', 'Strong'])
21176 pwdStrengths: false,
21189 initEvents: function ()
21191 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21193 if (this.el.is('input[type=password]') && Roo.isSafari) {
21194 this.el.on('keydown', this.SafariOnKeyDown, this);
21197 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21200 onRender: function (ct, position)
21202 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21203 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21204 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21206 this.trigger.createChild({
21211 cls: 'roo-password-meter-grey col-xs-12',
21214 //width: this.meterWidth + 'px'
21218 cls: 'roo-password-meter-text'
21224 if (this.hideTrigger) {
21225 this.trigger.setDisplayed(false);
21227 this.setSize(this.width || '', this.height || '');
21230 onDestroy: function ()
21232 if (this.trigger) {
21233 this.trigger.removeAllListeners();
21234 this.trigger.remove();
21237 this.wrap.remove();
21239 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21242 checkStrength: function ()
21244 var pwd = this.inputEl().getValue();
21245 if (pwd == this._lastPwd) {
21250 if (this.ClientSideStrongPassword(pwd)) {
21252 } else if (this.ClientSideMediumPassword(pwd)) {
21254 } else if (this.ClientSideWeakPassword(pwd)) {
21260 Roo.log('strength1: ' + strength);
21262 //var pm = this.trigger.child('div/div/div').dom;
21263 var pm = this.trigger.child('div/div');
21264 pm.removeClass(this.meterClass);
21265 pm.addClass(this.meterClass[strength]);
21268 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21270 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21272 this._lastPwd = pwd;
21276 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21278 this._lastPwd = '';
21280 var pm = this.trigger.child('div/div');
21281 pm.removeClass(this.meterClass);
21282 pm.addClass('roo-password-meter-grey');
21285 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21288 this.inputEl().dom.type='password';
21291 validateValue: function (value)
21294 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21297 if (value.length == 0) {
21298 if (this.allowBlank) {
21299 this.clearInvalid();
21303 this.markInvalid(this.errors.PwdEmpty);
21304 this.errorMsg = this.errors.PwdEmpty;
21312 if ('[\x21-\x7e]*'.match(value)) {
21313 this.markInvalid(this.errors.PwdBadChar);
21314 this.errorMsg = this.errors.PwdBadChar;
21317 if (value.length < 6) {
21318 this.markInvalid(this.errors.PwdShort);
21319 this.errorMsg = this.errors.PwdShort;
21322 if (value.length > 16) {
21323 this.markInvalid(this.errors.PwdLong);
21324 this.errorMsg = this.errors.PwdLong;
21328 if (this.ClientSideStrongPassword(value)) {
21330 } else if (this.ClientSideMediumPassword(value)) {
21332 } else if (this.ClientSideWeakPassword(value)) {
21339 if (strength < 2) {
21340 //this.markInvalid(this.errors.TooWeak);
21341 this.errorMsg = this.errors.TooWeak;
21346 console.log('strength2: ' + strength);
21348 //var pm = this.trigger.child('div/div/div').dom;
21350 var pm = this.trigger.child('div/div');
21351 pm.removeClass(this.meterClass);
21352 pm.addClass(this.meterClass[strength]);
21354 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21356 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21358 this.errorMsg = '';
21362 CharacterSetChecks: function (type)
21365 this.fResult = false;
21368 isctype: function (character, type)
21371 case this.kCapitalLetter:
21372 if (character >= 'A' && character <= 'Z') {
21377 case this.kSmallLetter:
21378 if (character >= 'a' && character <= 'z') {
21384 if (character >= '0' && character <= '9') {
21389 case this.kPunctuation:
21390 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21401 IsLongEnough: function (pwd, size)
21403 return !(pwd == null || isNaN(size) || pwd.length < size);
21406 SpansEnoughCharacterSets: function (word, nb)
21408 if (!this.IsLongEnough(word, nb))
21413 var characterSetChecks = new Array(
21414 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21415 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21418 for (var index = 0; index < word.length; ++index) {
21419 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21420 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21421 characterSetChecks[nCharSet].fResult = true;
21428 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21429 if (characterSetChecks[nCharSet].fResult) {
21434 if (nCharSets < nb) {
21440 ClientSideStrongPassword: function (pwd)
21442 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21445 ClientSideMediumPassword: function (pwd)
21447 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21450 ClientSideWeakPassword: function (pwd)
21452 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21455 })//<script type="text/javascript">
21458 * Based Ext JS Library 1.1.1
21459 * Copyright(c) 2006-2007, Ext JS, LLC.
21465 * @class Roo.HtmlEditorCore
21466 * @extends Roo.Component
21467 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21469 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21472 Roo.HtmlEditorCore = function(config){
21475 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21480 * @event initialize
21481 * Fires when the editor is fully initialized (including the iframe)
21482 * @param {Roo.HtmlEditorCore} this
21487 * Fires when the editor is first receives the focus. Any insertion must wait
21488 * until after this event.
21489 * @param {Roo.HtmlEditorCore} this
21493 * @event beforesync
21494 * Fires before the textarea is updated with content from the editor iframe. Return false
21495 * to cancel the sync.
21496 * @param {Roo.HtmlEditorCore} this
21497 * @param {String} html
21501 * @event beforepush
21502 * Fires before the iframe editor is updated with content from the textarea. Return false
21503 * to cancel the push.
21504 * @param {Roo.HtmlEditorCore} this
21505 * @param {String} html
21510 * Fires when the textarea is updated with content from the editor iframe.
21511 * @param {Roo.HtmlEditorCore} this
21512 * @param {String} html
21517 * Fires when the iframe editor is updated with content from the textarea.
21518 * @param {Roo.HtmlEditorCore} this
21519 * @param {String} html
21524 * @event editorevent
21525 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21526 * @param {Roo.HtmlEditorCore} this
21532 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21534 // defaults : white / black...
21535 this.applyBlacklists();
21542 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21546 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21552 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21557 * @cfg {Number} height (in pixels)
21561 * @cfg {Number} width (in pixels)
21566 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21569 stylesheets: false,
21574 // private properties
21575 validationEvent : false,
21577 initialized : false,
21579 sourceEditMode : false,
21580 onFocus : Roo.emptyFn,
21582 hideMode:'offsets',
21586 // blacklist + whitelisted elements..
21593 * Protected method that will not generally be called directly. It
21594 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21595 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21597 getDocMarkup : function(){
21601 // inherit styels from page...??
21602 if (this.stylesheets === false) {
21604 Roo.get(document.head).select('style').each(function(node) {
21605 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21608 Roo.get(document.head).select('link').each(function(node) {
21609 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21612 } else if (!this.stylesheets.length) {
21614 st = '<style type="text/css">' +
21615 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21618 st = '<style type="text/css">' +
21623 st += '<style type="text/css">' +
21624 'IMG { cursor: pointer } ' +
21627 var cls = 'roo-htmleditor-body';
21629 if(this.bodyCls.length){
21630 cls += ' ' + this.bodyCls;
21633 return '<html><head>' + st +
21634 //<style type="text/css">' +
21635 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21637 ' </head><body class="' + cls + '"></body></html>';
21641 onRender : function(ct, position)
21644 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21645 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21648 this.el.dom.style.border = '0 none';
21649 this.el.dom.setAttribute('tabIndex', -1);
21650 this.el.addClass('x-hidden hide');
21654 if(Roo.isIE){ // fix IE 1px bogus margin
21655 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21659 this.frameId = Roo.id();
21663 var iframe = this.owner.wrap.createChild({
21665 cls: 'form-control', // bootstrap..
21667 name: this.frameId,
21668 frameBorder : 'no',
21669 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21674 this.iframe = iframe.dom;
21676 this.assignDocWin();
21678 this.doc.designMode = 'on';
21681 this.doc.write(this.getDocMarkup());
21685 var task = { // must defer to wait for browser to be ready
21687 //console.log("run task?" + this.doc.readyState);
21688 this.assignDocWin();
21689 if(this.doc.body || this.doc.readyState == 'complete'){
21691 this.doc.designMode="on";
21695 Roo.TaskMgr.stop(task);
21696 this.initEditor.defer(10, this);
21703 Roo.TaskMgr.start(task);
21708 onResize : function(w, h)
21710 Roo.log('resize: ' +w + ',' + h );
21711 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21715 if(typeof w == 'number'){
21717 this.iframe.style.width = w + 'px';
21719 if(typeof h == 'number'){
21721 this.iframe.style.height = h + 'px';
21723 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21730 * Toggles the editor between standard and source edit mode.
21731 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21733 toggleSourceEdit : function(sourceEditMode){
21735 this.sourceEditMode = sourceEditMode === true;
21737 if(this.sourceEditMode){
21739 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21742 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21743 //this.iframe.className = '';
21746 //this.setSize(this.owner.wrap.getSize());
21747 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21754 * Protected method that will not generally be called directly. If you need/want
21755 * custom HTML cleanup, this is the method you should override.
21756 * @param {String} html The HTML to be cleaned
21757 * return {String} The cleaned HTML
21759 cleanHtml : function(html){
21760 html = String(html);
21761 if(html.length > 5){
21762 if(Roo.isSafari){ // strip safari nonsense
21763 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21766 if(html == ' '){
21773 * HTML Editor -> Textarea
21774 * Protected method that will not generally be called directly. Syncs the contents
21775 * of the editor iframe with the textarea.
21777 syncValue : function(){
21778 if(this.initialized){
21779 var bd = (this.doc.body || this.doc.documentElement);
21780 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21781 var html = bd.innerHTML;
21783 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21784 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21786 html = '<div style="'+m[0]+'">' + html + '</div>';
21789 html = this.cleanHtml(html);
21790 // fix up the special chars.. normaly like back quotes in word...
21791 // however we do not want to do this with chinese..
21792 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21793 var cc = b.charCodeAt();
21795 (cc >= 0x4E00 && cc < 0xA000 ) ||
21796 (cc >= 0x3400 && cc < 0x4E00 ) ||
21797 (cc >= 0xf900 && cc < 0xfb00 )
21803 if(this.owner.fireEvent('beforesync', this, html) !== false){
21804 this.el.dom.value = html;
21805 this.owner.fireEvent('sync', this, html);
21811 * Protected method that will not generally be called directly. Pushes the value of the textarea
21812 * into the iframe editor.
21814 pushValue : function(){
21815 if(this.initialized){
21816 var v = this.el.dom.value.trim();
21818 // if(v.length < 1){
21822 if(this.owner.fireEvent('beforepush', this, v) !== false){
21823 var d = (this.doc.body || this.doc.documentElement);
21825 this.cleanUpPaste();
21826 this.el.dom.value = d.innerHTML;
21827 this.owner.fireEvent('push', this, v);
21833 deferFocus : function(){
21834 this.focus.defer(10, this);
21838 focus : function(){
21839 if(this.win && !this.sourceEditMode){
21846 assignDocWin: function()
21848 var iframe = this.iframe;
21851 this.doc = iframe.contentWindow.document;
21852 this.win = iframe.contentWindow;
21854 // if (!Roo.get(this.frameId)) {
21857 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21858 // this.win = Roo.get(this.frameId).dom.contentWindow;
21860 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21864 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21865 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21870 initEditor : function(){
21871 //console.log("INIT EDITOR");
21872 this.assignDocWin();
21876 this.doc.designMode="on";
21878 this.doc.write(this.getDocMarkup());
21881 var dbody = (this.doc.body || this.doc.documentElement);
21882 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21883 // this copies styles from the containing element into thsi one..
21884 // not sure why we need all of this..
21885 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21887 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21888 //ss['background-attachment'] = 'fixed'; // w3c
21889 dbody.bgProperties = 'fixed'; // ie
21890 //Roo.DomHelper.applyStyles(dbody, ss);
21891 Roo.EventManager.on(this.doc, {
21892 //'mousedown': this.onEditorEvent,
21893 'mouseup': this.onEditorEvent,
21894 'dblclick': this.onEditorEvent,
21895 'click': this.onEditorEvent,
21896 'keyup': this.onEditorEvent,
21901 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21903 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21904 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21906 this.initialized = true;
21908 this.owner.fireEvent('initialize', this);
21913 onDestroy : function(){
21919 //for (var i =0; i < this.toolbars.length;i++) {
21920 // // fixme - ask toolbars for heights?
21921 // this.toolbars[i].onDestroy();
21924 //this.wrap.dom.innerHTML = '';
21925 //this.wrap.remove();
21930 onFirstFocus : function(){
21932 this.assignDocWin();
21935 this.activated = true;
21938 if(Roo.isGecko){ // prevent silly gecko errors
21940 var s = this.win.getSelection();
21941 if(!s.focusNode || s.focusNode.nodeType != 3){
21942 var r = s.getRangeAt(0);
21943 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21948 this.execCmd('useCSS', true);
21949 this.execCmd('styleWithCSS', false);
21952 this.owner.fireEvent('activate', this);
21956 adjustFont: function(btn){
21957 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21958 //if(Roo.isSafari){ // safari
21961 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21962 if(Roo.isSafari){ // safari
21963 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21964 v = (v < 10) ? 10 : v;
21965 v = (v > 48) ? 48 : v;
21966 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21971 v = Math.max(1, v+adjust);
21973 this.execCmd('FontSize', v );
21976 onEditorEvent : function(e)
21978 this.owner.fireEvent('editorevent', this, e);
21979 // this.updateToolbar();
21980 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21983 insertTag : function(tg)
21985 // could be a bit smarter... -> wrap the current selected tRoo..
21986 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21988 range = this.createRange(this.getSelection());
21989 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21990 wrappingNode.appendChild(range.extractContents());
21991 range.insertNode(wrappingNode);
21998 this.execCmd("formatblock", tg);
22002 insertText : function(txt)
22006 var range = this.createRange();
22007 range.deleteContents();
22008 //alert(Sender.getAttribute('label'));
22010 range.insertNode(this.doc.createTextNode(txt));
22016 * Executes a Midas editor command on the editor document and performs necessary focus and
22017 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22018 * @param {String} cmd The Midas command
22019 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22021 relayCmd : function(cmd, value){
22023 this.execCmd(cmd, value);
22024 this.owner.fireEvent('editorevent', this);
22025 //this.updateToolbar();
22026 this.owner.deferFocus();
22030 * Executes a Midas editor command directly on the editor document.
22031 * For visual commands, you should use {@link #relayCmd} instead.
22032 * <b>This should only be called after the editor is initialized.</b>
22033 * @param {String} cmd The Midas command
22034 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22036 execCmd : function(cmd, value){
22037 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22044 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22046 * @param {String} text | dom node..
22048 insertAtCursor : function(text)
22051 if(!this.activated){
22057 var r = this.doc.selection.createRange();
22068 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22072 // from jquery ui (MIT licenced)
22074 var win = this.win;
22076 if (win.getSelection && win.getSelection().getRangeAt) {
22077 range = win.getSelection().getRangeAt(0);
22078 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22079 range.insertNode(node);
22080 } else if (win.document.selection && win.document.selection.createRange) {
22081 // no firefox support
22082 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22083 win.document.selection.createRange().pasteHTML(txt);
22085 // no firefox support
22086 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22087 this.execCmd('InsertHTML', txt);
22096 mozKeyPress : function(e){
22098 var c = e.getCharCode(), cmd;
22101 c = String.fromCharCode(c).toLowerCase();
22115 this.cleanUpPaste.defer(100, this);
22123 e.preventDefault();
22131 fixKeys : function(){ // load time branching for fastest keydown performance
22133 return function(e){
22134 var k = e.getKey(), r;
22137 r = this.doc.selection.createRange();
22140 r.pasteHTML('    ');
22147 r = this.doc.selection.createRange();
22149 var target = r.parentElement();
22150 if(!target || target.tagName.toLowerCase() != 'li'){
22152 r.pasteHTML('<br />');
22158 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22159 this.cleanUpPaste.defer(100, this);
22165 }else if(Roo.isOpera){
22166 return function(e){
22167 var k = e.getKey();
22171 this.execCmd('InsertHTML','    ');
22174 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22175 this.cleanUpPaste.defer(100, this);
22180 }else if(Roo.isSafari){
22181 return function(e){
22182 var k = e.getKey();
22186 this.execCmd('InsertText','\t');
22190 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22191 this.cleanUpPaste.defer(100, this);
22199 getAllAncestors: function()
22201 var p = this.getSelectedNode();
22204 a.push(p); // push blank onto stack..
22205 p = this.getParentElement();
22209 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22213 a.push(this.doc.body);
22217 lastSelNode : false,
22220 getSelection : function()
22222 this.assignDocWin();
22223 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22226 getSelectedNode: function()
22228 // this may only work on Gecko!!!
22230 // should we cache this!!!!
22235 var range = this.createRange(this.getSelection()).cloneRange();
22238 var parent = range.parentElement();
22240 var testRange = range.duplicate();
22241 testRange.moveToElementText(parent);
22242 if (testRange.inRange(range)) {
22245 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22248 parent = parent.parentElement;
22253 // is ancestor a text element.
22254 var ac = range.commonAncestorContainer;
22255 if (ac.nodeType == 3) {
22256 ac = ac.parentNode;
22259 var ar = ac.childNodes;
22262 var other_nodes = [];
22263 var has_other_nodes = false;
22264 for (var i=0;i<ar.length;i++) {
22265 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22268 // fullly contained node.
22270 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22275 // probably selected..
22276 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22277 other_nodes.push(ar[i]);
22281 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22286 has_other_nodes = true;
22288 if (!nodes.length && other_nodes.length) {
22289 nodes= other_nodes;
22291 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22297 createRange: function(sel)
22299 // this has strange effects when using with
22300 // top toolbar - not sure if it's a great idea.
22301 //this.editor.contentWindow.focus();
22302 if (typeof sel != "undefined") {
22304 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22306 return this.doc.createRange();
22309 return this.doc.createRange();
22312 getParentElement: function()
22315 this.assignDocWin();
22316 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22318 var range = this.createRange(sel);
22321 var p = range.commonAncestorContainer;
22322 while (p.nodeType == 3) { // text node
22333 * Range intersection.. the hard stuff...
22337 * [ -- selected range --- ]
22341 * if end is before start or hits it. fail.
22342 * if start is after end or hits it fail.
22344 * if either hits (but other is outside. - then it's not
22350 // @see http://www.thismuchiknow.co.uk/?p=64.
22351 rangeIntersectsNode : function(range, node)
22353 var nodeRange = node.ownerDocument.createRange();
22355 nodeRange.selectNode(node);
22357 nodeRange.selectNodeContents(node);
22360 var rangeStartRange = range.cloneRange();
22361 rangeStartRange.collapse(true);
22363 var rangeEndRange = range.cloneRange();
22364 rangeEndRange.collapse(false);
22366 var nodeStartRange = nodeRange.cloneRange();
22367 nodeStartRange.collapse(true);
22369 var nodeEndRange = nodeRange.cloneRange();
22370 nodeEndRange.collapse(false);
22372 return rangeStartRange.compareBoundaryPoints(
22373 Range.START_TO_START, nodeEndRange) == -1 &&
22374 rangeEndRange.compareBoundaryPoints(
22375 Range.START_TO_START, nodeStartRange) == 1;
22379 rangeCompareNode : function(range, node)
22381 var nodeRange = node.ownerDocument.createRange();
22383 nodeRange.selectNode(node);
22385 nodeRange.selectNodeContents(node);
22389 range.collapse(true);
22391 nodeRange.collapse(true);
22393 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22394 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22396 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22398 var nodeIsBefore = ss == 1;
22399 var nodeIsAfter = ee == -1;
22401 if (nodeIsBefore && nodeIsAfter) {
22404 if (!nodeIsBefore && nodeIsAfter) {
22405 return 1; //right trailed.
22408 if (nodeIsBefore && !nodeIsAfter) {
22409 return 2; // left trailed.
22415 // private? - in a new class?
22416 cleanUpPaste : function()
22418 // cleans up the whole document..
22419 Roo.log('cleanuppaste');
22421 this.cleanUpChildren(this.doc.body);
22422 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22423 if (clean != this.doc.body.innerHTML) {
22424 this.doc.body.innerHTML = clean;
22429 cleanWordChars : function(input) {// change the chars to hex code
22430 var he = Roo.HtmlEditorCore;
22432 var output = input;
22433 Roo.each(he.swapCodes, function(sw) {
22434 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22436 output = output.replace(swapper, sw[1]);
22443 cleanUpChildren : function (n)
22445 if (!n.childNodes.length) {
22448 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22449 this.cleanUpChild(n.childNodes[i]);
22456 cleanUpChild : function (node)
22459 //console.log(node);
22460 if (node.nodeName == "#text") {
22461 // clean up silly Windows -- stuff?
22464 if (node.nodeName == "#comment") {
22465 node.parentNode.removeChild(node);
22466 // clean up silly Windows -- stuff?
22469 var lcname = node.tagName.toLowerCase();
22470 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22471 // whitelist of tags..
22473 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22475 node.parentNode.removeChild(node);
22480 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22482 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22483 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22485 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22486 // remove_keep_children = true;
22489 if (remove_keep_children) {
22490 this.cleanUpChildren(node);
22491 // inserts everything just before this node...
22492 while (node.childNodes.length) {
22493 var cn = node.childNodes[0];
22494 node.removeChild(cn);
22495 node.parentNode.insertBefore(cn, node);
22497 node.parentNode.removeChild(node);
22501 if (!node.attributes || !node.attributes.length) {
22502 this.cleanUpChildren(node);
22506 function cleanAttr(n,v)
22509 if (v.match(/^\./) || v.match(/^\//)) {
22512 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22515 if (v.match(/^#/)) {
22518 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22519 node.removeAttribute(n);
22523 var cwhite = this.cwhite;
22524 var cblack = this.cblack;
22526 function cleanStyle(n,v)
22528 if (v.match(/expression/)) { //XSS?? should we even bother..
22529 node.removeAttribute(n);
22533 var parts = v.split(/;/);
22536 Roo.each(parts, function(p) {
22537 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22541 var l = p.split(':').shift().replace(/\s+/g,'');
22542 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22544 if ( cwhite.length && cblack.indexOf(l) > -1) {
22545 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22546 //node.removeAttribute(n);
22550 // only allow 'c whitelisted system attributes'
22551 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22552 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22553 //node.removeAttribute(n);
22563 if (clean.length) {
22564 node.setAttribute(n, clean.join(';'));
22566 node.removeAttribute(n);
22572 for (var i = node.attributes.length-1; i > -1 ; i--) {
22573 var a = node.attributes[i];
22576 if (a.name.toLowerCase().substr(0,2)=='on') {
22577 node.removeAttribute(a.name);
22580 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22581 node.removeAttribute(a.name);
22584 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22585 cleanAttr(a.name,a.value); // fixme..
22588 if (a.name == 'style') {
22589 cleanStyle(a.name,a.value);
22592 /// clean up MS crap..
22593 // tecnically this should be a list of valid class'es..
22596 if (a.name == 'class') {
22597 if (a.value.match(/^Mso/)) {
22598 node.className = '';
22601 if (a.value.match(/^body$/)) {
22602 node.className = '';
22613 this.cleanUpChildren(node);
22619 * Clean up MS wordisms...
22621 cleanWord : function(node)
22626 this.cleanWord(this.doc.body);
22629 if (node.nodeName == "#text") {
22630 // clean up silly Windows -- stuff?
22633 if (node.nodeName == "#comment") {
22634 node.parentNode.removeChild(node);
22635 // clean up silly Windows -- stuff?
22639 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22640 node.parentNode.removeChild(node);
22644 // remove - but keep children..
22645 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22646 while (node.childNodes.length) {
22647 var cn = node.childNodes[0];
22648 node.removeChild(cn);
22649 node.parentNode.insertBefore(cn, node);
22651 node.parentNode.removeChild(node);
22652 this.iterateChildren(node, this.cleanWord);
22656 if (node.className.length) {
22658 var cn = node.className.split(/\W+/);
22660 Roo.each(cn, function(cls) {
22661 if (cls.match(/Mso[a-zA-Z]+/)) {
22666 node.className = cna.length ? cna.join(' ') : '';
22668 node.removeAttribute("class");
22672 if (node.hasAttribute("lang")) {
22673 node.removeAttribute("lang");
22676 if (node.hasAttribute("style")) {
22678 var styles = node.getAttribute("style").split(";");
22680 Roo.each(styles, function(s) {
22681 if (!s.match(/:/)) {
22684 var kv = s.split(":");
22685 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22688 // what ever is left... we allow.
22691 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22692 if (!nstyle.length) {
22693 node.removeAttribute('style');
22696 this.iterateChildren(node, this.cleanWord);
22702 * iterateChildren of a Node, calling fn each time, using this as the scole..
22703 * @param {DomNode} node node to iterate children of.
22704 * @param {Function} fn method of this class to call on each item.
22706 iterateChildren : function(node, fn)
22708 if (!node.childNodes.length) {
22711 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22712 fn.call(this, node.childNodes[i])
22718 * cleanTableWidths.
22720 * Quite often pasting from word etc.. results in tables with column and widths.
22721 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22724 cleanTableWidths : function(node)
22729 this.cleanTableWidths(this.doc.body);
22734 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22737 Roo.log(node.tagName);
22738 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22739 this.iterateChildren(node, this.cleanTableWidths);
22742 if (node.hasAttribute('width')) {
22743 node.removeAttribute('width');
22747 if (node.hasAttribute("style")) {
22750 var styles = node.getAttribute("style").split(";");
22752 Roo.each(styles, function(s) {
22753 if (!s.match(/:/)) {
22756 var kv = s.split(":");
22757 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22760 // what ever is left... we allow.
22763 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22764 if (!nstyle.length) {
22765 node.removeAttribute('style');
22769 this.iterateChildren(node, this.cleanTableWidths);
22777 domToHTML : function(currentElement, depth, nopadtext) {
22779 depth = depth || 0;
22780 nopadtext = nopadtext || false;
22782 if (!currentElement) {
22783 return this.domToHTML(this.doc.body);
22786 //Roo.log(currentElement);
22788 var allText = false;
22789 var nodeName = currentElement.nodeName;
22790 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22792 if (nodeName == '#text') {
22794 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22799 if (nodeName != 'BODY') {
22802 // Prints the node tagName, such as <A>, <IMG>, etc
22805 for(i = 0; i < currentElement.attributes.length;i++) {
22807 var aname = currentElement.attributes.item(i).name;
22808 if (!currentElement.attributes.item(i).value.length) {
22811 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22814 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22823 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22826 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22831 // Traverse the tree
22833 var currentElementChild = currentElement.childNodes.item(i);
22834 var allText = true;
22835 var innerHTML = '';
22837 while (currentElementChild) {
22838 // Formatting code (indent the tree so it looks nice on the screen)
22839 var nopad = nopadtext;
22840 if (lastnode == 'SPAN') {
22844 if (currentElementChild.nodeName == '#text') {
22845 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22846 toadd = nopadtext ? toadd : toadd.trim();
22847 if (!nopad && toadd.length > 80) {
22848 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22850 innerHTML += toadd;
22853 currentElementChild = currentElement.childNodes.item(i);
22859 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22861 // Recursively traverse the tree structure of the child node
22862 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22863 lastnode = currentElementChild.nodeName;
22865 currentElementChild=currentElement.childNodes.item(i);
22871 // The remaining code is mostly for formatting the tree
22872 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22877 ret+= "</"+tagName+">";
22883 applyBlacklists : function()
22885 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22886 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22890 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22891 if (b.indexOf(tag) > -1) {
22894 this.white.push(tag);
22898 Roo.each(w, function(tag) {
22899 if (b.indexOf(tag) > -1) {
22902 if (this.white.indexOf(tag) > -1) {
22905 this.white.push(tag);
22910 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22911 if (w.indexOf(tag) > -1) {
22914 this.black.push(tag);
22918 Roo.each(b, function(tag) {
22919 if (w.indexOf(tag) > -1) {
22922 if (this.black.indexOf(tag) > -1) {
22925 this.black.push(tag);
22930 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22931 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22935 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22936 if (b.indexOf(tag) > -1) {
22939 this.cwhite.push(tag);
22943 Roo.each(w, function(tag) {
22944 if (b.indexOf(tag) > -1) {
22947 if (this.cwhite.indexOf(tag) > -1) {
22950 this.cwhite.push(tag);
22955 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22956 if (w.indexOf(tag) > -1) {
22959 this.cblack.push(tag);
22963 Roo.each(b, function(tag) {
22964 if (w.indexOf(tag) > -1) {
22967 if (this.cblack.indexOf(tag) > -1) {
22970 this.cblack.push(tag);
22975 setStylesheets : function(stylesheets)
22977 if(typeof(stylesheets) == 'string'){
22978 Roo.get(this.iframe.contentDocument.head).createChild({
22980 rel : 'stylesheet',
22989 Roo.each(stylesheets, function(s) {
22994 Roo.get(_this.iframe.contentDocument.head).createChild({
22996 rel : 'stylesheet',
23005 removeStylesheets : function()
23009 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23014 setStyle : function(style)
23016 Roo.get(this.iframe.contentDocument.head).createChild({
23025 // hide stuff that is not compatible
23039 * @event specialkey
23043 * @cfg {String} fieldClass @hide
23046 * @cfg {String} focusClass @hide
23049 * @cfg {String} autoCreate @hide
23052 * @cfg {String} inputType @hide
23055 * @cfg {String} invalidClass @hide
23058 * @cfg {String} invalidText @hide
23061 * @cfg {String} msgFx @hide
23064 * @cfg {String} validateOnBlur @hide
23068 Roo.HtmlEditorCore.white = [
23069 'area', 'br', 'img', 'input', 'hr', 'wbr',
23071 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23072 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23073 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23074 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23075 'table', 'ul', 'xmp',
23077 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23080 'dir', 'menu', 'ol', 'ul', 'dl',
23086 Roo.HtmlEditorCore.black = [
23087 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23089 'base', 'basefont', 'bgsound', 'blink', 'body',
23090 'frame', 'frameset', 'head', 'html', 'ilayer',
23091 'iframe', 'layer', 'link', 'meta', 'object',
23092 'script', 'style' ,'title', 'xml' // clean later..
23094 Roo.HtmlEditorCore.clean = [
23095 'script', 'style', 'title', 'xml'
23097 Roo.HtmlEditorCore.remove = [
23102 Roo.HtmlEditorCore.ablack = [
23106 Roo.HtmlEditorCore.aclean = [
23107 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23111 Roo.HtmlEditorCore.pwhite= [
23112 'http', 'https', 'mailto'
23115 // white listed style attributes.
23116 Roo.HtmlEditorCore.cwhite= [
23117 // 'text-align', /// default is to allow most things..
23123 // black listed style attributes.
23124 Roo.HtmlEditorCore.cblack= [
23125 // 'font-size' -- this can be set by the project
23129 Roo.HtmlEditorCore.swapCodes =[
23148 * @class Roo.bootstrap.HtmlEditor
23149 * @extends Roo.bootstrap.TextArea
23150 * Bootstrap HtmlEditor class
23153 * Create a new HtmlEditor
23154 * @param {Object} config The config object
23157 Roo.bootstrap.HtmlEditor = function(config){
23158 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23159 if (!this.toolbars) {
23160 this.toolbars = [];
23163 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23166 * @event initialize
23167 * Fires when the editor is fully initialized (including the iframe)
23168 * @param {HtmlEditor} this
23173 * Fires when the editor is first receives the focus. Any insertion must wait
23174 * until after this event.
23175 * @param {HtmlEditor} this
23179 * @event beforesync
23180 * Fires before the textarea is updated with content from the editor iframe. Return false
23181 * to cancel the sync.
23182 * @param {HtmlEditor} this
23183 * @param {String} html
23187 * @event beforepush
23188 * Fires before the iframe editor is updated with content from the textarea. Return false
23189 * to cancel the push.
23190 * @param {HtmlEditor} this
23191 * @param {String} html
23196 * Fires when the textarea is updated with content from the editor iframe.
23197 * @param {HtmlEditor} this
23198 * @param {String} html
23203 * Fires when the iframe editor is updated with content from the textarea.
23204 * @param {HtmlEditor} this
23205 * @param {String} html
23209 * @event editmodechange
23210 * Fires when the editor switches edit modes
23211 * @param {HtmlEditor} this
23212 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23214 editmodechange: true,
23216 * @event editorevent
23217 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23218 * @param {HtmlEditor} this
23222 * @event firstfocus
23223 * Fires when on first focus - needed by toolbars..
23224 * @param {HtmlEditor} this
23229 * Auto save the htmlEditor value as a file into Events
23230 * @param {HtmlEditor} this
23234 * @event savedpreview
23235 * preview the saved version of htmlEditor
23236 * @param {HtmlEditor} this
23243 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23247 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23252 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23257 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23262 * @cfg {Number} height (in pixels)
23266 * @cfg {Number} width (in pixels)
23271 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23274 stylesheets: false,
23279 // private properties
23280 validationEvent : false,
23282 initialized : false,
23285 onFocus : Roo.emptyFn,
23287 hideMode:'offsets',
23289 tbContainer : false,
23293 toolbarContainer :function() {
23294 return this.wrap.select('.x-html-editor-tb',true).first();
23298 * Protected method that will not generally be called directly. It
23299 * is called when the editor creates its toolbar. Override this method if you need to
23300 * add custom toolbar buttons.
23301 * @param {HtmlEditor} editor
23303 createToolbar : function(){
23304 Roo.log('renewing');
23305 Roo.log("create toolbars");
23307 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23308 this.toolbars[0].render(this.toolbarContainer());
23312 // if (!editor.toolbars || !editor.toolbars.length) {
23313 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23316 // for (var i =0 ; i < editor.toolbars.length;i++) {
23317 // editor.toolbars[i] = Roo.factory(
23318 // typeof(editor.toolbars[i]) == 'string' ?
23319 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23320 // Roo.bootstrap.HtmlEditor);
23321 // editor.toolbars[i].init(editor);
23327 onRender : function(ct, position)
23329 // Roo.log("Call onRender: " + this.xtype);
23331 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23333 this.wrap = this.inputEl().wrap({
23334 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23337 this.editorcore.onRender(ct, position);
23339 if (this.resizable) {
23340 this.resizeEl = new Roo.Resizable(this.wrap, {
23344 minHeight : this.height,
23345 height: this.height,
23346 handles : this.resizable,
23349 resize : function(r, w, h) {
23350 _t.onResize(w,h); // -something
23356 this.createToolbar(this);
23359 if(!this.width && this.resizable){
23360 this.setSize(this.wrap.getSize());
23362 if (this.resizeEl) {
23363 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23364 // should trigger onReize..
23370 onResize : function(w, h)
23372 Roo.log('resize: ' +w + ',' + h );
23373 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23377 if(this.inputEl() ){
23378 if(typeof w == 'number'){
23379 var aw = w - this.wrap.getFrameWidth('lr');
23380 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23383 if(typeof h == 'number'){
23384 var tbh = -11; // fixme it needs to tool bar size!
23385 for (var i =0; i < this.toolbars.length;i++) {
23386 // fixme - ask toolbars for heights?
23387 tbh += this.toolbars[i].el.getHeight();
23388 //if (this.toolbars[i].footer) {
23389 // tbh += this.toolbars[i].footer.el.getHeight();
23397 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23398 ah -= 5; // knock a few pixes off for look..
23399 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23403 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23404 this.editorcore.onResize(ew,eh);
23409 * Toggles the editor between standard and source edit mode.
23410 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23412 toggleSourceEdit : function(sourceEditMode)
23414 this.editorcore.toggleSourceEdit(sourceEditMode);
23416 if(this.editorcore.sourceEditMode){
23417 Roo.log('editor - showing textarea');
23420 // Roo.log(this.syncValue());
23422 this.inputEl().removeClass(['hide', 'x-hidden']);
23423 this.inputEl().dom.removeAttribute('tabIndex');
23424 this.inputEl().focus();
23426 Roo.log('editor - hiding textarea');
23428 // Roo.log(this.pushValue());
23431 this.inputEl().addClass(['hide', 'x-hidden']);
23432 this.inputEl().dom.setAttribute('tabIndex', -1);
23433 //this.deferFocus();
23436 if(this.resizable){
23437 this.setSize(this.wrap.getSize());
23440 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23443 // private (for BoxComponent)
23444 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23446 // private (for BoxComponent)
23447 getResizeEl : function(){
23451 // private (for BoxComponent)
23452 getPositionEl : function(){
23457 initEvents : function(){
23458 this.originalValue = this.getValue();
23462 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23465 // markInvalid : Roo.emptyFn,
23467 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23470 // clearInvalid : Roo.emptyFn,
23472 setValue : function(v){
23473 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23474 this.editorcore.pushValue();
23479 deferFocus : function(){
23480 this.focus.defer(10, this);
23484 focus : function(){
23485 this.editorcore.focus();
23491 onDestroy : function(){
23497 for (var i =0; i < this.toolbars.length;i++) {
23498 // fixme - ask toolbars for heights?
23499 this.toolbars[i].onDestroy();
23502 this.wrap.dom.innerHTML = '';
23503 this.wrap.remove();
23508 onFirstFocus : function(){
23509 //Roo.log("onFirstFocus");
23510 this.editorcore.onFirstFocus();
23511 for (var i =0; i < this.toolbars.length;i++) {
23512 this.toolbars[i].onFirstFocus();
23518 syncValue : function()
23520 this.editorcore.syncValue();
23523 pushValue : function()
23525 this.editorcore.pushValue();
23529 // hide stuff that is not compatible
23543 * @event specialkey
23547 * @cfg {String} fieldClass @hide
23550 * @cfg {String} focusClass @hide
23553 * @cfg {String} autoCreate @hide
23556 * @cfg {String} inputType @hide
23559 * @cfg {String} invalidClass @hide
23562 * @cfg {String} invalidText @hide
23565 * @cfg {String} msgFx @hide
23568 * @cfg {String} validateOnBlur @hide
23577 Roo.namespace('Roo.bootstrap.htmleditor');
23579 * @class Roo.bootstrap.HtmlEditorToolbar1
23584 new Roo.bootstrap.HtmlEditor({
23587 new Roo.bootstrap.HtmlEditorToolbar1({
23588 disable : { fonts: 1 , format: 1, ..., ... , ...],
23594 * @cfg {Object} disable List of elements to disable..
23595 * @cfg {Array} btns List of additional buttons.
23599 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23602 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23605 Roo.apply(this, config);
23607 // default disabled, based on 'good practice'..
23608 this.disable = this.disable || {};
23609 Roo.applyIf(this.disable, {
23612 specialElements : true
23614 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23616 this.editor = config.editor;
23617 this.editorcore = config.editor.editorcore;
23619 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23621 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23622 // dont call parent... till later.
23624 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23629 editorcore : false,
23634 "h1","h2","h3","h4","h5","h6",
23636 "abbr", "acronym", "address", "cite", "samp", "var",
23640 onRender : function(ct, position)
23642 // Roo.log("Call onRender: " + this.xtype);
23644 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23646 this.el.dom.style.marginBottom = '0';
23648 var editorcore = this.editorcore;
23649 var editor= this.editor;
23652 var btn = function(id,cmd , toggle, handler, html){
23654 var event = toggle ? 'toggle' : 'click';
23659 xns: Roo.bootstrap,
23662 enableToggle:toggle !== false,
23664 pressed : toggle ? false : null,
23667 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23668 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23674 // var cb_box = function...
23679 xns: Roo.bootstrap,
23680 glyphicon : 'font',
23684 xns: Roo.bootstrap,
23688 Roo.each(this.formats, function(f) {
23689 style.menu.items.push({
23691 xns: Roo.bootstrap,
23692 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23697 editorcore.insertTag(this.tagname);
23704 children.push(style);
23706 btn('bold',false,true);
23707 btn('italic',false,true);
23708 btn('align-left', 'justifyleft',true);
23709 btn('align-center', 'justifycenter',true);
23710 btn('align-right' , 'justifyright',true);
23711 btn('link', false, false, function(btn) {
23712 //Roo.log("create link?");
23713 var url = prompt(this.createLinkText, this.defaultLinkValue);
23714 if(url && url != 'http:/'+'/'){
23715 this.editorcore.relayCmd('createlink', url);
23718 btn('list','insertunorderedlist',true);
23719 btn('pencil', false,true, function(btn){
23721 this.toggleSourceEdit(btn.pressed);
23724 if (this.editor.btns.length > 0) {
23725 for (var i = 0; i<this.editor.btns.length; i++) {
23726 children.push(this.editor.btns[i]);
23734 xns: Roo.bootstrap,
23739 xns: Roo.bootstrap,
23744 cog.menu.items.push({
23746 xns: Roo.bootstrap,
23747 html : Clean styles,
23752 editorcore.insertTag(this.tagname);
23761 this.xtype = 'NavSimplebar';
23763 for(var i=0;i< children.length;i++) {
23765 this.buttons.add(this.addxtypeChild(children[i]));
23769 editor.on('editorevent', this.updateToolbar, this);
23771 onBtnClick : function(id)
23773 this.editorcore.relayCmd(id);
23774 this.editorcore.focus();
23778 * Protected method that will not generally be called directly. It triggers
23779 * a toolbar update by reading the markup state of the current selection in the editor.
23781 updateToolbar: function(){
23783 if(!this.editorcore.activated){
23784 this.editor.onFirstFocus(); // is this neeed?
23788 var btns = this.buttons;
23789 var doc = this.editorcore.doc;
23790 btns.get('bold').setActive(doc.queryCommandState('bold'));
23791 btns.get('italic').setActive(doc.queryCommandState('italic'));
23792 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23794 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23795 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23796 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23798 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23799 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23802 var ans = this.editorcore.getAllAncestors();
23803 if (this.formatCombo) {
23806 var store = this.formatCombo.store;
23807 this.formatCombo.setValue("");
23808 for (var i =0; i < ans.length;i++) {
23809 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23811 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23819 // hides menus... - so this cant be on a menu...
23820 Roo.bootstrap.MenuMgr.hideAll();
23822 Roo.bootstrap.MenuMgr.hideAll();
23823 //this.editorsyncValue();
23825 onFirstFocus: function() {
23826 this.buttons.each(function(item){
23830 toggleSourceEdit : function(sourceEditMode){
23833 if(sourceEditMode){
23834 Roo.log("disabling buttons");
23835 this.buttons.each( function(item){
23836 if(item.cmd != 'pencil'){
23842 Roo.log("enabling buttons");
23843 if(this.editorcore.initialized){
23844 this.buttons.each( function(item){
23850 Roo.log("calling toggole on editor");
23851 // tell the editor that it's been pressed..
23852 this.editor.toggleSourceEdit(sourceEditMode);
23862 * @class Roo.bootstrap.Table.AbstractSelectionModel
23863 * @extends Roo.util.Observable
23864 * Abstract base class for grid SelectionModels. It provides the interface that should be
23865 * implemented by descendant classes. This class should not be directly instantiated.
23868 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23869 this.locked = false;
23870 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23874 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23875 /** @ignore Called by the grid automatically. Do not call directly. */
23876 init : function(grid){
23882 * Locks the selections.
23885 this.locked = true;
23889 * Unlocks the selections.
23891 unlock : function(){
23892 this.locked = false;
23896 * Returns true if the selections are locked.
23897 * @return {Boolean}
23899 isLocked : function(){
23900 return this.locked;
23904 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23905 * @class Roo.bootstrap.Table.RowSelectionModel
23906 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23907 * It supports multiple selections and keyboard selection/navigation.
23909 * @param {Object} config
23912 Roo.bootstrap.Table.RowSelectionModel = function(config){
23913 Roo.apply(this, config);
23914 this.selections = new Roo.util.MixedCollection(false, function(o){
23919 this.lastActive = false;
23923 * @event selectionchange
23924 * Fires when the selection changes
23925 * @param {SelectionModel} this
23927 "selectionchange" : true,
23929 * @event afterselectionchange
23930 * Fires after the selection changes (eg. by key press or clicking)
23931 * @param {SelectionModel} this
23933 "afterselectionchange" : true,
23935 * @event beforerowselect
23936 * Fires when a row is selected being selected, return false to cancel.
23937 * @param {SelectionModel} this
23938 * @param {Number} rowIndex The selected index
23939 * @param {Boolean} keepExisting False if other selections will be cleared
23941 "beforerowselect" : true,
23944 * Fires when a row is selected.
23945 * @param {SelectionModel} this
23946 * @param {Number} rowIndex The selected index
23947 * @param {Roo.data.Record} r The record
23949 "rowselect" : true,
23951 * @event rowdeselect
23952 * Fires when a row is deselected.
23953 * @param {SelectionModel} this
23954 * @param {Number} rowIndex The selected index
23956 "rowdeselect" : true
23958 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23959 this.locked = false;
23962 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23964 * @cfg {Boolean} singleSelect
23965 * True to allow selection of only one row at a time (defaults to false)
23967 singleSelect : false,
23970 initEvents : function()
23973 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23974 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23975 //}else{ // allow click to work like normal
23976 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23978 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23979 this.grid.on("rowclick", this.handleMouseDown, this);
23981 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23982 "up" : function(e){
23984 this.selectPrevious(e.shiftKey);
23985 }else if(this.last !== false && this.lastActive !== false){
23986 var last = this.last;
23987 this.selectRange(this.last, this.lastActive-1);
23988 this.grid.getView().focusRow(this.lastActive);
23989 if(last !== false){
23993 this.selectFirstRow();
23995 this.fireEvent("afterselectionchange", this);
23997 "down" : function(e){
23999 this.selectNext(e.shiftKey);
24000 }else if(this.last !== false && this.lastActive !== false){
24001 var last = this.last;
24002 this.selectRange(this.last, this.lastActive+1);
24003 this.grid.getView().focusRow(this.lastActive);
24004 if(last !== false){
24008 this.selectFirstRow();
24010 this.fireEvent("afterselectionchange", this);
24014 this.grid.store.on('load', function(){
24015 this.selections.clear();
24018 var view = this.grid.view;
24019 view.on("refresh", this.onRefresh, this);
24020 view.on("rowupdated", this.onRowUpdated, this);
24021 view.on("rowremoved", this.onRemove, this);
24026 onRefresh : function()
24028 var ds = this.grid.store, i, v = this.grid.view;
24029 var s = this.selections;
24030 s.each(function(r){
24031 if((i = ds.indexOfId(r.id)) != -1){
24040 onRemove : function(v, index, r){
24041 this.selections.remove(r);
24045 onRowUpdated : function(v, index, r){
24046 if(this.isSelected(r)){
24047 v.onRowSelect(index);
24053 * @param {Array} records The records to select
24054 * @param {Boolean} keepExisting (optional) True to keep existing selections
24056 selectRecords : function(records, keepExisting)
24059 this.clearSelections();
24061 var ds = this.grid.store;
24062 for(var i = 0, len = records.length; i < len; i++){
24063 this.selectRow(ds.indexOf(records[i]), true);
24068 * Gets the number of selected rows.
24071 getCount : function(){
24072 return this.selections.length;
24076 * Selects the first row in the grid.
24078 selectFirstRow : function(){
24083 * Select the last row.
24084 * @param {Boolean} keepExisting (optional) True to keep existing selections
24086 selectLastRow : function(keepExisting){
24087 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24088 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24092 * Selects the row immediately following the last selected row.
24093 * @param {Boolean} keepExisting (optional) True to keep existing selections
24095 selectNext : function(keepExisting)
24097 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24098 this.selectRow(this.last+1, keepExisting);
24099 this.grid.getView().focusRow(this.last);
24104 * Selects the row that precedes the last selected row.
24105 * @param {Boolean} keepExisting (optional) True to keep existing selections
24107 selectPrevious : function(keepExisting){
24109 this.selectRow(this.last-1, keepExisting);
24110 this.grid.getView().focusRow(this.last);
24115 * Returns the selected records
24116 * @return {Array} Array of selected records
24118 getSelections : function(){
24119 return [].concat(this.selections.items);
24123 * Returns the first selected record.
24126 getSelected : function(){
24127 return this.selections.itemAt(0);
24132 * Clears all selections.
24134 clearSelections : function(fast)
24140 var ds = this.grid.store;
24141 var s = this.selections;
24142 s.each(function(r){
24143 this.deselectRow(ds.indexOfId(r.id));
24147 this.selections.clear();
24154 * Selects all rows.
24156 selectAll : function(){
24160 this.selections.clear();
24161 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24162 this.selectRow(i, true);
24167 * Returns True if there is a selection.
24168 * @return {Boolean}
24170 hasSelection : function(){
24171 return this.selections.length > 0;
24175 * Returns True if the specified row is selected.
24176 * @param {Number/Record} record The record or index of the record to check
24177 * @return {Boolean}
24179 isSelected : function(index){
24180 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24181 return (r && this.selections.key(r.id) ? true : false);
24185 * Returns True if the specified record id is selected.
24186 * @param {String} id The id of record to check
24187 * @return {Boolean}
24189 isIdSelected : function(id){
24190 return (this.selections.key(id) ? true : false);
24195 handleMouseDBClick : function(e, t){
24199 handleMouseDown : function(e, t)
24201 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24202 if(this.isLocked() || rowIndex < 0 ){
24205 if(e.shiftKey && this.last !== false){
24206 var last = this.last;
24207 this.selectRange(last, rowIndex, e.ctrlKey);
24208 this.last = last; // reset the last
24212 var isSelected = this.isSelected(rowIndex);
24213 //Roo.log("select row:" + rowIndex);
24215 this.deselectRow(rowIndex);
24217 this.selectRow(rowIndex, true);
24221 if(e.button !== 0 && isSelected){
24222 alert('rowIndex 2: ' + rowIndex);
24223 view.focusRow(rowIndex);
24224 }else if(e.ctrlKey && isSelected){
24225 this.deselectRow(rowIndex);
24226 }else if(!isSelected){
24227 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24228 view.focusRow(rowIndex);
24232 this.fireEvent("afterselectionchange", this);
24235 handleDragableRowClick : function(grid, rowIndex, e)
24237 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24238 this.selectRow(rowIndex, false);
24239 grid.view.focusRow(rowIndex);
24240 this.fireEvent("afterselectionchange", this);
24245 * Selects multiple rows.
24246 * @param {Array} rows Array of the indexes of the row to select
24247 * @param {Boolean} keepExisting (optional) True to keep existing selections
24249 selectRows : function(rows, keepExisting){
24251 this.clearSelections();
24253 for(var i = 0, len = rows.length; i < len; i++){
24254 this.selectRow(rows[i], true);
24259 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24260 * @param {Number} startRow The index of the first row in the range
24261 * @param {Number} endRow The index of the last row in the range
24262 * @param {Boolean} keepExisting (optional) True to retain existing selections
24264 selectRange : function(startRow, endRow, keepExisting){
24269 this.clearSelections();
24271 if(startRow <= endRow){
24272 for(var i = startRow; i <= endRow; i++){
24273 this.selectRow(i, true);
24276 for(var i = startRow; i >= endRow; i--){
24277 this.selectRow(i, true);
24283 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24284 * @param {Number} startRow The index of the first row in the range
24285 * @param {Number} endRow The index of the last row in the range
24287 deselectRange : function(startRow, endRow, preventViewNotify){
24291 for(var i = startRow; i <= endRow; i++){
24292 this.deselectRow(i, preventViewNotify);
24298 * @param {Number} row The index of the row to select
24299 * @param {Boolean} keepExisting (optional) True to keep existing selections
24301 selectRow : function(index, keepExisting, preventViewNotify)
24303 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24306 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24307 if(!keepExisting || this.singleSelect){
24308 this.clearSelections();
24311 var r = this.grid.store.getAt(index);
24312 //console.log('selectRow - record id :' + r.id);
24314 this.selections.add(r);
24315 this.last = this.lastActive = index;
24316 if(!preventViewNotify){
24317 var proxy = new Roo.Element(
24318 this.grid.getRowDom(index)
24320 proxy.addClass('bg-info info');
24322 this.fireEvent("rowselect", this, index, r);
24323 this.fireEvent("selectionchange", this);
24329 * @param {Number} row The index of the row to deselect
24331 deselectRow : function(index, preventViewNotify)
24336 if(this.last == index){
24339 if(this.lastActive == index){
24340 this.lastActive = false;
24343 var r = this.grid.store.getAt(index);
24348 this.selections.remove(r);
24349 //.console.log('deselectRow - record id :' + r.id);
24350 if(!preventViewNotify){
24352 var proxy = new Roo.Element(
24353 this.grid.getRowDom(index)
24355 proxy.removeClass('bg-info info');
24357 this.fireEvent("rowdeselect", this, index);
24358 this.fireEvent("selectionchange", this);
24362 restoreLast : function(){
24364 this.last = this._last;
24369 acceptsNav : function(row, col, cm){
24370 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24374 onEditorKey : function(field, e){
24375 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24380 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24382 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24384 }else if(k == e.ENTER && !e.ctrlKey){
24388 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24390 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24392 }else if(k == e.ESC){
24396 g.startEditing(newCell[0], newCell[1]);
24402 * Ext JS Library 1.1.1
24403 * Copyright(c) 2006-2007, Ext JS, LLC.
24405 * Originally Released Under LGPL - original licence link has changed is not relivant.
24408 * <script type="text/javascript">
24412 * @class Roo.bootstrap.PagingToolbar
24413 * @extends Roo.bootstrap.NavSimplebar
24414 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24416 * Create a new PagingToolbar
24417 * @param {Object} config The config object
24418 * @param {Roo.data.Store} store
24420 Roo.bootstrap.PagingToolbar = function(config)
24422 // old args format still supported... - xtype is prefered..
24423 // created from xtype...
24425 this.ds = config.dataSource;
24427 if (config.store && !this.ds) {
24428 this.store= Roo.factory(config.store, Roo.data);
24429 this.ds = this.store;
24430 this.ds.xmodule = this.xmodule || false;
24433 this.toolbarItems = [];
24434 if (config.items) {
24435 this.toolbarItems = config.items;
24438 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24443 this.bind(this.ds);
24446 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24450 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24452 * @cfg {Roo.data.Store} dataSource
24453 * The underlying data store providing the paged data
24456 * @cfg {String/HTMLElement/Element} container
24457 * container The id or element that will contain the toolbar
24460 * @cfg {Boolean} displayInfo
24461 * True to display the displayMsg (defaults to false)
24464 * @cfg {Number} pageSize
24465 * The number of records to display per page (defaults to 20)
24469 * @cfg {String} displayMsg
24470 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24472 displayMsg : 'Displaying {0} - {1} of {2}',
24474 * @cfg {String} emptyMsg
24475 * The message to display when no records are found (defaults to "No data to display")
24477 emptyMsg : 'No data to display',
24479 * Customizable piece of the default paging text (defaults to "Page")
24482 beforePageText : "Page",
24484 * Customizable piece of the default paging text (defaults to "of %0")
24487 afterPageText : "of {0}",
24489 * Customizable piece of the default paging text (defaults to "First Page")
24492 firstText : "First Page",
24494 * Customizable piece of the default paging text (defaults to "Previous Page")
24497 prevText : "Previous Page",
24499 * Customizable piece of the default paging text (defaults to "Next Page")
24502 nextText : "Next Page",
24504 * Customizable piece of the default paging text (defaults to "Last Page")
24507 lastText : "Last Page",
24509 * Customizable piece of the default paging text (defaults to "Refresh")
24512 refreshText : "Refresh",
24516 onRender : function(ct, position)
24518 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24519 this.navgroup.parentId = this.id;
24520 this.navgroup.onRender(this.el, null);
24521 // add the buttons to the navgroup
24523 if(this.displayInfo){
24524 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24525 this.displayEl = this.el.select('.x-paging-info', true).first();
24526 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24527 // this.displayEl = navel.el.select('span',true).first();
24533 Roo.each(_this.buttons, function(e){ // this might need to use render????
24534 Roo.factory(e).render(_this.el);
24538 Roo.each(_this.toolbarItems, function(e) {
24539 _this.navgroup.addItem(e);
24543 this.first = this.navgroup.addItem({
24544 tooltip: this.firstText,
24546 icon : 'fa fa-backward',
24548 preventDefault: true,
24549 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24552 this.prev = this.navgroup.addItem({
24553 tooltip: this.prevText,
24555 icon : 'fa fa-step-backward',
24557 preventDefault: true,
24558 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24560 //this.addSeparator();
24563 var field = this.navgroup.addItem( {
24565 cls : 'x-paging-position',
24567 html : this.beforePageText +
24568 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24569 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24572 this.field = field.el.select('input', true).first();
24573 this.field.on("keydown", this.onPagingKeydown, this);
24574 this.field.on("focus", function(){this.dom.select();});
24577 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24578 //this.field.setHeight(18);
24579 //this.addSeparator();
24580 this.next = this.navgroup.addItem({
24581 tooltip: this.nextText,
24583 html : ' <i class="fa fa-step-forward">',
24585 preventDefault: true,
24586 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24588 this.last = this.navgroup.addItem({
24589 tooltip: this.lastText,
24590 icon : 'fa fa-forward',
24593 preventDefault: true,
24594 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24596 //this.addSeparator();
24597 this.loading = this.navgroup.addItem({
24598 tooltip: this.refreshText,
24599 icon: 'fa fa-refresh',
24600 preventDefault: true,
24601 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24607 updateInfo : function(){
24608 if(this.displayEl){
24609 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24610 var msg = count == 0 ?
24614 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24616 this.displayEl.update(msg);
24621 onLoad : function(ds, r, o)
24623 this.cursor = o.params.start ? o.params.start : 0;
24625 var d = this.getPageData(),
24630 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24631 this.field.dom.value = ap;
24632 this.first.setDisabled(ap == 1);
24633 this.prev.setDisabled(ap == 1);
24634 this.next.setDisabled(ap == ps);
24635 this.last.setDisabled(ap == ps);
24636 this.loading.enable();
24641 getPageData : function(){
24642 var total = this.ds.getTotalCount();
24645 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24646 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24651 onLoadError : function(){
24652 this.loading.enable();
24656 onPagingKeydown : function(e){
24657 var k = e.getKey();
24658 var d = this.getPageData();
24660 var v = this.field.dom.value, pageNum;
24661 if(!v || isNaN(pageNum = parseInt(v, 10))){
24662 this.field.dom.value = d.activePage;
24665 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24666 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24669 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))
24671 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24672 this.field.dom.value = pageNum;
24673 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24676 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24678 var v = this.field.dom.value, pageNum;
24679 var increment = (e.shiftKey) ? 10 : 1;
24680 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24683 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24684 this.field.dom.value = d.activePage;
24687 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24689 this.field.dom.value = parseInt(v, 10) + increment;
24690 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24691 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24698 beforeLoad : function(){
24700 this.loading.disable();
24705 onClick : function(which){
24714 ds.load({params:{start: 0, limit: this.pageSize}});
24717 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24720 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24723 var total = ds.getTotalCount();
24724 var extra = total % this.pageSize;
24725 var lastStart = extra ? (total - extra) : total-this.pageSize;
24726 ds.load({params:{start: lastStart, limit: this.pageSize}});
24729 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24735 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24736 * @param {Roo.data.Store} store The data store to unbind
24738 unbind : function(ds){
24739 ds.un("beforeload", this.beforeLoad, this);
24740 ds.un("load", this.onLoad, this);
24741 ds.un("loadexception", this.onLoadError, this);
24742 ds.un("remove", this.updateInfo, this);
24743 ds.un("add", this.updateInfo, this);
24744 this.ds = undefined;
24748 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24749 * @param {Roo.data.Store} store The data store to bind
24751 bind : function(ds){
24752 ds.on("beforeload", this.beforeLoad, this);
24753 ds.on("load", this.onLoad, this);
24754 ds.on("loadexception", this.onLoadError, this);
24755 ds.on("remove", this.updateInfo, this);
24756 ds.on("add", this.updateInfo, this);
24767 * @class Roo.bootstrap.MessageBar
24768 * @extends Roo.bootstrap.Component
24769 * Bootstrap MessageBar class
24770 * @cfg {String} html contents of the MessageBar
24771 * @cfg {String} weight (info | success | warning | danger) default info
24772 * @cfg {String} beforeClass insert the bar before the given class
24773 * @cfg {Boolean} closable (true | false) default false
24774 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24777 * Create a new Element
24778 * @param {Object} config The config object
24781 Roo.bootstrap.MessageBar = function(config){
24782 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24785 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24791 beforeClass: 'bootstrap-sticky-wrap',
24793 getAutoCreate : function(){
24797 cls: 'alert alert-dismissable alert-' + this.weight,
24802 html: this.html || ''
24808 cfg.cls += ' alert-messages-fixed';
24822 onRender : function(ct, position)
24824 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24827 var cfg = Roo.apply({}, this.getAutoCreate());
24831 cfg.cls += ' ' + this.cls;
24834 cfg.style = this.style;
24836 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24838 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24841 this.el.select('>button.close').on('click', this.hide, this);
24847 if (!this.rendered) {
24853 this.fireEvent('show', this);
24859 if (!this.rendered) {
24865 this.fireEvent('hide', this);
24868 update : function()
24870 // var e = this.el.dom.firstChild;
24872 // if(this.closable){
24873 // e = e.nextSibling;
24876 // e.data = this.html || '';
24878 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24894 * @class Roo.bootstrap.Graph
24895 * @extends Roo.bootstrap.Component
24896 * Bootstrap Graph class
24900 @cfg {String} graphtype bar | vbar | pie
24901 @cfg {number} g_x coodinator | centre x (pie)
24902 @cfg {number} g_y coodinator | centre y (pie)
24903 @cfg {number} g_r radius (pie)
24904 @cfg {number} g_height height of the chart (respected by all elements in the set)
24905 @cfg {number} g_width width of the chart (respected by all elements in the set)
24906 @cfg {Object} title The title of the chart
24909 -opts (object) options for the chart
24911 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24912 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24914 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.
24915 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24917 o stretch (boolean)
24919 -opts (object) options for the pie
24922 o startAngle (number)
24923 o endAngle (number)
24927 * Create a new Input
24928 * @param {Object} config The config object
24931 Roo.bootstrap.Graph = function(config){
24932 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24938 * The img click event for the img.
24939 * @param {Roo.EventObject} e
24945 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24956 //g_colors: this.colors,
24963 getAutoCreate : function(){
24974 onRender : function(ct,position){
24977 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24979 if (typeof(Raphael) == 'undefined') {
24980 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24984 this.raphael = Raphael(this.el.dom);
24986 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24987 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24988 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24989 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24991 r.text(160, 10, "Single Series Chart").attr(txtattr);
24992 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24993 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24994 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24996 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24997 r.barchart(330, 10, 300, 220, data1);
24998 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24999 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25002 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25003 // r.barchart(30, 30, 560, 250, xdata, {
25004 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25005 // axis : "0 0 1 1",
25006 // axisxlabels : xdata
25007 // //yvalues : cols,
25010 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25012 // this.load(null,xdata,{
25013 // axis : "0 0 1 1",
25014 // axisxlabels : xdata
25019 load : function(graphtype,xdata,opts)
25021 this.raphael.clear();
25023 graphtype = this.graphtype;
25028 var r = this.raphael,
25029 fin = function () {
25030 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25032 fout = function () {
25033 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25035 pfin = function() {
25036 this.sector.stop();
25037 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25040 this.label[0].stop();
25041 this.label[0].attr({ r: 7.5 });
25042 this.label[1].attr({ "font-weight": 800 });
25045 pfout = function() {
25046 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25049 this.label[0].animate({ r: 5 }, 500, "bounce");
25050 this.label[1].attr({ "font-weight": 400 });
25056 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25059 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25062 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25063 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25065 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25072 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25077 setTitle: function(o)
25082 initEvents: function() {
25085 this.el.on('click', this.onClick, this);
25089 onClick : function(e)
25091 Roo.log('img onclick');
25092 this.fireEvent('click', this, e);
25104 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25107 * @class Roo.bootstrap.dash.NumberBox
25108 * @extends Roo.bootstrap.Component
25109 * Bootstrap NumberBox class
25110 * @cfg {String} headline Box headline
25111 * @cfg {String} content Box content
25112 * @cfg {String} icon Box icon
25113 * @cfg {String} footer Footer text
25114 * @cfg {String} fhref Footer href
25117 * Create a new NumberBox
25118 * @param {Object} config The config object
25122 Roo.bootstrap.dash.NumberBox = function(config){
25123 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25127 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25136 getAutoCreate : function(){
25140 cls : 'small-box ',
25148 cls : 'roo-headline',
25149 html : this.headline
25153 cls : 'roo-content',
25154 html : this.content
25168 cls : 'ion ' + this.icon
25177 cls : 'small-box-footer',
25178 href : this.fhref || '#',
25182 cfg.cn.push(footer);
25189 onRender : function(ct,position){
25190 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25197 setHeadline: function (value)
25199 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25202 setFooter: function (value, href)
25204 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25207 this.el.select('a.small-box-footer',true).first().attr('href', href);
25212 setContent: function (value)
25214 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25217 initEvents: function()
25231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25234 * @class Roo.bootstrap.dash.TabBox
25235 * @extends Roo.bootstrap.Component
25236 * Bootstrap TabBox class
25237 * @cfg {String} title Title of the TabBox
25238 * @cfg {String} icon Icon of the TabBox
25239 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25240 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25243 * Create a new TabBox
25244 * @param {Object} config The config object
25248 Roo.bootstrap.dash.TabBox = function(config){
25249 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25254 * When a pane is added
25255 * @param {Roo.bootstrap.dash.TabPane} pane
25259 * @event activatepane
25260 * When a pane is activated
25261 * @param {Roo.bootstrap.dash.TabPane} pane
25263 "activatepane" : true
25271 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25276 tabScrollable : false,
25278 getChildContainer : function()
25280 return this.el.select('.tab-content', true).first();
25283 getAutoCreate : function(){
25287 cls: 'pull-left header',
25295 cls: 'fa ' + this.icon
25301 cls: 'nav nav-tabs pull-right',
25307 if(this.tabScrollable){
25314 cls: 'nav nav-tabs pull-right',
25325 cls: 'nav-tabs-custom',
25330 cls: 'tab-content no-padding',
25338 initEvents : function()
25340 //Roo.log('add add pane handler');
25341 this.on('addpane', this.onAddPane, this);
25344 * Updates the box title
25345 * @param {String} html to set the title to.
25347 setTitle : function(value)
25349 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25351 onAddPane : function(pane)
25353 this.panes.push(pane);
25354 //Roo.log('addpane');
25356 // tabs are rendere left to right..
25357 if(!this.showtabs){
25361 var ctr = this.el.select('.nav-tabs', true).first();
25364 var existing = ctr.select('.nav-tab',true);
25365 var qty = existing.getCount();;
25368 var tab = ctr.createChild({
25370 cls : 'nav-tab' + (qty ? '' : ' active'),
25378 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25381 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25383 pane.el.addClass('active');
25388 onTabClick : function(ev,un,ob,pane)
25390 //Roo.log('tab - prev default');
25391 ev.preventDefault();
25394 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25395 pane.tab.addClass('active');
25396 //Roo.log(pane.title);
25397 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25398 // technically we should have a deactivate event.. but maybe add later.
25399 // and it should not de-activate the selected tab...
25400 this.fireEvent('activatepane', pane);
25401 pane.el.addClass('active');
25402 pane.fireEvent('activate');
25407 getActivePane : function()
25410 Roo.each(this.panes, function(p) {
25411 if(p.el.hasClass('active')){
25432 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25434 * @class Roo.bootstrap.TabPane
25435 * @extends Roo.bootstrap.Component
25436 * Bootstrap TabPane class
25437 * @cfg {Boolean} active (false | true) Default false
25438 * @cfg {String} title title of panel
25442 * Create a new TabPane
25443 * @param {Object} config The config object
25446 Roo.bootstrap.dash.TabPane = function(config){
25447 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25453 * When a pane is activated
25454 * @param {Roo.bootstrap.dash.TabPane} pane
25461 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25466 // the tabBox that this is attached to.
25469 getAutoCreate : function()
25477 cfg.cls += ' active';
25482 initEvents : function()
25484 //Roo.log('trigger add pane handler');
25485 this.parent().fireEvent('addpane', this)
25489 * Updates the tab title
25490 * @param {String} html to set the title to.
25492 setTitle: function(str)
25498 this.tab.select('a', true).first().dom.innerHTML = str;
25515 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25518 * @class Roo.bootstrap.menu.Menu
25519 * @extends Roo.bootstrap.Component
25520 * Bootstrap Menu class - container for Menu
25521 * @cfg {String} html Text of the menu
25522 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25523 * @cfg {String} icon Font awesome icon
25524 * @cfg {String} pos Menu align to (top | bottom) default bottom
25528 * Create a new Menu
25529 * @param {Object} config The config object
25533 Roo.bootstrap.menu.Menu = function(config){
25534 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25538 * @event beforeshow
25539 * Fires before this menu is displayed
25540 * @param {Roo.bootstrap.menu.Menu} this
25544 * @event beforehide
25545 * Fires before this menu is hidden
25546 * @param {Roo.bootstrap.menu.Menu} this
25551 * Fires after this menu is displayed
25552 * @param {Roo.bootstrap.menu.Menu} this
25557 * Fires after this menu is hidden
25558 * @param {Roo.bootstrap.menu.Menu} this
25563 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25564 * @param {Roo.bootstrap.menu.Menu} this
25565 * @param {Roo.EventObject} e
25572 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25576 weight : 'default',
25581 getChildContainer : function() {
25582 if(this.isSubMenu){
25586 return this.el.select('ul.dropdown-menu', true).first();
25589 getAutoCreate : function()
25594 cls : 'roo-menu-text',
25602 cls : 'fa ' + this.icon
25613 cls : 'dropdown-button btn btn-' + this.weight,
25618 cls : 'dropdown-toggle btn btn-' + this.weight,
25628 cls : 'dropdown-menu'
25634 if(this.pos == 'top'){
25635 cfg.cls += ' dropup';
25638 if(this.isSubMenu){
25641 cls : 'dropdown-menu'
25648 onRender : function(ct, position)
25650 this.isSubMenu = ct.hasClass('dropdown-submenu');
25652 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25655 initEvents : function()
25657 if(this.isSubMenu){
25661 this.hidden = true;
25663 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25664 this.triggerEl.on('click', this.onTriggerPress, this);
25666 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25667 this.buttonEl.on('click', this.onClick, this);
25673 if(this.isSubMenu){
25677 return this.el.select('ul.dropdown-menu', true).first();
25680 onClick : function(e)
25682 this.fireEvent("click", this, e);
25685 onTriggerPress : function(e)
25687 if (this.isVisible()) {
25694 isVisible : function(){
25695 return !this.hidden;
25700 this.fireEvent("beforeshow", this);
25702 this.hidden = false;
25703 this.el.addClass('open');
25705 Roo.get(document).on("mouseup", this.onMouseUp, this);
25707 this.fireEvent("show", this);
25714 this.fireEvent("beforehide", this);
25716 this.hidden = true;
25717 this.el.removeClass('open');
25719 Roo.get(document).un("mouseup", this.onMouseUp);
25721 this.fireEvent("hide", this);
25724 onMouseUp : function()
25738 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25741 * @class Roo.bootstrap.menu.Item
25742 * @extends Roo.bootstrap.Component
25743 * Bootstrap MenuItem class
25744 * @cfg {Boolean} submenu (true | false) default false
25745 * @cfg {String} html text of the item
25746 * @cfg {String} href the link
25747 * @cfg {Boolean} disable (true | false) default false
25748 * @cfg {Boolean} preventDefault (true | false) default true
25749 * @cfg {String} icon Font awesome icon
25750 * @cfg {String} pos Submenu align to (left | right) default right
25754 * Create a new Item
25755 * @param {Object} config The config object
25759 Roo.bootstrap.menu.Item = function(config){
25760 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25764 * Fires when the mouse is hovering over this menu
25765 * @param {Roo.bootstrap.menu.Item} this
25766 * @param {Roo.EventObject} e
25771 * Fires when the mouse exits this menu
25772 * @param {Roo.bootstrap.menu.Item} this
25773 * @param {Roo.EventObject} e
25779 * The raw click event for the entire grid.
25780 * @param {Roo.EventObject} e
25786 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25791 preventDefault: true,
25796 getAutoCreate : function()
25801 cls : 'roo-menu-item-text',
25809 cls : 'fa ' + this.icon
25818 href : this.href || '#',
25825 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25829 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25831 if(this.pos == 'left'){
25832 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25839 initEvents : function()
25841 this.el.on('mouseover', this.onMouseOver, this);
25842 this.el.on('mouseout', this.onMouseOut, this);
25844 this.el.select('a', true).first().on('click', this.onClick, this);
25848 onClick : function(e)
25850 if(this.preventDefault){
25851 e.preventDefault();
25854 this.fireEvent("click", this, e);
25857 onMouseOver : function(e)
25859 if(this.submenu && this.pos == 'left'){
25860 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25863 this.fireEvent("mouseover", this, e);
25866 onMouseOut : function(e)
25868 this.fireEvent("mouseout", this, e);
25880 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25883 * @class Roo.bootstrap.menu.Separator
25884 * @extends Roo.bootstrap.Component
25885 * Bootstrap Separator class
25888 * Create a new Separator
25889 * @param {Object} config The config object
25893 Roo.bootstrap.menu.Separator = function(config){
25894 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25897 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25899 getAutoCreate : function(){
25920 * @class Roo.bootstrap.Tooltip
25921 * Bootstrap Tooltip class
25922 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25923 * to determine which dom element triggers the tooltip.
25925 * It needs to add support for additional attributes like tooltip-position
25928 * Create a new Toolti
25929 * @param {Object} config The config object
25932 Roo.bootstrap.Tooltip = function(config){
25933 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25935 this.alignment = Roo.bootstrap.Tooltip.alignment;
25937 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25938 this.alignment = config.alignment;
25943 Roo.apply(Roo.bootstrap.Tooltip, {
25945 * @function init initialize tooltip monitoring.
25949 currentTip : false,
25950 currentRegion : false,
25956 Roo.get(document).on('mouseover', this.enter ,this);
25957 Roo.get(document).on('mouseout', this.leave, this);
25960 this.currentTip = new Roo.bootstrap.Tooltip();
25963 enter : function(ev)
25965 var dom = ev.getTarget();
25967 //Roo.log(['enter',dom]);
25968 var el = Roo.fly(dom);
25969 if (this.currentEl) {
25971 //Roo.log(this.currentEl);
25972 //Roo.log(this.currentEl.contains(dom));
25973 if (this.currentEl == el) {
25976 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25982 if (this.currentTip.el) {
25983 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25987 if(!el || el.dom == document){
25993 // you can not look for children, as if el is the body.. then everythign is the child..
25994 if (!el.attr('tooltip')) { //
25995 if (!el.select("[tooltip]").elements.length) {
25998 // is the mouse over this child...?
25999 bindEl = el.select("[tooltip]").first();
26000 var xy = ev.getXY();
26001 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26002 //Roo.log("not in region.");
26005 //Roo.log("child element over..");
26008 this.currentEl = bindEl;
26009 this.currentTip.bind(bindEl);
26010 this.currentRegion = Roo.lib.Region.getRegion(dom);
26011 this.currentTip.enter();
26014 leave : function(ev)
26016 var dom = ev.getTarget();
26017 //Roo.log(['leave',dom]);
26018 if (!this.currentEl) {
26023 if (dom != this.currentEl.dom) {
26026 var xy = ev.getXY();
26027 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26030 // only activate leave if mouse cursor is outside... bounding box..
26035 if (this.currentTip) {
26036 this.currentTip.leave();
26038 //Roo.log('clear currentEl');
26039 this.currentEl = false;
26044 'left' : ['r-l', [-2,0], 'right'],
26045 'right' : ['l-r', [2,0], 'left'],
26046 'bottom' : ['t-b', [0,2], 'top'],
26047 'top' : [ 'b-t', [0,-2], 'bottom']
26053 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26058 delay : null, // can be { show : 300 , hide: 500}
26062 hoverState : null, //???
26064 placement : 'bottom',
26068 getAutoCreate : function(){
26075 cls : 'tooltip-arrow'
26078 cls : 'tooltip-inner'
26085 bind : function(el)
26091 enter : function () {
26093 if (this.timeout != null) {
26094 clearTimeout(this.timeout);
26097 this.hoverState = 'in';
26098 //Roo.log("enter - show");
26099 if (!this.delay || !this.delay.show) {
26104 this.timeout = setTimeout(function () {
26105 if (_t.hoverState == 'in') {
26108 }, this.delay.show);
26112 clearTimeout(this.timeout);
26114 this.hoverState = 'out';
26115 if (!this.delay || !this.delay.hide) {
26121 this.timeout = setTimeout(function () {
26122 //Roo.log("leave - timeout");
26124 if (_t.hoverState == 'out') {
26126 Roo.bootstrap.Tooltip.currentEl = false;
26131 show : function (msg)
26134 this.render(document.body);
26137 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26139 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26141 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26143 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26145 var placement = typeof this.placement == 'function' ?
26146 this.placement.call(this, this.el, on_el) :
26149 var autoToken = /\s?auto?\s?/i;
26150 var autoPlace = autoToken.test(placement);
26152 placement = placement.replace(autoToken, '') || 'top';
26156 //this.el.setXY([0,0]);
26158 //this.el.dom.style.display='block';
26160 //this.el.appendTo(on_el);
26162 var p = this.getPosition();
26163 var box = this.el.getBox();
26169 var align = this.alignment[placement];
26171 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26173 if(placement == 'top' || placement == 'bottom'){
26175 placement = 'right';
26178 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26179 placement = 'left';
26182 var scroll = Roo.select('body', true).first().getScroll();
26184 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26190 this.el.alignTo(this.bindEl, align[0],align[1]);
26191 //var arrow = this.el.select('.arrow',true).first();
26192 //arrow.set(align[2],
26194 this.el.addClass(placement);
26196 this.el.addClass('in fade');
26198 this.hoverState = null;
26200 if (this.el.hasClass('fade')) {
26211 //this.el.setXY([0,0]);
26212 this.el.removeClass('in');
26228 * @class Roo.bootstrap.LocationPicker
26229 * @extends Roo.bootstrap.Component
26230 * Bootstrap LocationPicker class
26231 * @cfg {Number} latitude Position when init default 0
26232 * @cfg {Number} longitude Position when init default 0
26233 * @cfg {Number} zoom default 15
26234 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26235 * @cfg {Boolean} mapTypeControl default false
26236 * @cfg {Boolean} disableDoubleClickZoom default false
26237 * @cfg {Boolean} scrollwheel default true
26238 * @cfg {Boolean} streetViewControl default false
26239 * @cfg {Number} radius default 0
26240 * @cfg {String} locationName
26241 * @cfg {Boolean} draggable default true
26242 * @cfg {Boolean} enableAutocomplete default false
26243 * @cfg {Boolean} enableReverseGeocode default true
26244 * @cfg {String} markerTitle
26247 * Create a new LocationPicker
26248 * @param {Object} config The config object
26252 Roo.bootstrap.LocationPicker = function(config){
26254 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26259 * Fires when the picker initialized.
26260 * @param {Roo.bootstrap.LocationPicker} this
26261 * @param {Google Location} location
26265 * @event positionchanged
26266 * Fires when the picker position changed.
26267 * @param {Roo.bootstrap.LocationPicker} this
26268 * @param {Google Location} location
26270 positionchanged : true,
26273 * Fires when the map resize.
26274 * @param {Roo.bootstrap.LocationPicker} this
26279 * Fires when the map show.
26280 * @param {Roo.bootstrap.LocationPicker} this
26285 * Fires when the map hide.
26286 * @param {Roo.bootstrap.LocationPicker} this
26291 * Fires when click the map.
26292 * @param {Roo.bootstrap.LocationPicker} this
26293 * @param {Map event} e
26297 * @event mapRightClick
26298 * Fires when right click the map.
26299 * @param {Roo.bootstrap.LocationPicker} this
26300 * @param {Map event} e
26302 mapRightClick : true,
26304 * @event markerClick
26305 * Fires when click the marker.
26306 * @param {Roo.bootstrap.LocationPicker} this
26307 * @param {Map event} e
26309 markerClick : true,
26311 * @event markerRightClick
26312 * Fires when right click the marker.
26313 * @param {Roo.bootstrap.LocationPicker} this
26314 * @param {Map event} e
26316 markerRightClick : true,
26318 * @event OverlayViewDraw
26319 * Fires when OverlayView Draw
26320 * @param {Roo.bootstrap.LocationPicker} this
26322 OverlayViewDraw : true,
26324 * @event OverlayViewOnAdd
26325 * Fires when OverlayView Draw
26326 * @param {Roo.bootstrap.LocationPicker} this
26328 OverlayViewOnAdd : true,
26330 * @event OverlayViewOnRemove
26331 * Fires when OverlayView Draw
26332 * @param {Roo.bootstrap.LocationPicker} this
26334 OverlayViewOnRemove : true,
26336 * @event OverlayViewShow
26337 * Fires when OverlayView Draw
26338 * @param {Roo.bootstrap.LocationPicker} this
26339 * @param {Pixel} cpx
26341 OverlayViewShow : true,
26343 * @event OverlayViewHide
26344 * Fires when OverlayView Draw
26345 * @param {Roo.bootstrap.LocationPicker} this
26347 OverlayViewHide : true,
26349 * @event loadexception
26350 * Fires when load google lib failed.
26351 * @param {Roo.bootstrap.LocationPicker} this
26353 loadexception : true
26358 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26360 gMapContext: false,
26366 mapTypeControl: false,
26367 disableDoubleClickZoom: false,
26369 streetViewControl: false,
26373 enableAutocomplete: false,
26374 enableReverseGeocode: true,
26377 getAutoCreate: function()
26382 cls: 'roo-location-picker'
26388 initEvents: function(ct, position)
26390 if(!this.el.getWidth() || this.isApplied()){
26394 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26399 initial: function()
26401 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26402 this.fireEvent('loadexception', this);
26406 if(!this.mapTypeId){
26407 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26410 this.gMapContext = this.GMapContext();
26412 this.initOverlayView();
26414 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26418 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26419 _this.setPosition(_this.gMapContext.marker.position);
26422 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26423 _this.fireEvent('mapClick', this, event);
26427 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26428 _this.fireEvent('mapRightClick', this, event);
26432 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26433 _this.fireEvent('markerClick', this, event);
26437 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26438 _this.fireEvent('markerRightClick', this, event);
26442 this.setPosition(this.gMapContext.location);
26444 this.fireEvent('initial', this, this.gMapContext.location);
26447 initOverlayView: function()
26451 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26455 _this.fireEvent('OverlayViewDraw', _this);
26460 _this.fireEvent('OverlayViewOnAdd', _this);
26463 onRemove: function()
26465 _this.fireEvent('OverlayViewOnRemove', _this);
26468 show: function(cpx)
26470 _this.fireEvent('OverlayViewShow', _this, cpx);
26475 _this.fireEvent('OverlayViewHide', _this);
26481 fromLatLngToContainerPixel: function(event)
26483 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26486 isApplied: function()
26488 return this.getGmapContext() == false ? false : true;
26491 getGmapContext: function()
26493 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26496 GMapContext: function()
26498 var position = new google.maps.LatLng(this.latitude, this.longitude);
26500 var _map = new google.maps.Map(this.el.dom, {
26503 mapTypeId: this.mapTypeId,
26504 mapTypeControl: this.mapTypeControl,
26505 disableDoubleClickZoom: this.disableDoubleClickZoom,
26506 scrollwheel: this.scrollwheel,
26507 streetViewControl: this.streetViewControl,
26508 locationName: this.locationName,
26509 draggable: this.draggable,
26510 enableAutocomplete: this.enableAutocomplete,
26511 enableReverseGeocode: this.enableReverseGeocode
26514 var _marker = new google.maps.Marker({
26515 position: position,
26517 title: this.markerTitle,
26518 draggable: this.draggable
26525 location: position,
26526 radius: this.radius,
26527 locationName: this.locationName,
26528 addressComponents: {
26529 formatted_address: null,
26530 addressLine1: null,
26531 addressLine2: null,
26533 streetNumber: null,
26537 stateOrProvince: null
26540 domContainer: this.el.dom,
26541 geodecoder: new google.maps.Geocoder()
26545 drawCircle: function(center, radius, options)
26547 if (this.gMapContext.circle != null) {
26548 this.gMapContext.circle.setMap(null);
26552 options = Roo.apply({}, options, {
26553 strokeColor: "#0000FF",
26554 strokeOpacity: .35,
26556 fillColor: "#0000FF",
26560 options.map = this.gMapContext.map;
26561 options.radius = radius;
26562 options.center = center;
26563 this.gMapContext.circle = new google.maps.Circle(options);
26564 return this.gMapContext.circle;
26570 setPosition: function(location)
26572 this.gMapContext.location = location;
26573 this.gMapContext.marker.setPosition(location);
26574 this.gMapContext.map.panTo(location);
26575 this.drawCircle(location, this.gMapContext.radius, {});
26579 if (this.gMapContext.settings.enableReverseGeocode) {
26580 this.gMapContext.geodecoder.geocode({
26581 latLng: this.gMapContext.location
26582 }, function(results, status) {
26584 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26585 _this.gMapContext.locationName = results[0].formatted_address;
26586 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26588 _this.fireEvent('positionchanged', this, location);
26595 this.fireEvent('positionchanged', this, location);
26600 google.maps.event.trigger(this.gMapContext.map, "resize");
26602 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26604 this.fireEvent('resize', this);
26607 setPositionByLatLng: function(latitude, longitude)
26609 this.setPosition(new google.maps.LatLng(latitude, longitude));
26612 getCurrentPosition: function()
26615 latitude: this.gMapContext.location.lat(),
26616 longitude: this.gMapContext.location.lng()
26620 getAddressName: function()
26622 return this.gMapContext.locationName;
26625 getAddressComponents: function()
26627 return this.gMapContext.addressComponents;
26630 address_component_from_google_geocode: function(address_components)
26634 for (var i = 0; i < address_components.length; i++) {
26635 var component = address_components[i];
26636 if (component.types.indexOf("postal_code") >= 0) {
26637 result.postalCode = component.short_name;
26638 } else if (component.types.indexOf("street_number") >= 0) {
26639 result.streetNumber = component.short_name;
26640 } else if (component.types.indexOf("route") >= 0) {
26641 result.streetName = component.short_name;
26642 } else if (component.types.indexOf("neighborhood") >= 0) {
26643 result.city = component.short_name;
26644 } else if (component.types.indexOf("locality") >= 0) {
26645 result.city = component.short_name;
26646 } else if (component.types.indexOf("sublocality") >= 0) {
26647 result.district = component.short_name;
26648 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26649 result.stateOrProvince = component.short_name;
26650 } else if (component.types.indexOf("country") >= 0) {
26651 result.country = component.short_name;
26655 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26656 result.addressLine2 = "";
26660 setZoomLevel: function(zoom)
26662 this.gMapContext.map.setZoom(zoom);
26675 this.fireEvent('show', this);
26686 this.fireEvent('hide', this);
26691 Roo.apply(Roo.bootstrap.LocationPicker, {
26693 OverlayView : function(map, options)
26695 options = options || {};
26709 * @class Roo.bootstrap.Alert
26710 * @extends Roo.bootstrap.Component
26711 * Bootstrap Alert class
26712 * @cfg {String} title The title of alert
26713 * @cfg {String} html The content of alert
26714 * @cfg {String} weight ( success | info | warning | danger )
26715 * @cfg {String} faicon font-awesomeicon
26718 * Create a new alert
26719 * @param {Object} config The config object
26723 Roo.bootstrap.Alert = function(config){
26724 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26728 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26735 getAutoCreate : function()
26744 cls : 'roo-alert-icon'
26749 cls : 'roo-alert-title',
26754 cls : 'roo-alert-text',
26761 cfg.cn[0].cls += ' fa ' + this.faicon;
26765 cfg.cls += ' alert-' + this.weight;
26771 initEvents: function()
26773 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26776 setTitle : function(str)
26778 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26781 setText : function(str)
26783 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26786 setWeight : function(weight)
26789 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26792 this.weight = weight;
26794 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26797 setIcon : function(icon)
26800 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26803 this.faicon = icon;
26805 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26826 * @class Roo.bootstrap.UploadCropbox
26827 * @extends Roo.bootstrap.Component
26828 * Bootstrap UploadCropbox class
26829 * @cfg {String} emptyText show when image has been loaded
26830 * @cfg {String} rotateNotify show when image too small to rotate
26831 * @cfg {Number} errorTimeout default 3000
26832 * @cfg {Number} minWidth default 300
26833 * @cfg {Number} minHeight default 300
26834 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26835 * @cfg {Boolean} isDocument (true|false) default false
26836 * @cfg {String} url action url
26837 * @cfg {String} paramName default 'imageUpload'
26838 * @cfg {String} method default POST
26839 * @cfg {Boolean} loadMask (true|false) default true
26840 * @cfg {Boolean} loadingText default 'Loading...'
26843 * Create a new UploadCropbox
26844 * @param {Object} config The config object
26847 Roo.bootstrap.UploadCropbox = function(config){
26848 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26852 * @event beforeselectfile
26853 * Fire before select file
26854 * @param {Roo.bootstrap.UploadCropbox} this
26856 "beforeselectfile" : true,
26859 * Fire after initEvent
26860 * @param {Roo.bootstrap.UploadCropbox} this
26865 * Fire after initEvent
26866 * @param {Roo.bootstrap.UploadCropbox} this
26867 * @param {String} data
26872 * Fire when preparing the file data
26873 * @param {Roo.bootstrap.UploadCropbox} this
26874 * @param {Object} file
26879 * Fire when get exception
26880 * @param {Roo.bootstrap.UploadCropbox} this
26881 * @param {XMLHttpRequest} xhr
26883 "exception" : true,
26885 * @event beforeloadcanvas
26886 * Fire before load the canvas
26887 * @param {Roo.bootstrap.UploadCropbox} this
26888 * @param {String} src
26890 "beforeloadcanvas" : true,
26893 * Fire when trash image
26894 * @param {Roo.bootstrap.UploadCropbox} this
26899 * Fire when download the image
26900 * @param {Roo.bootstrap.UploadCropbox} this
26904 * @event footerbuttonclick
26905 * Fire when footerbuttonclick
26906 * @param {Roo.bootstrap.UploadCropbox} this
26907 * @param {String} type
26909 "footerbuttonclick" : true,
26913 * @param {Roo.bootstrap.UploadCropbox} this
26918 * Fire when rotate the image
26919 * @param {Roo.bootstrap.UploadCropbox} this
26920 * @param {String} pos
26925 * Fire when inspect the file
26926 * @param {Roo.bootstrap.UploadCropbox} this
26927 * @param {Object} file
26932 * Fire when xhr upload the file
26933 * @param {Roo.bootstrap.UploadCropbox} this
26934 * @param {Object} data
26939 * Fire when arrange the file data
26940 * @param {Roo.bootstrap.UploadCropbox} this
26941 * @param {Object} formData
26946 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26949 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26951 emptyText : 'Click to upload image',
26952 rotateNotify : 'Image is too small to rotate',
26953 errorTimeout : 3000,
26967 cropType : 'image/jpeg',
26969 canvasLoaded : false,
26970 isDocument : false,
26972 paramName : 'imageUpload',
26974 loadingText : 'Loading...',
26977 getAutoCreate : function()
26981 cls : 'roo-upload-cropbox',
26985 cls : 'roo-upload-cropbox-selector',
26990 cls : 'roo-upload-cropbox-body',
26991 style : 'cursor:pointer',
26995 cls : 'roo-upload-cropbox-preview'
26999 cls : 'roo-upload-cropbox-thumb'
27003 cls : 'roo-upload-cropbox-empty-notify',
27004 html : this.emptyText
27008 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27009 html : this.rotateNotify
27015 cls : 'roo-upload-cropbox-footer',
27018 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27028 onRender : function(ct, position)
27030 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27032 if (this.buttons.length) {
27034 Roo.each(this.buttons, function(bb) {
27036 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27038 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27044 this.maskEl = this.el;
27048 initEvents : function()
27050 this.urlAPI = (window.createObjectURL && window) ||
27051 (window.URL && URL.revokeObjectURL && URL) ||
27052 (window.webkitURL && webkitURL);
27054 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27055 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27057 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27058 this.selectorEl.hide();
27060 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27061 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27063 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27064 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27065 this.thumbEl.hide();
27067 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27068 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27071 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072 this.errorEl.hide();
27074 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27075 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076 this.footerEl.hide();
27078 this.setThumbBoxSize();
27084 this.fireEvent('initial', this);
27091 window.addEventListener("resize", function() { _this.resize(); } );
27093 this.bodyEl.on('click', this.beforeSelectFile, this);
27096 this.bodyEl.on('touchstart', this.onTouchStart, this);
27097 this.bodyEl.on('touchmove', this.onTouchMove, this);
27098 this.bodyEl.on('touchend', this.onTouchEnd, this);
27102 this.bodyEl.on('mousedown', this.onMouseDown, this);
27103 this.bodyEl.on('mousemove', this.onMouseMove, this);
27104 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27105 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27106 Roo.get(document).on('mouseup', this.onMouseUp, this);
27109 this.selectorEl.on('change', this.onFileSelected, this);
27115 this.baseScale = 1;
27117 this.baseRotate = 1;
27118 this.dragable = false;
27119 this.pinching = false;
27122 this.cropData = false;
27123 this.notifyEl.dom.innerHTML = this.emptyText;
27125 this.selectorEl.dom.value = '';
27129 resize : function()
27131 if(this.fireEvent('resize', this) != false){
27132 this.setThumbBoxPosition();
27133 this.setCanvasPosition();
27137 onFooterButtonClick : function(e, el, o, type)
27140 case 'rotate-left' :
27141 this.onRotateLeft(e);
27143 case 'rotate-right' :
27144 this.onRotateRight(e);
27147 this.beforeSelectFile(e);
27162 this.fireEvent('footerbuttonclick', this, type);
27165 beforeSelectFile : function(e)
27167 e.preventDefault();
27169 if(this.fireEvent('beforeselectfile', this) != false){
27170 this.selectorEl.dom.click();
27174 onFileSelected : function(e)
27176 e.preventDefault();
27178 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27182 var file = this.selectorEl.dom.files[0];
27184 if(this.fireEvent('inspect', this, file) != false){
27185 this.prepare(file);
27190 trash : function(e)
27192 this.fireEvent('trash', this);
27195 download : function(e)
27197 this.fireEvent('download', this);
27200 loadCanvas : function(src)
27202 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27206 this.imageEl = document.createElement('img');
27210 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27212 this.imageEl.src = src;
27216 onLoadCanvas : function()
27218 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27219 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27221 this.bodyEl.un('click', this.beforeSelectFile, this);
27223 this.notifyEl.hide();
27224 this.thumbEl.show();
27225 this.footerEl.show();
27227 this.baseRotateLevel();
27229 if(this.isDocument){
27230 this.setThumbBoxSize();
27233 this.setThumbBoxPosition();
27235 this.baseScaleLevel();
27241 this.canvasLoaded = true;
27244 this.maskEl.unmask();
27249 setCanvasPosition : function()
27251 if(!this.canvasEl){
27255 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27256 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27258 this.previewEl.setLeft(pw);
27259 this.previewEl.setTop(ph);
27263 onMouseDown : function(e)
27267 this.dragable = true;
27268 this.pinching = false;
27270 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27271 this.dragable = false;
27275 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27276 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27280 onMouseMove : function(e)
27284 if(!this.canvasLoaded){
27288 if (!this.dragable){
27292 var minX = Math.ceil(this.thumbEl.getLeft(true));
27293 var minY = Math.ceil(this.thumbEl.getTop(true));
27295 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27296 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27298 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27299 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27301 x = x - this.mouseX;
27302 y = y - this.mouseY;
27304 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27305 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27307 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27308 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27310 this.previewEl.setLeft(bgX);
27311 this.previewEl.setTop(bgY);
27313 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27314 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27317 onMouseUp : function(e)
27321 this.dragable = false;
27324 onMouseWheel : function(e)
27328 this.startScale = this.scale;
27330 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27332 if(!this.zoomable()){
27333 this.scale = this.startScale;
27342 zoomable : function()
27344 var minScale = this.thumbEl.getWidth() / this.minWidth;
27346 if(this.minWidth < this.minHeight){
27347 minScale = this.thumbEl.getHeight() / this.minHeight;
27350 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27351 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27355 (this.rotate == 0 || this.rotate == 180) &&
27357 width > this.imageEl.OriginWidth ||
27358 height > this.imageEl.OriginHeight ||
27359 (width < this.minWidth && height < this.minHeight)
27367 (this.rotate == 90 || this.rotate == 270) &&
27369 width > this.imageEl.OriginWidth ||
27370 height > this.imageEl.OriginHeight ||
27371 (width < this.minHeight && height < this.minWidth)
27378 !this.isDocument &&
27379 (this.rotate == 0 || this.rotate == 180) &&
27381 width < this.minWidth ||
27382 width > this.imageEl.OriginWidth ||
27383 height < this.minHeight ||
27384 height > this.imageEl.OriginHeight
27391 !this.isDocument &&
27392 (this.rotate == 90 || this.rotate == 270) &&
27394 width < this.minHeight ||
27395 width > this.imageEl.OriginWidth ||
27396 height < this.minWidth ||
27397 height > this.imageEl.OriginHeight
27407 onRotateLeft : function(e)
27409 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27411 var minScale = this.thumbEl.getWidth() / this.minWidth;
27413 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27414 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27416 this.startScale = this.scale;
27418 while (this.getScaleLevel() < minScale){
27420 this.scale = this.scale + 1;
27422 if(!this.zoomable()){
27427 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27428 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27433 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27440 this.scale = this.startScale;
27442 this.onRotateFail();
27447 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27449 if(this.isDocument){
27450 this.setThumbBoxSize();
27451 this.setThumbBoxPosition();
27452 this.setCanvasPosition();
27457 this.fireEvent('rotate', this, 'left');
27461 onRotateRight : function(e)
27463 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27465 var minScale = this.thumbEl.getWidth() / this.minWidth;
27467 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27468 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27470 this.startScale = this.scale;
27472 while (this.getScaleLevel() < minScale){
27474 this.scale = this.scale + 1;
27476 if(!this.zoomable()){
27481 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27482 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27487 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27494 this.scale = this.startScale;
27496 this.onRotateFail();
27501 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27503 if(this.isDocument){
27504 this.setThumbBoxSize();
27505 this.setThumbBoxPosition();
27506 this.setCanvasPosition();
27511 this.fireEvent('rotate', this, 'right');
27514 onRotateFail : function()
27516 this.errorEl.show(true);
27520 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27525 this.previewEl.dom.innerHTML = '';
27527 var canvasEl = document.createElement("canvas");
27529 var contextEl = canvasEl.getContext("2d");
27531 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27532 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27533 var center = this.imageEl.OriginWidth / 2;
27535 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27536 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27537 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27538 center = this.imageEl.OriginHeight / 2;
27541 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27543 contextEl.translate(center, center);
27544 contextEl.rotate(this.rotate * Math.PI / 180);
27546 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27548 this.canvasEl = document.createElement("canvas");
27550 this.contextEl = this.canvasEl.getContext("2d");
27552 switch (this.rotate) {
27555 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27556 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27558 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27563 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27564 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27566 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27567 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);
27571 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27576 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27577 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27579 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27580 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);
27584 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);
27589 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27590 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27592 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27593 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27597 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);
27604 this.previewEl.appendChild(this.canvasEl);
27606 this.setCanvasPosition();
27611 if(!this.canvasLoaded){
27615 var imageCanvas = document.createElement("canvas");
27617 var imageContext = imageCanvas.getContext("2d");
27619 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27620 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27622 var center = imageCanvas.width / 2;
27624 imageContext.translate(center, center);
27626 imageContext.rotate(this.rotate * Math.PI / 180);
27628 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27630 var canvas = document.createElement("canvas");
27632 var context = canvas.getContext("2d");
27634 canvas.width = this.minWidth;
27635 canvas.height = this.minHeight;
27637 switch (this.rotate) {
27640 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27641 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27643 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27644 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27646 var targetWidth = this.minWidth - 2 * x;
27647 var targetHeight = this.minHeight - 2 * y;
27651 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27652 scale = targetWidth / width;
27655 if(x > 0 && y == 0){
27656 scale = targetHeight / height;
27659 if(x > 0 && y > 0){
27660 scale = targetWidth / width;
27662 if(width < height){
27663 scale = targetHeight / height;
27667 context.scale(scale, scale);
27669 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27670 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27672 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27673 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27675 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27680 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27681 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27683 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27684 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27686 var targetWidth = this.minWidth - 2 * x;
27687 var targetHeight = this.minHeight - 2 * y;
27691 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27692 scale = targetWidth / width;
27695 if(x > 0 && y == 0){
27696 scale = targetHeight / height;
27699 if(x > 0 && y > 0){
27700 scale = targetWidth / width;
27702 if(width < height){
27703 scale = targetHeight / height;
27707 context.scale(scale, scale);
27709 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27710 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27712 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27713 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27715 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27717 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27722 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27723 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27725 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27726 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27728 var targetWidth = this.minWidth - 2 * x;
27729 var targetHeight = this.minHeight - 2 * y;
27733 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27734 scale = targetWidth / width;
27737 if(x > 0 && y == 0){
27738 scale = targetHeight / height;
27741 if(x > 0 && y > 0){
27742 scale = targetWidth / width;
27744 if(width < height){
27745 scale = targetHeight / height;
27749 context.scale(scale, scale);
27751 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27752 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27754 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27755 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27757 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27758 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27760 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27765 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27766 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27768 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27769 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27771 var targetWidth = this.minWidth - 2 * x;
27772 var targetHeight = this.minHeight - 2 * y;
27776 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27777 scale = targetWidth / width;
27780 if(x > 0 && y == 0){
27781 scale = targetHeight / height;
27784 if(x > 0 && y > 0){
27785 scale = targetWidth / width;
27787 if(width < height){
27788 scale = targetHeight / height;
27792 context.scale(scale, scale);
27794 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27795 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27797 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27798 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27800 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27802 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27809 this.cropData = canvas.toDataURL(this.cropType);
27811 if(this.fireEvent('crop', this, this.cropData) !== false){
27812 this.process(this.file, this.cropData);
27819 setThumbBoxSize : function()
27823 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27824 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27825 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27827 this.minWidth = width;
27828 this.minHeight = height;
27830 if(this.rotate == 90 || this.rotate == 270){
27831 this.minWidth = height;
27832 this.minHeight = width;
27837 width = Math.ceil(this.minWidth * height / this.minHeight);
27839 if(this.minWidth > this.minHeight){
27841 height = Math.ceil(this.minHeight * width / this.minWidth);
27844 this.thumbEl.setStyle({
27845 width : width + 'px',
27846 height : height + 'px'
27853 setThumbBoxPosition : function()
27855 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27856 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27858 this.thumbEl.setLeft(x);
27859 this.thumbEl.setTop(y);
27863 baseRotateLevel : function()
27865 this.baseRotate = 1;
27868 typeof(this.exif) != 'undefined' &&
27869 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27870 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27872 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27875 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27879 baseScaleLevel : function()
27883 if(this.isDocument){
27885 if(this.baseRotate == 6 || this.baseRotate == 8){
27887 height = this.thumbEl.getHeight();
27888 this.baseScale = height / this.imageEl.OriginWidth;
27890 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27891 width = this.thumbEl.getWidth();
27892 this.baseScale = width / this.imageEl.OriginHeight;
27898 height = this.thumbEl.getHeight();
27899 this.baseScale = height / this.imageEl.OriginHeight;
27901 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27902 width = this.thumbEl.getWidth();
27903 this.baseScale = width / this.imageEl.OriginWidth;
27909 if(this.baseRotate == 6 || this.baseRotate == 8){
27911 width = this.thumbEl.getHeight();
27912 this.baseScale = width / this.imageEl.OriginHeight;
27914 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27915 height = this.thumbEl.getWidth();
27916 this.baseScale = height / this.imageEl.OriginHeight;
27919 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27920 height = this.thumbEl.getWidth();
27921 this.baseScale = height / this.imageEl.OriginHeight;
27923 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27924 width = this.thumbEl.getHeight();
27925 this.baseScale = width / this.imageEl.OriginWidth;
27932 width = this.thumbEl.getWidth();
27933 this.baseScale = width / this.imageEl.OriginWidth;
27935 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27936 height = this.thumbEl.getHeight();
27937 this.baseScale = height / this.imageEl.OriginHeight;
27940 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27942 height = this.thumbEl.getHeight();
27943 this.baseScale = height / this.imageEl.OriginHeight;
27945 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27946 width = this.thumbEl.getWidth();
27947 this.baseScale = width / this.imageEl.OriginWidth;
27955 getScaleLevel : function()
27957 return this.baseScale * Math.pow(1.1, this.scale);
27960 onTouchStart : function(e)
27962 if(!this.canvasLoaded){
27963 this.beforeSelectFile(e);
27967 var touches = e.browserEvent.touches;
27973 if(touches.length == 1){
27974 this.onMouseDown(e);
27978 if(touches.length != 2){
27984 for(var i = 0, finger; finger = touches[i]; i++){
27985 coords.push(finger.pageX, finger.pageY);
27988 var x = Math.pow(coords[0] - coords[2], 2);
27989 var y = Math.pow(coords[1] - coords[3], 2);
27991 this.startDistance = Math.sqrt(x + y);
27993 this.startScale = this.scale;
27995 this.pinching = true;
27996 this.dragable = false;
28000 onTouchMove : function(e)
28002 if(!this.pinching && !this.dragable){
28006 var touches = e.browserEvent.touches;
28013 this.onMouseMove(e);
28019 for(var i = 0, finger; finger = touches[i]; i++){
28020 coords.push(finger.pageX, finger.pageY);
28023 var x = Math.pow(coords[0] - coords[2], 2);
28024 var y = Math.pow(coords[1] - coords[3], 2);
28026 this.endDistance = Math.sqrt(x + y);
28028 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28030 if(!this.zoomable()){
28031 this.scale = this.startScale;
28039 onTouchEnd : function(e)
28041 this.pinching = false;
28042 this.dragable = false;
28046 process : function(file, crop)
28049 this.maskEl.mask(this.loadingText);
28052 this.xhr = new XMLHttpRequest();
28054 file.xhr = this.xhr;
28056 this.xhr.open(this.method, this.url, true);
28059 "Accept": "application/json",
28060 "Cache-Control": "no-cache",
28061 "X-Requested-With": "XMLHttpRequest"
28064 for (var headerName in headers) {
28065 var headerValue = headers[headerName];
28067 this.xhr.setRequestHeader(headerName, headerValue);
28073 this.xhr.onload = function()
28075 _this.xhrOnLoad(_this.xhr);
28078 this.xhr.onerror = function()
28080 _this.xhrOnError(_this.xhr);
28083 var formData = new FormData();
28085 formData.append('returnHTML', 'NO');
28088 formData.append('crop', crop);
28091 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28092 formData.append(this.paramName, file, file.name);
28095 if(typeof(file.filename) != 'undefined'){
28096 formData.append('filename', file.filename);
28099 if(typeof(file.mimetype) != 'undefined'){
28100 formData.append('mimetype', file.mimetype);
28103 if(this.fireEvent('arrange', this, formData) != false){
28104 this.xhr.send(formData);
28108 xhrOnLoad : function(xhr)
28111 this.maskEl.unmask();
28114 if (xhr.readyState !== 4) {
28115 this.fireEvent('exception', this, xhr);
28119 var response = Roo.decode(xhr.responseText);
28121 if(!response.success){
28122 this.fireEvent('exception', this, xhr);
28126 var response = Roo.decode(xhr.responseText);
28128 this.fireEvent('upload', this, response);
28132 xhrOnError : function()
28135 this.maskEl.unmask();
28138 Roo.log('xhr on error');
28140 var response = Roo.decode(xhr.responseText);
28146 prepare : function(file)
28149 this.maskEl.mask(this.loadingText);
28155 if(typeof(file) === 'string'){
28156 this.loadCanvas(file);
28160 if(!file || !this.urlAPI){
28165 this.cropType = file.type;
28169 if(this.fireEvent('prepare', this, this.file) != false){
28171 var reader = new FileReader();
28173 reader.onload = function (e) {
28174 if (e.target.error) {
28175 Roo.log(e.target.error);
28179 var buffer = e.target.result,
28180 dataView = new DataView(buffer),
28182 maxOffset = dataView.byteLength - 4,
28186 if (dataView.getUint16(0) === 0xffd8) {
28187 while (offset < maxOffset) {
28188 markerBytes = dataView.getUint16(offset);
28190 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28191 markerLength = dataView.getUint16(offset + 2) + 2;
28192 if (offset + markerLength > dataView.byteLength) {
28193 Roo.log('Invalid meta data: Invalid segment size.');
28197 if(markerBytes == 0xffe1){
28198 _this.parseExifData(
28205 offset += markerLength;
28215 var url = _this.urlAPI.createObjectURL(_this.file);
28217 _this.loadCanvas(url);
28222 reader.readAsArrayBuffer(this.file);
28228 parseExifData : function(dataView, offset, length)
28230 var tiffOffset = offset + 10,
28234 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28235 // No Exif data, might be XMP data instead
28239 // Check for the ASCII code for "Exif" (0x45786966):
28240 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28241 // No Exif data, might be XMP data instead
28244 if (tiffOffset + 8 > dataView.byteLength) {
28245 Roo.log('Invalid Exif data: Invalid segment size.');
28248 // Check for the two null bytes:
28249 if (dataView.getUint16(offset + 8) !== 0x0000) {
28250 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28253 // Check the byte alignment:
28254 switch (dataView.getUint16(tiffOffset)) {
28256 littleEndian = true;
28259 littleEndian = false;
28262 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28265 // Check for the TIFF tag marker (0x002A):
28266 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28267 Roo.log('Invalid Exif data: Missing TIFF marker.');
28270 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28271 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28273 this.parseExifTags(
28276 tiffOffset + dirOffset,
28281 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28286 if (dirOffset + 6 > dataView.byteLength) {
28287 Roo.log('Invalid Exif data: Invalid directory offset.');
28290 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28291 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28292 if (dirEndOffset + 4 > dataView.byteLength) {
28293 Roo.log('Invalid Exif data: Invalid directory size.');
28296 for (i = 0; i < tagsNumber; i += 1) {
28300 dirOffset + 2 + 12 * i, // tag offset
28304 // Return the offset to the next directory:
28305 return dataView.getUint32(dirEndOffset, littleEndian);
28308 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28310 var tag = dataView.getUint16(offset, littleEndian);
28312 this.exif[tag] = this.getExifValue(
28316 dataView.getUint16(offset + 2, littleEndian), // tag type
28317 dataView.getUint32(offset + 4, littleEndian), // tag length
28322 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28324 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28333 Roo.log('Invalid Exif data: Invalid tag type.');
28337 tagSize = tagType.size * length;
28338 // Determine if the value is contained in the dataOffset bytes,
28339 // or if the value at the dataOffset is a pointer to the actual data:
28340 dataOffset = tagSize > 4 ?
28341 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28342 if (dataOffset + tagSize > dataView.byteLength) {
28343 Roo.log('Invalid Exif data: Invalid data offset.');
28346 if (length === 1) {
28347 return tagType.getValue(dataView, dataOffset, littleEndian);
28350 for (i = 0; i < length; i += 1) {
28351 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28354 if (tagType.ascii) {
28356 // Concatenate the chars:
28357 for (i = 0; i < values.length; i += 1) {
28359 // Ignore the terminating NULL byte(s):
28360 if (c === '\u0000') {
28372 Roo.apply(Roo.bootstrap.UploadCropbox, {
28374 'Orientation': 0x0112
28378 1: 0, //'top-left',
28380 3: 180, //'bottom-right',
28381 // 4: 'bottom-left',
28383 6: 90, //'right-top',
28384 // 7: 'right-bottom',
28385 8: 270 //'left-bottom'
28389 // byte, 8-bit unsigned int:
28391 getValue: function (dataView, dataOffset) {
28392 return dataView.getUint8(dataOffset);
28396 // ascii, 8-bit byte:
28398 getValue: function (dataView, dataOffset) {
28399 return String.fromCharCode(dataView.getUint8(dataOffset));
28404 // short, 16 bit int:
28406 getValue: function (dataView, dataOffset, littleEndian) {
28407 return dataView.getUint16(dataOffset, littleEndian);
28411 // long, 32 bit int:
28413 getValue: function (dataView, dataOffset, littleEndian) {
28414 return dataView.getUint32(dataOffset, littleEndian);
28418 // rational = two long values, first is numerator, second is denominator:
28420 getValue: function (dataView, dataOffset, littleEndian) {
28421 return dataView.getUint32(dataOffset, littleEndian) /
28422 dataView.getUint32(dataOffset + 4, littleEndian);
28426 // slong, 32 bit signed int:
28428 getValue: function (dataView, dataOffset, littleEndian) {
28429 return dataView.getInt32(dataOffset, littleEndian);
28433 // srational, two slongs, first is numerator, second is denominator:
28435 getValue: function (dataView, dataOffset, littleEndian) {
28436 return dataView.getInt32(dataOffset, littleEndian) /
28437 dataView.getInt32(dataOffset + 4, littleEndian);
28447 cls : 'btn-group roo-upload-cropbox-rotate-left',
28448 action : 'rotate-left',
28452 cls : 'btn btn-default',
28453 html : '<i class="fa fa-undo"></i>'
28459 cls : 'btn-group roo-upload-cropbox-picture',
28460 action : 'picture',
28464 cls : 'btn btn-default',
28465 html : '<i class="fa fa-picture-o"></i>'
28471 cls : 'btn-group roo-upload-cropbox-rotate-right',
28472 action : 'rotate-right',
28476 cls : 'btn btn-default',
28477 html : '<i class="fa fa-repeat"></i>'
28485 cls : 'btn-group roo-upload-cropbox-rotate-left',
28486 action : 'rotate-left',
28490 cls : 'btn btn-default',
28491 html : '<i class="fa fa-undo"></i>'
28497 cls : 'btn-group roo-upload-cropbox-download',
28498 action : 'download',
28502 cls : 'btn btn-default',
28503 html : '<i class="fa fa-download"></i>'
28509 cls : 'btn-group roo-upload-cropbox-crop',
28514 cls : 'btn btn-default',
28515 html : '<i class="fa fa-crop"></i>'
28521 cls : 'btn-group roo-upload-cropbox-trash',
28526 cls : 'btn btn-default',
28527 html : '<i class="fa fa-trash"></i>'
28533 cls : 'btn-group roo-upload-cropbox-rotate-right',
28534 action : 'rotate-right',
28538 cls : 'btn btn-default',
28539 html : '<i class="fa fa-repeat"></i>'
28547 cls : 'btn-group roo-upload-cropbox-rotate-left',
28548 action : 'rotate-left',
28552 cls : 'btn btn-default',
28553 html : '<i class="fa fa-undo"></i>'
28559 cls : 'btn-group roo-upload-cropbox-rotate-right',
28560 action : 'rotate-right',
28564 cls : 'btn btn-default',
28565 html : '<i class="fa fa-repeat"></i>'
28578 * @class Roo.bootstrap.DocumentManager
28579 * @extends Roo.bootstrap.Component
28580 * Bootstrap DocumentManager class
28581 * @cfg {String} paramName default 'imageUpload'
28582 * @cfg {String} toolTipName default 'filename'
28583 * @cfg {String} method default POST
28584 * @cfg {String} url action url
28585 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28586 * @cfg {Boolean} multiple multiple upload default true
28587 * @cfg {Number} thumbSize default 300
28588 * @cfg {String} fieldLabel
28589 * @cfg {Number} labelWidth default 4
28590 * @cfg {String} labelAlign (left|top) default left
28591 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28592 * @cfg {Number} labellg set the width of label (1-12)
28593 * @cfg {Number} labelmd set the width of label (1-12)
28594 * @cfg {Number} labelsm set the width of label (1-12)
28595 * @cfg {Number} labelxs set the width of label (1-12)
28598 * Create a new DocumentManager
28599 * @param {Object} config The config object
28602 Roo.bootstrap.DocumentManager = function(config){
28603 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28606 this.delegates = [];
28611 * Fire when initial the DocumentManager
28612 * @param {Roo.bootstrap.DocumentManager} this
28617 * inspect selected file
28618 * @param {Roo.bootstrap.DocumentManager} this
28619 * @param {File} file
28624 * Fire when xhr load exception
28625 * @param {Roo.bootstrap.DocumentManager} this
28626 * @param {XMLHttpRequest} xhr
28628 "exception" : true,
28630 * @event afterupload
28631 * Fire when xhr load exception
28632 * @param {Roo.bootstrap.DocumentManager} this
28633 * @param {XMLHttpRequest} xhr
28635 "afterupload" : true,
28638 * prepare the form data
28639 * @param {Roo.bootstrap.DocumentManager} this
28640 * @param {Object} formData
28645 * Fire when remove the file
28646 * @param {Roo.bootstrap.DocumentManager} this
28647 * @param {Object} file
28652 * Fire after refresh the file
28653 * @param {Roo.bootstrap.DocumentManager} this
28658 * Fire after click the image
28659 * @param {Roo.bootstrap.DocumentManager} this
28660 * @param {Object} file
28665 * Fire when upload a image and editable set to true
28666 * @param {Roo.bootstrap.DocumentManager} this
28667 * @param {Object} file
28671 * @event beforeselectfile
28672 * Fire before select file
28673 * @param {Roo.bootstrap.DocumentManager} this
28675 "beforeselectfile" : true,
28678 * Fire before process file
28679 * @param {Roo.bootstrap.DocumentManager} this
28680 * @param {Object} file
28684 * @event previewrendered
28685 * Fire when preview rendered
28686 * @param {Roo.bootstrap.DocumentManager} this
28687 * @param {Object} file
28689 "previewrendered" : true
28694 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28703 paramName : 'imageUpload',
28704 toolTipName : 'filename',
28707 labelAlign : 'left',
28717 getAutoCreate : function()
28719 var managerWidget = {
28721 cls : 'roo-document-manager',
28725 cls : 'roo-document-manager-selector',
28730 cls : 'roo-document-manager-uploader',
28734 cls : 'roo-document-manager-upload-btn',
28735 html : '<i class="fa fa-plus"></i>'
28746 cls : 'column col-md-12',
28751 if(this.fieldLabel.length){
28756 cls : 'column col-md-12',
28757 html : this.fieldLabel
28761 cls : 'column col-md-12',
28766 if(this.labelAlign == 'left'){
28771 html : this.fieldLabel
28780 if(this.labelWidth > 12){
28781 content[0].style = "width: " + this.labelWidth + 'px';
28784 if(this.labelWidth < 13 && this.labelmd == 0){
28785 this.labelmd = this.labelWidth;
28788 if(this.labellg > 0){
28789 content[0].cls += ' col-lg-' + this.labellg;
28790 content[1].cls += ' col-lg-' + (12 - this.labellg);
28793 if(this.labelmd > 0){
28794 content[0].cls += ' col-md-' + this.labelmd;
28795 content[1].cls += ' col-md-' + (12 - this.labelmd);
28798 if(this.labelsm > 0){
28799 content[0].cls += ' col-sm-' + this.labelsm;
28800 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28803 if(this.labelxs > 0){
28804 content[0].cls += ' col-xs-' + this.labelxs;
28805 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28813 cls : 'row clearfix',
28821 initEvents : function()
28823 this.managerEl = this.el.select('.roo-document-manager', true).first();
28824 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28826 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28827 this.selectorEl.hide();
28830 this.selectorEl.attr('multiple', 'multiple');
28833 this.selectorEl.on('change', this.onFileSelected, this);
28835 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28836 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28838 this.uploader.on('click', this.onUploaderClick, this);
28840 this.renderProgressDialog();
28844 window.addEventListener("resize", function() { _this.refresh(); } );
28846 this.fireEvent('initial', this);
28849 renderProgressDialog : function()
28853 this.progressDialog = new Roo.bootstrap.Modal({
28854 cls : 'roo-document-manager-progress-dialog',
28855 allow_close : false,
28865 btnclick : function() {
28866 _this.uploadCancel();
28872 this.progressDialog.render(Roo.get(document.body));
28874 this.progress = new Roo.bootstrap.Progress({
28875 cls : 'roo-document-manager-progress',
28880 this.progress.render(this.progressDialog.getChildContainer());
28882 this.progressBar = new Roo.bootstrap.ProgressBar({
28883 cls : 'roo-document-manager-progress-bar',
28886 aria_valuemax : 12,
28890 this.progressBar.render(this.progress.getChildContainer());
28893 onUploaderClick : function(e)
28895 e.preventDefault();
28897 if(this.fireEvent('beforeselectfile', this) != false){
28898 this.selectorEl.dom.click();
28903 onFileSelected : function(e)
28905 e.preventDefault();
28907 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28911 Roo.each(this.selectorEl.dom.files, function(file){
28912 if(this.fireEvent('inspect', this, file) != false){
28913 this.files.push(file);
28923 this.selectorEl.dom.value = '';
28925 if(!this.files || !this.files.length){
28929 if(this.boxes > 0 && this.files.length > this.boxes){
28930 this.files = this.files.slice(0, this.boxes);
28933 this.uploader.show();
28935 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28936 this.uploader.hide();
28945 Roo.each(this.files, function(file){
28947 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28948 var f = this.renderPreview(file);
28953 if(file.type.indexOf('image') != -1){
28954 this.delegates.push(
28956 _this.process(file);
28957 }).createDelegate(this)
28965 _this.process(file);
28966 }).createDelegate(this)
28971 this.files = files;
28973 this.delegates = this.delegates.concat(docs);
28975 if(!this.delegates.length){
28980 this.progressBar.aria_valuemax = this.delegates.length;
28987 arrange : function()
28989 if(!this.delegates.length){
28990 this.progressDialog.hide();
28995 var delegate = this.delegates.shift();
28997 this.progressDialog.show();
28999 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29001 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29006 refresh : function()
29008 this.uploader.show();
29010 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29011 this.uploader.hide();
29014 Roo.isTouch ? this.closable(false) : this.closable(true);
29016 this.fireEvent('refresh', this);
29019 onRemove : function(e, el, o)
29021 e.preventDefault();
29023 this.fireEvent('remove', this, o);
29027 remove : function(o)
29031 Roo.each(this.files, function(file){
29032 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29041 this.files = files;
29048 Roo.each(this.files, function(file){
29053 file.target.remove();
29062 onClick : function(e, el, o)
29064 e.preventDefault();
29066 this.fireEvent('click', this, o);
29070 closable : function(closable)
29072 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29074 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29086 xhrOnLoad : function(xhr)
29088 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29092 if (xhr.readyState !== 4) {
29094 this.fireEvent('exception', this, xhr);
29098 var response = Roo.decode(xhr.responseText);
29100 if(!response.success){
29102 this.fireEvent('exception', this, xhr);
29106 var file = this.renderPreview(response.data);
29108 this.files.push(file);
29112 this.fireEvent('afterupload', this, xhr);
29116 xhrOnError : function(xhr)
29118 Roo.log('xhr on error');
29120 var response = Roo.decode(xhr.responseText);
29127 process : function(file)
29129 if(this.fireEvent('process', this, file) !== false){
29130 if(this.editable && file.type.indexOf('image') != -1){
29131 this.fireEvent('edit', this, file);
29135 this.uploadStart(file, false);
29142 uploadStart : function(file, crop)
29144 this.xhr = new XMLHttpRequest();
29146 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29151 file.xhr = this.xhr;
29153 this.managerEl.createChild({
29155 cls : 'roo-document-manager-loading',
29159 tooltip : file.name,
29160 cls : 'roo-document-manager-thumb',
29161 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29167 this.xhr.open(this.method, this.url, true);
29170 "Accept": "application/json",
29171 "Cache-Control": "no-cache",
29172 "X-Requested-With": "XMLHttpRequest"
29175 for (var headerName in headers) {
29176 var headerValue = headers[headerName];
29178 this.xhr.setRequestHeader(headerName, headerValue);
29184 this.xhr.onload = function()
29186 _this.xhrOnLoad(_this.xhr);
29189 this.xhr.onerror = function()
29191 _this.xhrOnError(_this.xhr);
29194 var formData = new FormData();
29196 formData.append('returnHTML', 'NO');
29199 formData.append('crop', crop);
29202 formData.append(this.paramName, file, file.name);
29209 if(this.fireEvent('prepare', this, formData, options) != false){
29211 if(options.manually){
29215 this.xhr.send(formData);
29219 this.uploadCancel();
29222 uploadCancel : function()
29228 this.delegates = [];
29230 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29237 renderPreview : function(file)
29239 if(typeof(file.target) != 'undefined' && file.target){
29243 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29245 var previewEl = this.managerEl.createChild({
29247 cls : 'roo-document-manager-preview',
29251 tooltip : file[this.toolTipName],
29252 cls : 'roo-document-manager-thumb',
29253 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29258 html : '<i class="fa fa-times-circle"></i>'
29263 var close = previewEl.select('button.close', true).first();
29265 close.on('click', this.onRemove, this, file);
29267 file.target = previewEl;
29269 var image = previewEl.select('img', true).first();
29273 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29275 image.on('click', this.onClick, this, file);
29277 this.fireEvent('previewrendered', this, file);
29283 onPreviewLoad : function(file, image)
29285 if(typeof(file.target) == 'undefined' || !file.target){
29289 var width = image.dom.naturalWidth || image.dom.width;
29290 var height = image.dom.naturalHeight || image.dom.height;
29292 if(width > height){
29293 file.target.addClass('wide');
29297 file.target.addClass('tall');
29302 uploadFromSource : function(file, crop)
29304 this.xhr = new XMLHttpRequest();
29306 this.managerEl.createChild({
29308 cls : 'roo-document-manager-loading',
29312 tooltip : file.name,
29313 cls : 'roo-document-manager-thumb',
29314 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29320 this.xhr.open(this.method, this.url, true);
29323 "Accept": "application/json",
29324 "Cache-Control": "no-cache",
29325 "X-Requested-With": "XMLHttpRequest"
29328 for (var headerName in headers) {
29329 var headerValue = headers[headerName];
29331 this.xhr.setRequestHeader(headerName, headerValue);
29337 this.xhr.onload = function()
29339 _this.xhrOnLoad(_this.xhr);
29342 this.xhr.onerror = function()
29344 _this.xhrOnError(_this.xhr);
29347 var formData = new FormData();
29349 formData.append('returnHTML', 'NO');
29351 formData.append('crop', crop);
29353 if(typeof(file.filename) != 'undefined'){
29354 formData.append('filename', file.filename);
29357 if(typeof(file.mimetype) != 'undefined'){
29358 formData.append('mimetype', file.mimetype);
29363 if(this.fireEvent('prepare', this, formData) != false){
29364 this.xhr.send(formData);
29374 * @class Roo.bootstrap.DocumentViewer
29375 * @extends Roo.bootstrap.Component
29376 * Bootstrap DocumentViewer class
29377 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29378 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29381 * Create a new DocumentViewer
29382 * @param {Object} config The config object
29385 Roo.bootstrap.DocumentViewer = function(config){
29386 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29391 * Fire after initEvent
29392 * @param {Roo.bootstrap.DocumentViewer} this
29398 * @param {Roo.bootstrap.DocumentViewer} this
29403 * Fire after download button
29404 * @param {Roo.bootstrap.DocumentViewer} this
29409 * Fire after trash button
29410 * @param {Roo.bootstrap.DocumentViewer} this
29417 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29419 showDownload : true,
29423 getAutoCreate : function()
29427 cls : 'roo-document-viewer',
29431 cls : 'roo-document-viewer-body',
29435 cls : 'roo-document-viewer-thumb',
29439 cls : 'roo-document-viewer-image'
29447 cls : 'roo-document-viewer-footer',
29450 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29454 cls : 'btn-group roo-document-viewer-download',
29458 cls : 'btn btn-default',
29459 html : '<i class="fa fa-download"></i>'
29465 cls : 'btn-group roo-document-viewer-trash',
29469 cls : 'btn btn-default',
29470 html : '<i class="fa fa-trash"></i>'
29483 initEvents : function()
29485 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29486 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29488 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29489 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29491 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29492 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29494 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29495 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29497 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29498 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29500 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29501 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29503 this.bodyEl.on('click', this.onClick, this);
29504 this.downloadBtn.on('click', this.onDownload, this);
29505 this.trashBtn.on('click', this.onTrash, this);
29507 this.downloadBtn.hide();
29508 this.trashBtn.hide();
29510 if(this.showDownload){
29511 this.downloadBtn.show();
29514 if(this.showTrash){
29515 this.trashBtn.show();
29518 if(!this.showDownload && !this.showTrash) {
29519 this.footerEl.hide();
29524 initial : function()
29526 this.fireEvent('initial', this);
29530 onClick : function(e)
29532 e.preventDefault();
29534 this.fireEvent('click', this);
29537 onDownload : function(e)
29539 e.preventDefault();
29541 this.fireEvent('download', this);
29544 onTrash : function(e)
29546 e.preventDefault();
29548 this.fireEvent('trash', this);
29560 * @class Roo.bootstrap.NavProgressBar
29561 * @extends Roo.bootstrap.Component
29562 * Bootstrap NavProgressBar class
29565 * Create a new nav progress bar
29566 * @param {Object} config The config object
29569 Roo.bootstrap.NavProgressBar = function(config){
29570 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29572 this.bullets = this.bullets || [];
29574 // Roo.bootstrap.NavProgressBar.register(this);
29578 * Fires when the active item changes
29579 * @param {Roo.bootstrap.NavProgressBar} this
29580 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29581 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29588 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29593 getAutoCreate : function()
29595 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29599 cls : 'roo-navigation-bar-group',
29603 cls : 'roo-navigation-top-bar'
29607 cls : 'roo-navigation-bullets-bar',
29611 cls : 'roo-navigation-bar'
29618 cls : 'roo-navigation-bottom-bar'
29628 initEvents: function()
29633 onRender : function(ct, position)
29635 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29637 if(this.bullets.length){
29638 Roo.each(this.bullets, function(b){
29647 addItem : function(cfg)
29649 var item = new Roo.bootstrap.NavProgressItem(cfg);
29651 item.parentId = this.id;
29652 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29655 var top = new Roo.bootstrap.Element({
29657 cls : 'roo-navigation-bar-text'
29660 var bottom = new Roo.bootstrap.Element({
29662 cls : 'roo-navigation-bar-text'
29665 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29666 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29668 var topText = new Roo.bootstrap.Element({
29670 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29673 var bottomText = new Roo.bootstrap.Element({
29675 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29678 topText.onRender(top.el, null);
29679 bottomText.onRender(bottom.el, null);
29682 item.bottomEl = bottom;
29685 this.barItems.push(item);
29690 getActive : function()
29692 var active = false;
29694 Roo.each(this.barItems, function(v){
29696 if (!v.isActive()) {
29708 setActiveItem : function(item)
29712 Roo.each(this.barItems, function(v){
29713 if (v.rid == item.rid) {
29717 if (v.isActive()) {
29718 v.setActive(false);
29723 item.setActive(true);
29725 this.fireEvent('changed', this, item, prev);
29728 getBarItem: function(rid)
29732 Roo.each(this.barItems, function(e) {
29733 if (e.rid != rid) {
29744 indexOfItem : function(item)
29748 Roo.each(this.barItems, function(v, i){
29750 if (v.rid != item.rid) {
29761 setActiveNext : function()
29763 var i = this.indexOfItem(this.getActive());
29765 if (i > this.barItems.length) {
29769 this.setActiveItem(this.barItems[i+1]);
29772 setActivePrev : function()
29774 var i = this.indexOfItem(this.getActive());
29780 this.setActiveItem(this.barItems[i-1]);
29783 format : function()
29785 if(!this.barItems.length){
29789 var width = 100 / this.barItems.length;
29791 Roo.each(this.barItems, function(i){
29792 i.el.setStyle('width', width + '%');
29793 i.topEl.el.setStyle('width', width + '%');
29794 i.bottomEl.el.setStyle('width', width + '%');
29803 * Nav Progress Item
29808 * @class Roo.bootstrap.NavProgressItem
29809 * @extends Roo.bootstrap.Component
29810 * Bootstrap NavProgressItem class
29811 * @cfg {String} rid the reference id
29812 * @cfg {Boolean} active (true|false) Is item active default false
29813 * @cfg {Boolean} disabled (true|false) Is item active default false
29814 * @cfg {String} html
29815 * @cfg {String} position (top|bottom) text position default bottom
29816 * @cfg {String} icon show icon instead of number
29819 * Create a new NavProgressItem
29820 * @param {Object} config The config object
29822 Roo.bootstrap.NavProgressItem = function(config){
29823 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29828 * The raw click event for the entire grid.
29829 * @param {Roo.bootstrap.NavProgressItem} this
29830 * @param {Roo.EventObject} e
29837 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29843 position : 'bottom',
29846 getAutoCreate : function()
29848 var iconCls = 'roo-navigation-bar-item-icon';
29850 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29854 cls: 'roo-navigation-bar-item',
29864 cfg.cls += ' active';
29867 cfg.cls += ' disabled';
29873 disable : function()
29875 this.setDisabled(true);
29878 enable : function()
29880 this.setDisabled(false);
29883 initEvents: function()
29885 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29887 this.iconEl.on('click', this.onClick, this);
29890 onClick : function(e)
29892 e.preventDefault();
29898 if(this.fireEvent('click', this, e) === false){
29902 this.parent().setActiveItem(this);
29905 isActive: function ()
29907 return this.active;
29910 setActive : function(state)
29912 if(this.active == state){
29916 this.active = state;
29919 this.el.addClass('active');
29923 this.el.removeClass('active');
29928 setDisabled : function(state)
29930 if(this.disabled == state){
29934 this.disabled = state;
29937 this.el.addClass('disabled');
29941 this.el.removeClass('disabled');
29944 tooltipEl : function()
29946 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29959 * @class Roo.bootstrap.FieldLabel
29960 * @extends Roo.bootstrap.Component
29961 * Bootstrap FieldLabel class
29962 * @cfg {String} html contents of the element
29963 * @cfg {String} tag tag of the element default label
29964 * @cfg {String} cls class of the element
29965 * @cfg {String} target label target
29966 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29967 * @cfg {String} invalidClass default "text-warning"
29968 * @cfg {String} validClass default "text-success"
29969 * @cfg {String} iconTooltip default "This field is required"
29970 * @cfg {String} indicatorpos (left|right) default left
29973 * Create a new FieldLabel
29974 * @param {Object} config The config object
29977 Roo.bootstrap.FieldLabel = function(config){
29978 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29983 * Fires after the field has been marked as invalid.
29984 * @param {Roo.form.FieldLabel} this
29985 * @param {String} msg The validation message
29990 * Fires after the field has been validated with no errors.
29991 * @param {Roo.form.FieldLabel} this
29997 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30004 invalidClass : 'has-warning',
30005 validClass : 'has-success',
30006 iconTooltip : 'This field is required',
30007 indicatorpos : 'left',
30009 getAutoCreate : function(){
30013 cls : 'roo-bootstrap-field-label ' + this.cls,
30018 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30019 tooltip : this.iconTooltip
30028 if(this.indicatorpos == 'right'){
30031 cls : 'roo-bootstrap-field-label ' + this.cls,
30040 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30041 tooltip : this.iconTooltip
30050 initEvents: function()
30052 Roo.bootstrap.Element.superclass.initEvents.call(this);
30054 this.indicator = this.indicatorEl();
30056 if(this.indicator){
30057 this.indicator.removeClass('visible');
30058 this.indicator.addClass('invisible');
30061 Roo.bootstrap.FieldLabel.register(this);
30064 indicatorEl : function()
30066 var indicator = this.el.select('i.roo-required-indicator',true).first();
30077 * Mark this field as valid
30079 markValid : function()
30081 if(this.indicator){
30082 this.indicator.removeClass('visible');
30083 this.indicator.addClass('invisible');
30086 this.el.removeClass(this.invalidClass);
30088 this.el.addClass(this.validClass);
30090 this.fireEvent('valid', this);
30094 * Mark this field as invalid
30095 * @param {String} msg The validation message
30097 markInvalid : function(msg)
30099 if(this.indicator){
30100 this.indicator.removeClass('invisible');
30101 this.indicator.addClass('visible');
30104 this.el.removeClass(this.validClass);
30106 this.el.addClass(this.invalidClass);
30108 this.fireEvent('invalid', this, msg);
30114 Roo.apply(Roo.bootstrap.FieldLabel, {
30119 * register a FieldLabel Group
30120 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30122 register : function(label)
30124 if(this.groups.hasOwnProperty(label.target)){
30128 this.groups[label.target] = label;
30132 * fetch a FieldLabel Group based on the target
30133 * @param {string} target
30134 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30136 get: function(target) {
30137 if (typeof(this.groups[target]) == 'undefined') {
30141 return this.groups[target] ;
30150 * page DateSplitField.
30156 * @class Roo.bootstrap.DateSplitField
30157 * @extends Roo.bootstrap.Component
30158 * Bootstrap DateSplitField class
30159 * @cfg {string} fieldLabel - the label associated
30160 * @cfg {Number} labelWidth set the width of label (0-12)
30161 * @cfg {String} labelAlign (top|left)
30162 * @cfg {Boolean} dayAllowBlank (true|false) default false
30163 * @cfg {Boolean} monthAllowBlank (true|false) default false
30164 * @cfg {Boolean} yearAllowBlank (true|false) default false
30165 * @cfg {string} dayPlaceholder
30166 * @cfg {string} monthPlaceholder
30167 * @cfg {string} yearPlaceholder
30168 * @cfg {string} dayFormat default 'd'
30169 * @cfg {string} monthFormat default 'm'
30170 * @cfg {string} yearFormat default 'Y'
30171 * @cfg {Number} labellg set the width of label (1-12)
30172 * @cfg {Number} labelmd set the width of label (1-12)
30173 * @cfg {Number} labelsm set the width of label (1-12)
30174 * @cfg {Number} labelxs set the width of label (1-12)
30178 * Create a new DateSplitField
30179 * @param {Object} config The config object
30182 Roo.bootstrap.DateSplitField = function(config){
30183 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30189 * getting the data of years
30190 * @param {Roo.bootstrap.DateSplitField} this
30191 * @param {Object} years
30196 * getting the data of days
30197 * @param {Roo.bootstrap.DateSplitField} this
30198 * @param {Object} days
30203 * Fires after the field has been marked as invalid.
30204 * @param {Roo.form.Field} this
30205 * @param {String} msg The validation message
30210 * Fires after the field has been validated with no errors.
30211 * @param {Roo.form.Field} this
30217 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30220 labelAlign : 'top',
30222 dayAllowBlank : false,
30223 monthAllowBlank : false,
30224 yearAllowBlank : false,
30225 dayPlaceholder : '',
30226 monthPlaceholder : '',
30227 yearPlaceholder : '',
30231 isFormField : true,
30237 getAutoCreate : function()
30241 cls : 'row roo-date-split-field-group',
30246 cls : 'form-hidden-field roo-date-split-field-group-value',
30252 var labelCls = 'col-md-12';
30253 var contentCls = 'col-md-4';
30255 if(this.fieldLabel){
30259 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30263 html : this.fieldLabel
30268 if(this.labelAlign == 'left'){
30270 if(this.labelWidth > 12){
30271 label.style = "width: " + this.labelWidth + 'px';
30274 if(this.labelWidth < 13 && this.labelmd == 0){
30275 this.labelmd = this.labelWidth;
30278 if(this.labellg > 0){
30279 labelCls = ' col-lg-' + this.labellg;
30280 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30283 if(this.labelmd > 0){
30284 labelCls = ' col-md-' + this.labelmd;
30285 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30288 if(this.labelsm > 0){
30289 labelCls = ' col-sm-' + this.labelsm;
30290 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30293 if(this.labelxs > 0){
30294 labelCls = ' col-xs-' + this.labelxs;
30295 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30299 label.cls += ' ' + labelCls;
30301 cfg.cn.push(label);
30304 Roo.each(['day', 'month', 'year'], function(t){
30307 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30314 inputEl: function ()
30316 return this.el.select('.roo-date-split-field-group-value', true).first();
30319 onRender : function(ct, position)
30323 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30325 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30327 this.dayField = new Roo.bootstrap.ComboBox({
30328 allowBlank : this.dayAllowBlank,
30329 alwaysQuery : true,
30330 displayField : 'value',
30333 forceSelection : true,
30335 placeholder : this.dayPlaceholder,
30336 selectOnFocus : true,
30337 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30338 triggerAction : 'all',
30340 valueField : 'value',
30341 store : new Roo.data.SimpleStore({
30342 data : (function() {
30344 _this.fireEvent('days', _this, days);
30347 fields : [ 'value' ]
30350 select : function (_self, record, index)
30352 _this.setValue(_this.getValue());
30357 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30359 this.monthField = new Roo.bootstrap.MonthField({
30360 after : '<i class=\"fa fa-calendar\"></i>',
30361 allowBlank : this.monthAllowBlank,
30362 placeholder : this.monthPlaceholder,
30365 render : function (_self)
30367 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30368 e.preventDefault();
30372 select : function (_self, oldvalue, newvalue)
30374 _this.setValue(_this.getValue());
30379 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30381 this.yearField = new Roo.bootstrap.ComboBox({
30382 allowBlank : this.yearAllowBlank,
30383 alwaysQuery : true,
30384 displayField : 'value',
30387 forceSelection : true,
30389 placeholder : this.yearPlaceholder,
30390 selectOnFocus : true,
30391 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30392 triggerAction : 'all',
30394 valueField : 'value',
30395 store : new Roo.data.SimpleStore({
30396 data : (function() {
30398 _this.fireEvent('years', _this, years);
30401 fields : [ 'value' ]
30404 select : function (_self, record, index)
30406 _this.setValue(_this.getValue());
30411 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30414 setValue : function(v, format)
30416 this.inputEl.dom.value = v;
30418 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30420 var d = Date.parseDate(v, f);
30427 this.setDay(d.format(this.dayFormat));
30428 this.setMonth(d.format(this.monthFormat));
30429 this.setYear(d.format(this.yearFormat));
30436 setDay : function(v)
30438 this.dayField.setValue(v);
30439 this.inputEl.dom.value = this.getValue();
30444 setMonth : function(v)
30446 this.monthField.setValue(v, true);
30447 this.inputEl.dom.value = this.getValue();
30452 setYear : function(v)
30454 this.yearField.setValue(v);
30455 this.inputEl.dom.value = this.getValue();
30460 getDay : function()
30462 return this.dayField.getValue();
30465 getMonth : function()
30467 return this.monthField.getValue();
30470 getYear : function()
30472 return this.yearField.getValue();
30475 getValue : function()
30477 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30479 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30489 this.inputEl.dom.value = '';
30494 validate : function()
30496 var d = this.dayField.validate();
30497 var m = this.monthField.validate();
30498 var y = this.yearField.validate();
30503 (!this.dayAllowBlank && !d) ||
30504 (!this.monthAllowBlank && !m) ||
30505 (!this.yearAllowBlank && !y)
30510 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30519 this.markInvalid();
30524 markValid : function()
30527 var label = this.el.select('label', true).first();
30528 var icon = this.el.select('i.fa-star', true).first();
30534 this.fireEvent('valid', this);
30538 * Mark this field as invalid
30539 * @param {String} msg The validation message
30541 markInvalid : function(msg)
30544 var label = this.el.select('label', true).first();
30545 var icon = this.el.select('i.fa-star', true).first();
30547 if(label && !icon){
30548 this.el.select('.roo-date-split-field-label', true).createChild({
30550 cls : 'text-danger fa fa-lg fa-star',
30551 tooltip : 'This field is required',
30552 style : 'margin-right:5px;'
30556 this.fireEvent('invalid', this, msg);
30559 clearInvalid : function()
30561 var label = this.el.select('label', true).first();
30562 var icon = this.el.select('i.fa-star', true).first();
30568 this.fireEvent('valid', this);
30571 getName: function()
30581 * http://masonry.desandro.com
30583 * The idea is to render all the bricks based on vertical width...
30585 * The original code extends 'outlayer' - we might need to use that....
30591 * @class Roo.bootstrap.LayoutMasonry
30592 * @extends Roo.bootstrap.Component
30593 * Bootstrap Layout Masonry class
30596 * Create a new Element
30597 * @param {Object} config The config object
30600 Roo.bootstrap.LayoutMasonry = function(config){
30602 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30606 Roo.bootstrap.LayoutMasonry.register(this);
30612 * Fire after layout the items
30613 * @param {Roo.bootstrap.LayoutMasonry} this
30614 * @param {Roo.EventObject} e
30621 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30624 * @cfg {Boolean} isLayoutInstant = no animation?
30626 isLayoutInstant : false, // needed?
30629 * @cfg {Number} boxWidth width of the columns
30634 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30639 * @cfg {Number} padWidth padding below box..
30644 * @cfg {Number} gutter gutter width..
30649 * @cfg {Number} maxCols maximum number of columns
30655 * @cfg {Boolean} isAutoInitial defalut true
30657 isAutoInitial : true,
30662 * @cfg {Boolean} isHorizontal defalut false
30664 isHorizontal : false,
30666 currentSize : null,
30672 bricks: null, //CompositeElement
30676 _isLayoutInited : false,
30678 // isAlternative : false, // only use for vertical layout...
30681 * @cfg {Number} alternativePadWidth padding below box..
30683 alternativePadWidth : 50,
30685 selectedBrick : [],
30687 getAutoCreate : function(){
30689 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30693 cls: 'blog-masonary-wrapper ' + this.cls,
30695 cls : 'mas-boxes masonary'
30702 getChildContainer: function( )
30704 if (this.boxesEl) {
30705 return this.boxesEl;
30708 this.boxesEl = this.el.select('.mas-boxes').first();
30710 return this.boxesEl;
30714 initEvents : function()
30718 if(this.isAutoInitial){
30719 Roo.log('hook children rendered');
30720 this.on('childrenrendered', function() {
30721 Roo.log('children rendered');
30727 initial : function()
30729 this.selectedBrick = [];
30731 this.currentSize = this.el.getBox(true);
30733 Roo.EventManager.onWindowResize(this.resize, this);
30735 if(!this.isAutoInitial){
30743 //this.layout.defer(500,this);
30747 resize : function()
30749 var cs = this.el.getBox(true);
30752 this.currentSize.width == cs.width &&
30753 this.currentSize.x == cs.x &&
30754 this.currentSize.height == cs.height &&
30755 this.currentSize.y == cs.y
30757 Roo.log("no change in with or X or Y");
30761 this.currentSize = cs;
30767 layout : function()
30769 this._resetLayout();
30771 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30773 this.layoutItems( isInstant );
30775 this._isLayoutInited = true;
30777 this.fireEvent('layout', this);
30781 _resetLayout : function()
30783 if(this.isHorizontal){
30784 this.horizontalMeasureColumns();
30788 this.verticalMeasureColumns();
30792 verticalMeasureColumns : function()
30794 this.getContainerWidth();
30796 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30797 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30801 var boxWidth = this.boxWidth + this.padWidth;
30803 if(this.containerWidth < this.boxWidth){
30804 boxWidth = this.containerWidth
30807 var containerWidth = this.containerWidth;
30809 var cols = Math.floor(containerWidth / boxWidth);
30811 this.cols = Math.max( cols, 1 );
30813 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30815 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30817 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30819 this.colWidth = boxWidth + avail - this.padWidth;
30821 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30822 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30825 horizontalMeasureColumns : function()
30827 this.getContainerWidth();
30829 var boxWidth = this.boxWidth;
30831 if(this.containerWidth < boxWidth){
30832 boxWidth = this.containerWidth;
30835 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30837 this.el.setHeight(boxWidth);
30841 getContainerWidth : function()
30843 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30846 layoutItems : function( isInstant )
30848 Roo.log(this.bricks);
30850 var items = Roo.apply([], this.bricks);
30852 if(this.isHorizontal){
30853 this._horizontalLayoutItems( items , isInstant );
30857 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30858 // this._verticalAlternativeLayoutItems( items , isInstant );
30862 this._verticalLayoutItems( items , isInstant );
30866 _verticalLayoutItems : function ( items , isInstant)
30868 if ( !items || !items.length ) {
30873 ['xs', 'xs', 'xs', 'tall'],
30874 ['xs', 'xs', 'tall'],
30875 ['xs', 'xs', 'sm'],
30876 ['xs', 'xs', 'xs'],
30882 ['sm', 'xs', 'xs'],
30886 ['tall', 'xs', 'xs', 'xs'],
30887 ['tall', 'xs', 'xs'],
30899 Roo.each(items, function(item, k){
30901 switch (item.size) {
30902 // these layouts take up a full box,
30913 boxes.push([item]);
30936 var filterPattern = function(box, length)
30944 var pattern = box.slice(0, length);
30948 Roo.each(pattern, function(i){
30949 format.push(i.size);
30952 Roo.each(standard, function(s){
30954 if(String(s) != String(format)){
30963 if(!match && length == 1){
30968 filterPattern(box, length - 1);
30972 queue.push(pattern);
30974 box = box.slice(length, box.length);
30976 filterPattern(box, 4);
30982 Roo.each(boxes, function(box, k){
30988 if(box.length == 1){
30993 filterPattern(box, 4);
30997 this._processVerticalLayoutQueue( queue, isInstant );
31001 // _verticalAlternativeLayoutItems : function( items , isInstant )
31003 // if ( !items || !items.length ) {
31007 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31011 _horizontalLayoutItems : function ( items , isInstant)
31013 if ( !items || !items.length || items.length < 3) {
31019 var eItems = items.slice(0, 3);
31021 items = items.slice(3, items.length);
31024 ['xs', 'xs', 'xs', 'wide'],
31025 ['xs', 'xs', 'wide'],
31026 ['xs', 'xs', 'sm'],
31027 ['xs', 'xs', 'xs'],
31033 ['sm', 'xs', 'xs'],
31037 ['wide', 'xs', 'xs', 'xs'],
31038 ['wide', 'xs', 'xs'],
31051 Roo.each(items, function(item, k){
31053 switch (item.size) {
31064 boxes.push([item]);
31088 var filterPattern = function(box, length)
31096 var pattern = box.slice(0, length);
31100 Roo.each(pattern, function(i){
31101 format.push(i.size);
31104 Roo.each(standard, function(s){
31106 if(String(s) != String(format)){
31115 if(!match && length == 1){
31120 filterPattern(box, length - 1);
31124 queue.push(pattern);
31126 box = box.slice(length, box.length);
31128 filterPattern(box, 4);
31134 Roo.each(boxes, function(box, k){
31140 if(box.length == 1){
31145 filterPattern(box, 4);
31152 var pos = this.el.getBox(true);
31156 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31158 var hit_end = false;
31160 Roo.each(queue, function(box){
31164 Roo.each(box, function(b){
31166 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31176 Roo.each(box, function(b){
31178 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31181 mx = Math.max(mx, b.x);
31185 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31189 Roo.each(box, function(b){
31191 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31205 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31208 /** Sets position of item in DOM
31209 * @param {Element} item
31210 * @param {Number} x - horizontal position
31211 * @param {Number} y - vertical position
31212 * @param {Boolean} isInstant - disables transitions
31214 _processVerticalLayoutQueue : function( queue, isInstant )
31216 var pos = this.el.getBox(true);
31221 for (var i = 0; i < this.cols; i++){
31225 Roo.each(queue, function(box, k){
31227 var col = k % this.cols;
31229 Roo.each(box, function(b,kk){
31231 b.el.position('absolute');
31233 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31234 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31236 if(b.size == 'md-left' || b.size == 'md-right'){
31237 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31238 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31241 b.el.setWidth(width);
31242 b.el.setHeight(height);
31244 b.el.select('iframe',true).setSize(width,height);
31248 for (var i = 0; i < this.cols; i++){
31250 if(maxY[i] < maxY[col]){
31255 col = Math.min(col, i);
31259 x = pos.x + col * (this.colWidth + this.padWidth);
31263 var positions = [];
31265 switch (box.length){
31267 positions = this.getVerticalOneBoxColPositions(x, y, box);
31270 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31273 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31276 positions = this.getVerticalFourBoxColPositions(x, y, box);
31282 Roo.each(box, function(b,kk){
31284 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31286 var sz = b.el.getSize();
31288 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31296 for (var i = 0; i < this.cols; i++){
31297 mY = Math.max(mY, maxY[i]);
31300 this.el.setHeight(mY - pos.y);
31304 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31306 // var pos = this.el.getBox(true);
31309 // var maxX = pos.right;
31311 // var maxHeight = 0;
31313 // Roo.each(items, function(item, k){
31317 // item.el.position('absolute');
31319 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31321 // item.el.setWidth(width);
31323 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31325 // item.el.setHeight(height);
31328 // item.el.setXY([x, y], isInstant ? false : true);
31330 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31333 // y = y + height + this.alternativePadWidth;
31335 // maxHeight = maxHeight + height + this.alternativePadWidth;
31339 // this.el.setHeight(maxHeight);
31343 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31345 var pos = this.el.getBox(true);
31350 var maxX = pos.right;
31352 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31354 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31356 Roo.each(queue, function(box, k){
31358 Roo.each(box, function(b, kk){
31360 b.el.position('absolute');
31362 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31363 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31365 if(b.size == 'md-left' || b.size == 'md-right'){
31366 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31367 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31370 b.el.setWidth(width);
31371 b.el.setHeight(height);
31379 var positions = [];
31381 switch (box.length){
31383 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31386 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31389 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31392 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31398 Roo.each(box, function(b,kk){
31400 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31402 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31410 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31412 Roo.each(eItems, function(b,k){
31414 b.size = (k == 0) ? 'sm' : 'xs';
31415 b.x = (k == 0) ? 2 : 1;
31416 b.y = (k == 0) ? 2 : 1;
31418 b.el.position('absolute');
31420 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31422 b.el.setWidth(width);
31424 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31426 b.el.setHeight(height);
31430 var positions = [];
31433 x : maxX - this.unitWidth * 2 - this.gutter,
31438 x : maxX - this.unitWidth,
31439 y : minY + (this.unitWidth + this.gutter) * 2
31443 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31447 Roo.each(eItems, function(b,k){
31449 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31455 getVerticalOneBoxColPositions : function(x, y, box)
31459 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31461 if(box[0].size == 'md-left'){
31465 if(box[0].size == 'md-right'){
31470 x : x + (this.unitWidth + this.gutter) * rand,
31477 getVerticalTwoBoxColPositions : function(x, y, box)
31481 if(box[0].size == 'xs'){
31485 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31489 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31503 x : x + (this.unitWidth + this.gutter) * 2,
31504 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31511 getVerticalThreeBoxColPositions : function(x, y, box)
31515 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31523 x : x + (this.unitWidth + this.gutter) * 1,
31528 x : x + (this.unitWidth + this.gutter) * 2,
31536 if(box[0].size == 'xs' && box[1].size == 'xs'){
31545 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31549 x : x + (this.unitWidth + this.gutter) * 1,
31563 x : x + (this.unitWidth + this.gutter) * 2,
31568 x : x + (this.unitWidth + this.gutter) * 2,
31569 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31576 getVerticalFourBoxColPositions : function(x, y, box)
31580 if(box[0].size == 'xs'){
31589 y : y + (this.unitHeight + this.gutter) * 1
31594 y : y + (this.unitHeight + this.gutter) * 2
31598 x : x + (this.unitWidth + this.gutter) * 1,
31612 x : x + (this.unitWidth + this.gutter) * 2,
31617 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31618 y : y + (this.unitHeight + this.gutter) * 1
31622 x : x + (this.unitWidth + this.gutter) * 2,
31623 y : y + (this.unitWidth + this.gutter) * 2
31630 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31634 if(box[0].size == 'md-left'){
31636 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31643 if(box[0].size == 'md-right'){
31645 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31646 y : minY + (this.unitWidth + this.gutter) * 1
31652 var rand = Math.floor(Math.random() * (4 - box[0].y));
31655 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31656 y : minY + (this.unitWidth + this.gutter) * rand
31663 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31667 if(box[0].size == 'xs'){
31670 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31675 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31676 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31684 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31689 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31690 y : minY + (this.unitWidth + this.gutter) * 2
31697 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31701 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31704 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31709 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31710 y : minY + (this.unitWidth + this.gutter) * 1
31714 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31715 y : minY + (this.unitWidth + this.gutter) * 2
31722 if(box[0].size == 'xs' && box[1].size == 'xs'){
31725 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31730 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31735 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31736 y : minY + (this.unitWidth + this.gutter) * 1
31744 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31749 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31750 y : minY + (this.unitWidth + this.gutter) * 2
31754 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31755 y : minY + (this.unitWidth + this.gutter) * 2
31762 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31766 if(box[0].size == 'xs'){
31769 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31774 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31779 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),
31784 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31785 y : minY + (this.unitWidth + this.gutter) * 1
31793 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31798 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31799 y : minY + (this.unitWidth + this.gutter) * 2
31803 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31804 y : minY + (this.unitWidth + this.gutter) * 2
31808 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),
31809 y : minY + (this.unitWidth + this.gutter) * 2
31817 * remove a Masonry Brick
31818 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31820 removeBrick : function(brick_id)
31826 for (var i = 0; i<this.bricks.length; i++) {
31827 if (this.bricks[i].id == brick_id) {
31828 this.bricks.splice(i,1);
31829 this.el.dom.removeChild(Roo.get(brick_id).dom);
31836 * adds a Masonry Brick
31837 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31839 addBrick : function(cfg)
31841 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31842 //this.register(cn);
31843 cn.parentId = this.id;
31844 cn.onRender(this.el, null);
31849 * register a Masonry Brick
31850 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31853 register : function(brick)
31855 this.bricks.push(brick);
31856 brick.masonryId = this.id;
31860 * clear all the Masonry Brick
31862 clearAll : function()
31865 //this.getChildContainer().dom.innerHTML = "";
31866 this.el.dom.innerHTML = '';
31869 getSelected : function()
31871 if (!this.selectedBrick) {
31875 return this.selectedBrick;
31879 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31883 * register a Masonry Layout
31884 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31887 register : function(layout)
31889 this.groups[layout.id] = layout;
31892 * fetch a Masonry Layout based on the masonry layout ID
31893 * @param {string} the masonry layout to add
31894 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31897 get: function(layout_id) {
31898 if (typeof(this.groups[layout_id]) == 'undefined') {
31901 return this.groups[layout_id] ;
31913 * http://masonry.desandro.com
31915 * The idea is to render all the bricks based on vertical width...
31917 * The original code extends 'outlayer' - we might need to use that....
31923 * @class Roo.bootstrap.LayoutMasonryAuto
31924 * @extends Roo.bootstrap.Component
31925 * Bootstrap Layout Masonry class
31928 * Create a new Element
31929 * @param {Object} config The config object
31932 Roo.bootstrap.LayoutMasonryAuto = function(config){
31933 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31936 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31939 * @cfg {Boolean} isFitWidth - resize the width..
31941 isFitWidth : false, // options..
31943 * @cfg {Boolean} isOriginLeft = left align?
31945 isOriginLeft : true,
31947 * @cfg {Boolean} isOriginTop = top align?
31949 isOriginTop : false,
31951 * @cfg {Boolean} isLayoutInstant = no animation?
31953 isLayoutInstant : false, // needed?
31955 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31957 isResizingContainer : true,
31959 * @cfg {Number} columnWidth width of the columns
31965 * @cfg {Number} maxCols maximum number of columns
31970 * @cfg {Number} padHeight padding below box..
31976 * @cfg {Boolean} isAutoInitial defalut true
31979 isAutoInitial : true,
31985 initialColumnWidth : 0,
31986 currentSize : null,
31988 colYs : null, // array.
31995 bricks: null, //CompositeElement
31996 cols : 0, // array?
31997 // element : null, // wrapped now this.el
31998 _isLayoutInited : null,
32001 getAutoCreate : function(){
32005 cls: 'blog-masonary-wrapper ' + this.cls,
32007 cls : 'mas-boxes masonary'
32014 getChildContainer: function( )
32016 if (this.boxesEl) {
32017 return this.boxesEl;
32020 this.boxesEl = this.el.select('.mas-boxes').first();
32022 return this.boxesEl;
32026 initEvents : function()
32030 if(this.isAutoInitial){
32031 Roo.log('hook children rendered');
32032 this.on('childrenrendered', function() {
32033 Roo.log('children rendered');
32040 initial : function()
32042 this.reloadItems();
32044 this.currentSize = this.el.getBox(true);
32046 /// was window resize... - let's see if this works..
32047 Roo.EventManager.onWindowResize(this.resize, this);
32049 if(!this.isAutoInitial){
32054 this.layout.defer(500,this);
32057 reloadItems: function()
32059 this.bricks = this.el.select('.masonry-brick', true);
32061 this.bricks.each(function(b) {
32062 //Roo.log(b.getSize());
32063 if (!b.attr('originalwidth')) {
32064 b.attr('originalwidth', b.getSize().width);
32069 Roo.log(this.bricks.elements.length);
32072 resize : function()
32075 var cs = this.el.getBox(true);
32077 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32078 Roo.log("no change in with or X");
32081 this.currentSize = cs;
32085 layout : function()
32088 this._resetLayout();
32089 //this._manageStamps();
32091 // don't animate first layout
32092 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32093 this.layoutItems( isInstant );
32095 // flag for initalized
32096 this._isLayoutInited = true;
32099 layoutItems : function( isInstant )
32101 //var items = this._getItemsForLayout( this.items );
32102 // original code supports filtering layout items.. we just ignore it..
32104 this._layoutItems( this.bricks , isInstant );
32106 this._postLayout();
32108 _layoutItems : function ( items , isInstant)
32110 //this.fireEvent( 'layout', this, items );
32113 if ( !items || !items.elements.length ) {
32114 // no items, emit event with empty array
32119 items.each(function(item) {
32120 Roo.log("layout item");
32122 // get x/y object from method
32123 var position = this._getItemLayoutPosition( item );
32125 position.item = item;
32126 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32127 queue.push( position );
32130 this._processLayoutQueue( queue );
32132 /** Sets position of item in DOM
32133 * @param {Element} item
32134 * @param {Number} x - horizontal position
32135 * @param {Number} y - vertical position
32136 * @param {Boolean} isInstant - disables transitions
32138 _processLayoutQueue : function( queue )
32140 for ( var i=0, len = queue.length; i < len; i++ ) {
32141 var obj = queue[i];
32142 obj.item.position('absolute');
32143 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32149 * Any logic you want to do after each layout,
32150 * i.e. size the container
32152 _postLayout : function()
32154 this.resizeContainer();
32157 resizeContainer : function()
32159 if ( !this.isResizingContainer ) {
32162 var size = this._getContainerSize();
32164 this.el.setSize(size.width,size.height);
32165 this.boxesEl.setSize(size.width,size.height);
32171 _resetLayout : function()
32173 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32174 this.colWidth = this.el.getWidth();
32175 //this.gutter = this.el.getWidth();
32177 this.measureColumns();
32183 this.colYs.push( 0 );
32189 measureColumns : function()
32191 this.getContainerWidth();
32192 // if columnWidth is 0, default to outerWidth of first item
32193 if ( !this.columnWidth ) {
32194 var firstItem = this.bricks.first();
32195 Roo.log(firstItem);
32196 this.columnWidth = this.containerWidth;
32197 if (firstItem && firstItem.attr('originalwidth') ) {
32198 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32200 // columnWidth fall back to item of first element
32201 Roo.log("set column width?");
32202 this.initialColumnWidth = this.columnWidth ;
32204 // if first elem has no width, default to size of container
32209 if (this.initialColumnWidth) {
32210 this.columnWidth = this.initialColumnWidth;
32215 // column width is fixed at the top - however if container width get's smaller we should
32218 // this bit calcs how man columns..
32220 var columnWidth = this.columnWidth += this.gutter;
32222 // calculate columns
32223 var containerWidth = this.containerWidth + this.gutter;
32225 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32226 // fix rounding errors, typically with gutters
32227 var excess = columnWidth - containerWidth % columnWidth;
32230 // if overshoot is less than a pixel, round up, otherwise floor it
32231 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32232 cols = Math[ mathMethod ]( cols );
32233 this.cols = Math.max( cols, 1 );
32234 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32236 // padding positioning..
32237 var totalColWidth = this.cols * this.columnWidth;
32238 var padavail = this.containerWidth - totalColWidth;
32239 // so for 2 columns - we need 3 'pads'
32241 var padNeeded = (1+this.cols) * this.padWidth;
32243 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32245 this.columnWidth += padExtra
32246 //this.padWidth = Math.floor(padavail / ( this.cols));
32248 // adjust colum width so that padding is fixed??
32250 // we have 3 columns ... total = width * 3
32251 // we have X left over... that should be used by
32253 //if (this.expandC) {
32261 getContainerWidth : function()
32263 /* // container is parent if fit width
32264 var container = this.isFitWidth ? this.element.parentNode : this.element;
32265 // check that this.size and size are there
32266 // IE8 triggers resize on body size change, so they might not be
32268 var size = getSize( container ); //FIXME
32269 this.containerWidth = size && size.innerWidth; //FIXME
32272 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32276 _getItemLayoutPosition : function( item ) // what is item?
32278 // we resize the item to our columnWidth..
32280 item.setWidth(this.columnWidth);
32281 item.autoBoxAdjust = false;
32283 var sz = item.getSize();
32285 // how many columns does this brick span
32286 var remainder = this.containerWidth % this.columnWidth;
32288 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32289 // round if off by 1 pixel, otherwise use ceil
32290 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32291 colSpan = Math.min( colSpan, this.cols );
32293 // normally this should be '1' as we dont' currently allow multi width columns..
32295 var colGroup = this._getColGroup( colSpan );
32296 // get the minimum Y value from the columns
32297 var minimumY = Math.min.apply( Math, colGroup );
32298 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32300 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32302 // position the brick
32304 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32305 y: this.currentSize.y + minimumY + this.padHeight
32309 // apply setHeight to necessary columns
32310 var setHeight = minimumY + sz.height + this.padHeight;
32311 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32313 var setSpan = this.cols + 1 - colGroup.length;
32314 for ( var i = 0; i < setSpan; i++ ) {
32315 this.colYs[ shortColIndex + i ] = setHeight ;
32322 * @param {Number} colSpan - number of columns the element spans
32323 * @returns {Array} colGroup
32325 _getColGroup : function( colSpan )
32327 if ( colSpan < 2 ) {
32328 // if brick spans only one column, use all the column Ys
32333 // how many different places could this brick fit horizontally
32334 var groupCount = this.cols + 1 - colSpan;
32335 // for each group potential horizontal position
32336 for ( var i = 0; i < groupCount; i++ ) {
32337 // make an array of colY values for that one group
32338 var groupColYs = this.colYs.slice( i, i + colSpan );
32339 // and get the max value of the array
32340 colGroup[i] = Math.max.apply( Math, groupColYs );
32345 _manageStamp : function( stamp )
32347 var stampSize = stamp.getSize();
32348 var offset = stamp.getBox();
32349 // get the columns that this stamp affects
32350 var firstX = this.isOriginLeft ? offset.x : offset.right;
32351 var lastX = firstX + stampSize.width;
32352 var firstCol = Math.floor( firstX / this.columnWidth );
32353 firstCol = Math.max( 0, firstCol );
32355 var lastCol = Math.floor( lastX / this.columnWidth );
32356 // lastCol should not go over if multiple of columnWidth #425
32357 lastCol -= lastX % this.columnWidth ? 0 : 1;
32358 lastCol = Math.min( this.cols - 1, lastCol );
32360 // set colYs to bottom of the stamp
32361 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32364 for ( var i = firstCol; i <= lastCol; i++ ) {
32365 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32370 _getContainerSize : function()
32372 this.maxY = Math.max.apply( Math, this.colYs );
32377 if ( this.isFitWidth ) {
32378 size.width = this._getContainerFitWidth();
32384 _getContainerFitWidth : function()
32386 var unusedCols = 0;
32387 // count unused columns
32390 if ( this.colYs[i] !== 0 ) {
32395 // fit container to columns that have been used
32396 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32399 needsResizeLayout : function()
32401 var previousWidth = this.containerWidth;
32402 this.getContainerWidth();
32403 return previousWidth !== this.containerWidth;
32418 * @class Roo.bootstrap.MasonryBrick
32419 * @extends Roo.bootstrap.Component
32420 * Bootstrap MasonryBrick class
32423 * Create a new MasonryBrick
32424 * @param {Object} config The config object
32427 Roo.bootstrap.MasonryBrick = function(config){
32429 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32431 Roo.bootstrap.MasonryBrick.register(this);
32437 * When a MasonryBrick is clcik
32438 * @param {Roo.bootstrap.MasonryBrick} this
32439 * @param {Roo.EventObject} e
32445 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32448 * @cfg {String} title
32452 * @cfg {String} html
32456 * @cfg {String} bgimage
32460 * @cfg {String} videourl
32464 * @cfg {String} cls
32468 * @cfg {String} href
32472 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32477 * @cfg {String} placetitle (center|bottom)
32482 * @cfg {Boolean} isFitContainer defalut true
32484 isFitContainer : true,
32487 * @cfg {Boolean} preventDefault defalut false
32489 preventDefault : false,
32492 * @cfg {Boolean} inverse defalut false
32494 maskInverse : false,
32496 getAutoCreate : function()
32498 if(!this.isFitContainer){
32499 return this.getSplitAutoCreate();
32502 var cls = 'masonry-brick masonry-brick-full';
32504 if(this.href.length){
32505 cls += ' masonry-brick-link';
32508 if(this.bgimage.length){
32509 cls += ' masonry-brick-image';
32512 if(this.maskInverse){
32513 cls += ' mask-inverse';
32516 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32517 cls += ' enable-mask';
32521 cls += ' masonry-' + this.size + '-brick';
32524 if(this.placetitle.length){
32526 switch (this.placetitle) {
32528 cls += ' masonry-center-title';
32531 cls += ' masonry-bottom-title';
32538 if(!this.html.length && !this.bgimage.length){
32539 cls += ' masonry-center-title';
32542 if(!this.html.length && this.bgimage.length){
32543 cls += ' masonry-bottom-title';
32548 cls += ' ' + this.cls;
32552 tag: (this.href.length) ? 'a' : 'div',
32557 cls: 'masonry-brick-mask'
32561 cls: 'masonry-brick-paragraph',
32567 if(this.href.length){
32568 cfg.href = this.href;
32571 var cn = cfg.cn[1].cn;
32573 if(this.title.length){
32576 cls: 'masonry-brick-title',
32581 if(this.html.length){
32584 cls: 'masonry-brick-text',
32589 if (!this.title.length && !this.html.length) {
32590 cfg.cn[1].cls += ' hide';
32593 if(this.bgimage.length){
32596 cls: 'masonry-brick-image-view',
32601 if(this.videourl.length){
32602 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32603 // youtube support only?
32606 cls: 'masonry-brick-image-view',
32609 allowfullscreen : true
32617 getSplitAutoCreate : function()
32619 var cls = 'masonry-brick masonry-brick-split';
32621 if(this.href.length){
32622 cls += ' masonry-brick-link';
32625 if(this.bgimage.length){
32626 cls += ' masonry-brick-image';
32630 cls += ' masonry-' + this.size + '-brick';
32633 switch (this.placetitle) {
32635 cls += ' masonry-center-title';
32638 cls += ' masonry-bottom-title';
32641 if(!this.bgimage.length){
32642 cls += ' masonry-center-title';
32645 if(this.bgimage.length){
32646 cls += ' masonry-bottom-title';
32652 cls += ' ' + this.cls;
32656 tag: (this.href.length) ? 'a' : 'div',
32661 cls: 'masonry-brick-split-head',
32665 cls: 'masonry-brick-paragraph',
32672 cls: 'masonry-brick-split-body',
32678 if(this.href.length){
32679 cfg.href = this.href;
32682 if(this.title.length){
32683 cfg.cn[0].cn[0].cn.push({
32685 cls: 'masonry-brick-title',
32690 if(this.html.length){
32691 cfg.cn[1].cn.push({
32693 cls: 'masonry-brick-text',
32698 if(this.bgimage.length){
32699 cfg.cn[0].cn.push({
32701 cls: 'masonry-brick-image-view',
32706 if(this.videourl.length){
32707 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32708 // youtube support only?
32709 cfg.cn[0].cn.cn.push({
32711 cls: 'masonry-brick-image-view',
32714 allowfullscreen : true
32721 initEvents: function()
32723 switch (this.size) {
32756 this.el.on('touchstart', this.onTouchStart, this);
32757 this.el.on('touchmove', this.onTouchMove, this);
32758 this.el.on('touchend', this.onTouchEnd, this);
32759 this.el.on('contextmenu', this.onContextMenu, this);
32761 this.el.on('mouseenter' ,this.enter, this);
32762 this.el.on('mouseleave', this.leave, this);
32763 this.el.on('click', this.onClick, this);
32766 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32767 this.parent().bricks.push(this);
32772 onClick: function(e, el)
32774 var time = this.endTimer - this.startTimer;
32775 // Roo.log(e.preventDefault());
32778 e.preventDefault();
32783 if(!this.preventDefault){
32787 e.preventDefault();
32789 if (this.activeClass != '') {
32790 this.selectBrick();
32793 this.fireEvent('click', this, e);
32796 enter: function(e, el)
32798 e.preventDefault();
32800 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32804 if(this.bgimage.length && this.html.length){
32805 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32809 leave: function(e, el)
32811 e.preventDefault();
32813 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32817 if(this.bgimage.length && this.html.length){
32818 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32822 onTouchStart: function(e, el)
32824 // e.preventDefault();
32826 this.touchmoved = false;
32828 if(!this.isFitContainer){
32832 if(!this.bgimage.length || !this.html.length){
32836 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32838 this.timer = new Date().getTime();
32842 onTouchMove: function(e, el)
32844 this.touchmoved = true;
32847 onContextMenu : function(e,el)
32849 e.preventDefault();
32850 e.stopPropagation();
32854 onTouchEnd: function(e, el)
32856 // e.preventDefault();
32858 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32865 if(!this.bgimage.length || !this.html.length){
32867 if(this.href.length){
32868 window.location.href = this.href;
32874 if(!this.isFitContainer){
32878 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32880 window.location.href = this.href;
32883 //selection on single brick only
32884 selectBrick : function() {
32886 if (!this.parentId) {
32890 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32891 var index = m.selectedBrick.indexOf(this.id);
32894 m.selectedBrick.splice(index,1);
32895 this.el.removeClass(this.activeClass);
32899 for(var i = 0; i < m.selectedBrick.length; i++) {
32900 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32901 b.el.removeClass(b.activeClass);
32904 m.selectedBrick = [];
32906 m.selectedBrick.push(this.id);
32907 this.el.addClass(this.activeClass);
32911 isSelected : function(){
32912 return this.el.hasClass(this.activeClass);
32917 Roo.apply(Roo.bootstrap.MasonryBrick, {
32920 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32922 * register a Masonry Brick
32923 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32926 register : function(brick)
32928 //this.groups[brick.id] = brick;
32929 this.groups.add(brick.id, brick);
32932 * fetch a masonry brick based on the masonry brick ID
32933 * @param {string} the masonry brick to add
32934 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32937 get: function(brick_id)
32939 // if (typeof(this.groups[brick_id]) == 'undefined') {
32942 // return this.groups[brick_id] ;
32944 if(this.groups.key(brick_id)) {
32945 return this.groups.key(brick_id);
32963 * @class Roo.bootstrap.Brick
32964 * @extends Roo.bootstrap.Component
32965 * Bootstrap Brick class
32968 * Create a new Brick
32969 * @param {Object} config The config object
32972 Roo.bootstrap.Brick = function(config){
32973 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32979 * When a Brick is click
32980 * @param {Roo.bootstrap.Brick} this
32981 * @param {Roo.EventObject} e
32987 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32990 * @cfg {String} title
32994 * @cfg {String} html
32998 * @cfg {String} bgimage
33002 * @cfg {String} cls
33006 * @cfg {String} href
33010 * @cfg {String} video
33014 * @cfg {Boolean} square
33018 getAutoCreate : function()
33020 var cls = 'roo-brick';
33022 if(this.href.length){
33023 cls += ' roo-brick-link';
33026 if(this.bgimage.length){
33027 cls += ' roo-brick-image';
33030 if(!this.html.length && !this.bgimage.length){
33031 cls += ' roo-brick-center-title';
33034 if(!this.html.length && this.bgimage.length){
33035 cls += ' roo-brick-bottom-title';
33039 cls += ' ' + this.cls;
33043 tag: (this.href.length) ? 'a' : 'div',
33048 cls: 'roo-brick-paragraph',
33054 if(this.href.length){
33055 cfg.href = this.href;
33058 var cn = cfg.cn[0].cn;
33060 if(this.title.length){
33063 cls: 'roo-brick-title',
33068 if(this.html.length){
33071 cls: 'roo-brick-text',
33078 if(this.bgimage.length){
33081 cls: 'roo-brick-image-view',
33089 initEvents: function()
33091 if(this.title.length || this.html.length){
33092 this.el.on('mouseenter' ,this.enter, this);
33093 this.el.on('mouseleave', this.leave, this);
33096 Roo.EventManager.onWindowResize(this.resize, this);
33098 if(this.bgimage.length){
33099 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33100 this.imageEl.on('load', this.onImageLoad, this);
33107 onImageLoad : function()
33112 resize : function()
33114 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33116 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33118 if(this.bgimage.length){
33119 var image = this.el.select('.roo-brick-image-view', true).first();
33121 image.setWidth(paragraph.getWidth());
33124 image.setHeight(paragraph.getWidth());
33127 this.el.setHeight(image.getHeight());
33128 paragraph.setHeight(image.getHeight());
33134 enter: function(e, el)
33136 e.preventDefault();
33138 if(this.bgimage.length){
33139 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33140 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33144 leave: function(e, el)
33146 e.preventDefault();
33148 if(this.bgimage.length){
33149 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33150 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33165 * @class Roo.bootstrap.NumberField
33166 * @extends Roo.bootstrap.Input
33167 * Bootstrap NumberField class
33173 * Create a new NumberField
33174 * @param {Object} config The config object
33177 Roo.bootstrap.NumberField = function(config){
33178 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33181 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33184 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33186 allowDecimals : true,
33188 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33190 decimalSeparator : ".",
33192 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33194 decimalPrecision : 2,
33196 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33198 allowNegative : true,
33201 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33205 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33207 minValue : Number.NEGATIVE_INFINITY,
33209 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33211 maxValue : Number.MAX_VALUE,
33213 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33215 minText : "The minimum value for this field is {0}",
33217 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33219 maxText : "The maximum value for this field is {0}",
33221 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33222 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33224 nanText : "{0} is not a valid number",
33226 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33230 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33232 thousandsDelimiter : false,
33234 * @cfg {String} valueAlign alignment of value
33236 valueAlign : "left",
33238 getAutoCreate : function()
33240 var hiddenInput = {
33244 cls: 'hidden-number-input'
33248 hiddenInput.name = this.name;
33253 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33255 this.name = hiddenInput.name;
33257 if(cfg.cn.length > 0) {
33258 cfg.cn.push(hiddenInput);
33265 initEvents : function()
33267 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33269 var allowed = "0123456789";
33271 if(this.allowDecimals){
33272 allowed += this.decimalSeparator;
33275 if(this.allowNegative){
33279 if(this.thousandsDelimiter) {
33283 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33285 var keyPress = function(e){
33287 var k = e.getKey();
33289 var c = e.getCharCode();
33292 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33293 allowed.indexOf(String.fromCharCode(c)) === -1
33299 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33303 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33308 this.el.on("keypress", keyPress, this);
33311 validateValue : function(value)
33314 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33318 var num = this.parseValue(value);
33321 this.markInvalid(String.format(this.nanText, value));
33325 if(num < this.minValue){
33326 this.markInvalid(String.format(this.minText, this.minValue));
33330 if(num > this.maxValue){
33331 this.markInvalid(String.format(this.maxText, this.maxValue));
33338 getValue : function()
33340 var v = this.hiddenEl().getValue();
33342 return this.fixPrecision(this.parseValue(v));
33345 parseValue : function(value)
33347 if(this.thousandsDelimiter) {
33349 r = new RegExp(",", "g");
33350 value = value.replace(r, "");
33353 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33354 return isNaN(value) ? '' : value;
33357 fixPrecision : function(value)
33359 if(this.thousandsDelimiter) {
33361 r = new RegExp(",", "g");
33362 value = value.replace(r, "");
33365 var nan = isNaN(value);
33367 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33368 return nan ? '' : value;
33370 return parseFloat(value).toFixed(this.decimalPrecision);
33373 setValue : function(v)
33375 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33381 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33383 this.inputEl().dom.value = (v == '') ? '' :
33384 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33386 if(!this.allowZero && v === '0') {
33387 this.hiddenEl().dom.value = '';
33388 this.inputEl().dom.value = '';
33395 decimalPrecisionFcn : function(v)
33397 return Math.floor(v);
33400 beforeBlur : function()
33406 var v = this.parseValue(this.getRawValue());
33413 hiddenEl : function()
33415 return this.el.select('input.hidden-number-input',true).first();
33427 * @class Roo.bootstrap.DocumentSlider
33428 * @extends Roo.bootstrap.Component
33429 * Bootstrap DocumentSlider class
33432 * Create a new DocumentViewer
33433 * @param {Object} config The config object
33436 Roo.bootstrap.DocumentSlider = function(config){
33437 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33444 * Fire after initEvent
33445 * @param {Roo.bootstrap.DocumentSlider} this
33450 * Fire after update
33451 * @param {Roo.bootstrap.DocumentSlider} this
33457 * @param {Roo.bootstrap.DocumentSlider} this
33463 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33469 getAutoCreate : function()
33473 cls : 'roo-document-slider',
33477 cls : 'roo-document-slider-header',
33481 cls : 'roo-document-slider-header-title'
33487 cls : 'roo-document-slider-body',
33491 cls : 'roo-document-slider-prev',
33495 cls : 'fa fa-chevron-left'
33501 cls : 'roo-document-slider-thumb',
33505 cls : 'roo-document-slider-image'
33511 cls : 'roo-document-slider-next',
33515 cls : 'fa fa-chevron-right'
33527 initEvents : function()
33529 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33530 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33532 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33533 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33535 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33536 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33538 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33539 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33541 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33542 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33544 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33545 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33547 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33548 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33550 this.thumbEl.on('click', this.onClick, this);
33552 this.prevIndicator.on('click', this.prev, this);
33554 this.nextIndicator.on('click', this.next, this);
33558 initial : function()
33560 if(this.files.length){
33561 this.indicator = 1;
33565 this.fireEvent('initial', this);
33568 update : function()
33570 this.imageEl.attr('src', this.files[this.indicator - 1]);
33572 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33574 this.prevIndicator.show();
33576 if(this.indicator == 1){
33577 this.prevIndicator.hide();
33580 this.nextIndicator.show();
33582 if(this.indicator == this.files.length){
33583 this.nextIndicator.hide();
33586 this.thumbEl.scrollTo('top');
33588 this.fireEvent('update', this);
33591 onClick : function(e)
33593 e.preventDefault();
33595 this.fireEvent('click', this);
33600 e.preventDefault();
33602 this.indicator = Math.max(1, this.indicator - 1);
33609 e.preventDefault();
33611 this.indicator = Math.min(this.files.length, this.indicator + 1);
33625 * @class Roo.bootstrap.RadioSet
33626 * @extends Roo.bootstrap.Input
33627 * Bootstrap RadioSet class
33628 * @cfg {String} indicatorpos (left|right) default left
33629 * @cfg {Boolean} inline (true|false) inline the element (default true)
33630 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33632 * Create a new RadioSet
33633 * @param {Object} config The config object
33636 Roo.bootstrap.RadioSet = function(config){
33638 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33642 Roo.bootstrap.RadioSet.register(this);
33647 * Fires when the element is checked or unchecked.
33648 * @param {Roo.bootstrap.RadioSet} this This radio
33649 * @param {Roo.bootstrap.Radio} item The checked item
33654 * Fires when the element is click.
33655 * @param {Roo.bootstrap.RadioSet} this This radio set
33656 * @param {Roo.bootstrap.Radio} item The checked item
33657 * @param {Roo.EventObject} e The event object
33664 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33672 indicatorpos : 'left',
33674 getAutoCreate : function()
33678 cls : 'roo-radio-set-label',
33682 html : this.fieldLabel
33687 if(this.indicatorpos == 'left'){
33690 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33691 tooltip : 'This field is required'
33696 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33697 tooltip : 'This field is required'
33703 cls : 'roo-radio-set-items'
33706 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33708 if (align === 'left' && this.fieldLabel.length) {
33711 cls : "roo-radio-set-right",
33717 if(this.labelWidth > 12){
33718 label.style = "width: " + this.labelWidth + 'px';
33721 if(this.labelWidth < 13 && this.labelmd == 0){
33722 this.labelmd = this.labelWidth;
33725 if(this.labellg > 0){
33726 label.cls += ' col-lg-' + this.labellg;
33727 items.cls += ' col-lg-' + (12 - this.labellg);
33730 if(this.labelmd > 0){
33731 label.cls += ' col-md-' + this.labelmd;
33732 items.cls += ' col-md-' + (12 - this.labelmd);
33735 if(this.labelsm > 0){
33736 label.cls += ' col-sm-' + this.labelsm;
33737 items.cls += ' col-sm-' + (12 - this.labelsm);
33740 if(this.labelxs > 0){
33741 label.cls += ' col-xs-' + this.labelxs;
33742 items.cls += ' col-xs-' + (12 - this.labelxs);
33748 cls : 'roo-radio-set',
33752 cls : 'roo-radio-set-input',
33755 value : this.value ? this.value : ''
33762 if(this.weight.length){
33763 cfg.cls += ' roo-radio-' + this.weight;
33767 cfg.cls += ' roo-radio-set-inline';
33771 ['xs','sm','md','lg'].map(function(size){
33772 if (settings[size]) {
33773 cfg.cls += ' col-' + size + '-' + settings[size];
33781 initEvents : function()
33783 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33784 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33786 if(!this.fieldLabel.length){
33787 this.labelEl.hide();
33790 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33791 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33793 this.indicator = this.indicatorEl();
33795 if(this.indicator){
33796 this.indicator.addClass('invisible');
33799 this.originalValue = this.getValue();
33803 inputEl: function ()
33805 return this.el.select('.roo-radio-set-input', true).first();
33808 getChildContainer : function()
33810 return this.itemsEl;
33813 register : function(item)
33815 this.radioes.push(item);
33819 validate : function()
33821 if(this.getVisibilityEl().hasClass('hidden')){
33827 Roo.each(this.radioes, function(i){
33836 if(this.allowBlank) {
33840 if(this.disabled || valid){
33845 this.markInvalid();
33850 markValid : function()
33852 if(this.labelEl.isVisible(true)){
33853 this.indicatorEl().removeClass('visible');
33854 this.indicatorEl().addClass('invisible');
33857 this.el.removeClass([this.invalidClass, this.validClass]);
33858 this.el.addClass(this.validClass);
33860 this.fireEvent('valid', this);
33863 markInvalid : function(msg)
33865 if(this.allowBlank || this.disabled){
33869 if(this.labelEl.isVisible(true)){
33870 this.indicatorEl().removeClass('invisible');
33871 this.indicatorEl().addClass('visible');
33874 this.el.removeClass([this.invalidClass, this.validClass]);
33875 this.el.addClass(this.invalidClass);
33877 this.fireEvent('invalid', this, msg);
33881 setValue : function(v, suppressEvent)
33883 if(this.value === v){
33890 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33893 Roo.each(this.radioes, function(i){
33895 i.el.removeClass('checked');
33898 Roo.each(this.radioes, function(i){
33900 if(i.value === v || i.value.toString() === v.toString()){
33902 i.el.addClass('checked');
33904 if(suppressEvent !== true){
33905 this.fireEvent('check', this, i);
33916 clearInvalid : function(){
33918 if(!this.el || this.preventMark){
33922 this.el.removeClass([this.invalidClass]);
33924 this.fireEvent('valid', this);
33929 Roo.apply(Roo.bootstrap.RadioSet, {
33933 register : function(set)
33935 this.groups[set.name] = set;
33938 get: function(name)
33940 if (typeof(this.groups[name]) == 'undefined') {
33944 return this.groups[name] ;
33950 * Ext JS Library 1.1.1
33951 * Copyright(c) 2006-2007, Ext JS, LLC.
33953 * Originally Released Under LGPL - original licence link has changed is not relivant.
33956 * <script type="text/javascript">
33961 * @class Roo.bootstrap.SplitBar
33962 * @extends Roo.util.Observable
33963 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33967 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33968 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33969 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33970 split.minSize = 100;
33971 split.maxSize = 600;
33972 split.animate = true;
33973 split.on('moved', splitterMoved);
33976 * Create a new SplitBar
33977 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33978 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33979 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33980 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33981 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33982 position of the SplitBar).
33984 Roo.bootstrap.SplitBar = function(cfg){
33989 // dragElement : elm
33990 // resizingElement: el,
33992 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33993 // placement : Roo.bootstrap.SplitBar.LEFT ,
33994 // existingProxy ???
33997 this.el = Roo.get(cfg.dragElement, true);
33998 this.el.dom.unselectable = "on";
34000 this.resizingEl = Roo.get(cfg.resizingElement, true);
34004 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34005 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34008 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34011 * The minimum size of the resizing element. (Defaults to 0)
34017 * The maximum size of the resizing element. (Defaults to 2000)
34020 this.maxSize = 2000;
34023 * Whether to animate the transition to the new size
34026 this.animate = false;
34029 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34032 this.useShim = false;
34037 if(!cfg.existingProxy){
34039 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34041 this.proxy = Roo.get(cfg.existingProxy).dom;
34044 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34047 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34050 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34053 this.dragSpecs = {};
34056 * @private The adapter to use to positon and resize elements
34058 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34059 this.adapter.init(this);
34061 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34063 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34064 this.el.addClass("roo-splitbar-h");
34067 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34068 this.el.addClass("roo-splitbar-v");
34074 * Fires when the splitter is moved (alias for {@link #event-moved})
34075 * @param {Roo.bootstrap.SplitBar} this
34076 * @param {Number} newSize the new width or height
34081 * Fires when the splitter is moved
34082 * @param {Roo.bootstrap.SplitBar} this
34083 * @param {Number} newSize the new width or height
34087 * @event beforeresize
34088 * Fires before the splitter is dragged
34089 * @param {Roo.bootstrap.SplitBar} this
34091 "beforeresize" : true,
34093 "beforeapply" : true
34096 Roo.util.Observable.call(this);
34099 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34100 onStartProxyDrag : function(x, y){
34101 this.fireEvent("beforeresize", this);
34103 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34105 o.enableDisplayMode("block");
34106 // all splitbars share the same overlay
34107 Roo.bootstrap.SplitBar.prototype.overlay = o;
34109 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34110 this.overlay.show();
34111 Roo.get(this.proxy).setDisplayed("block");
34112 var size = this.adapter.getElementSize(this);
34113 this.activeMinSize = this.getMinimumSize();;
34114 this.activeMaxSize = this.getMaximumSize();;
34115 var c1 = size - this.activeMinSize;
34116 var c2 = Math.max(this.activeMaxSize - size, 0);
34117 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34118 this.dd.resetConstraints();
34119 this.dd.setXConstraint(
34120 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34121 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34123 this.dd.setYConstraint(0, 0);
34125 this.dd.resetConstraints();
34126 this.dd.setXConstraint(0, 0);
34127 this.dd.setYConstraint(
34128 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34129 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34132 this.dragSpecs.startSize = size;
34133 this.dragSpecs.startPoint = [x, y];
34134 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34138 * @private Called after the drag operation by the DDProxy
34140 onEndProxyDrag : function(e){
34141 Roo.get(this.proxy).setDisplayed(false);
34142 var endPoint = Roo.lib.Event.getXY(e);
34144 this.overlay.hide();
34147 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34148 newSize = this.dragSpecs.startSize +
34149 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34150 endPoint[0] - this.dragSpecs.startPoint[0] :
34151 this.dragSpecs.startPoint[0] - endPoint[0]
34154 newSize = this.dragSpecs.startSize +
34155 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34156 endPoint[1] - this.dragSpecs.startPoint[1] :
34157 this.dragSpecs.startPoint[1] - endPoint[1]
34160 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34161 if(newSize != this.dragSpecs.startSize){
34162 if(this.fireEvent('beforeapply', this, newSize) !== false){
34163 this.adapter.setElementSize(this, newSize);
34164 this.fireEvent("moved", this, newSize);
34165 this.fireEvent("resize", this, newSize);
34171 * Get the adapter this SplitBar uses
34172 * @return The adapter object
34174 getAdapter : function(){
34175 return this.adapter;
34179 * Set the adapter this SplitBar uses
34180 * @param {Object} adapter A SplitBar adapter object
34182 setAdapter : function(adapter){
34183 this.adapter = adapter;
34184 this.adapter.init(this);
34188 * Gets the minimum size for the resizing element
34189 * @return {Number} The minimum size
34191 getMinimumSize : function(){
34192 return this.minSize;
34196 * Sets the minimum size for the resizing element
34197 * @param {Number} minSize The minimum size
34199 setMinimumSize : function(minSize){
34200 this.minSize = minSize;
34204 * Gets the maximum size for the resizing element
34205 * @return {Number} The maximum size
34207 getMaximumSize : function(){
34208 return this.maxSize;
34212 * Sets the maximum size for the resizing element
34213 * @param {Number} maxSize The maximum size
34215 setMaximumSize : function(maxSize){
34216 this.maxSize = maxSize;
34220 * Sets the initialize size for the resizing element
34221 * @param {Number} size The initial size
34223 setCurrentSize : function(size){
34224 var oldAnimate = this.animate;
34225 this.animate = false;
34226 this.adapter.setElementSize(this, size);
34227 this.animate = oldAnimate;
34231 * Destroy this splitbar.
34232 * @param {Boolean} removeEl True to remove the element
34234 destroy : function(removeEl){
34236 this.shim.remove();
34239 this.proxy.parentNode.removeChild(this.proxy);
34247 * @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.
34249 Roo.bootstrap.SplitBar.createProxy = function(dir){
34250 var proxy = new Roo.Element(document.createElement("div"));
34251 proxy.unselectable();
34252 var cls = 'roo-splitbar-proxy';
34253 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34254 document.body.appendChild(proxy.dom);
34259 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34260 * Default Adapter. It assumes the splitter and resizing element are not positioned
34261 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34263 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34266 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34267 // do nothing for now
34268 init : function(s){
34272 * Called before drag operations to get the current size of the resizing element.
34273 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34275 getElementSize : function(s){
34276 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34277 return s.resizingEl.getWidth();
34279 return s.resizingEl.getHeight();
34284 * Called after drag operations to set the size of the resizing element.
34285 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34286 * @param {Number} newSize The new size to set
34287 * @param {Function} onComplete A function to be invoked when resizing is complete
34289 setElementSize : function(s, newSize, onComplete){
34290 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34292 s.resizingEl.setWidth(newSize);
34294 onComplete(s, newSize);
34297 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34302 s.resizingEl.setHeight(newSize);
34304 onComplete(s, newSize);
34307 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34314 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34315 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34316 * Adapter that moves the splitter element to align with the resized sizing element.
34317 * Used with an absolute positioned SplitBar.
34318 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34319 * document.body, make sure you assign an id to the body element.
34321 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34322 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34323 this.container = Roo.get(container);
34326 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34327 init : function(s){
34328 this.basic.init(s);
34331 getElementSize : function(s){
34332 return this.basic.getElementSize(s);
34335 setElementSize : function(s, newSize, onComplete){
34336 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34339 moveSplitter : function(s){
34340 var yes = Roo.bootstrap.SplitBar;
34341 switch(s.placement){
34343 s.el.setX(s.resizingEl.getRight());
34346 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34349 s.el.setY(s.resizingEl.getBottom());
34352 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34359 * Orientation constant - Create a vertical SplitBar
34363 Roo.bootstrap.SplitBar.VERTICAL = 1;
34366 * Orientation constant - Create a horizontal SplitBar
34370 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34373 * Placement constant - The resizing element is to the left of the splitter element
34377 Roo.bootstrap.SplitBar.LEFT = 1;
34380 * Placement constant - The resizing element is to the right of the splitter element
34384 Roo.bootstrap.SplitBar.RIGHT = 2;
34387 * Placement constant - The resizing element is positioned above the splitter element
34391 Roo.bootstrap.SplitBar.TOP = 3;
34394 * Placement constant - The resizing element is positioned under splitter element
34398 Roo.bootstrap.SplitBar.BOTTOM = 4;
34399 Roo.namespace("Roo.bootstrap.layout");/*
34401 * Ext JS Library 1.1.1
34402 * Copyright(c) 2006-2007, Ext JS, LLC.
34404 * Originally Released Under LGPL - original licence link has changed is not relivant.
34407 * <script type="text/javascript">
34411 * @class Roo.bootstrap.layout.Manager
34412 * @extends Roo.bootstrap.Component
34413 * Base class for layout managers.
34415 Roo.bootstrap.layout.Manager = function(config)
34417 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34423 /** false to disable window resize monitoring @type Boolean */
34424 this.monitorWindowResize = true;
34429 * Fires when a layout is performed.
34430 * @param {Roo.LayoutManager} this
34434 * @event regionresized
34435 * Fires when the user resizes a region.
34436 * @param {Roo.LayoutRegion} region The resized region
34437 * @param {Number} newSize The new size (width for east/west, height for north/south)
34439 "regionresized" : true,
34441 * @event regioncollapsed
34442 * Fires when a region is collapsed.
34443 * @param {Roo.LayoutRegion} region The collapsed region
34445 "regioncollapsed" : true,
34447 * @event regionexpanded
34448 * Fires when a region is expanded.
34449 * @param {Roo.LayoutRegion} region The expanded region
34451 "regionexpanded" : true
34453 this.updating = false;
34456 this.el = Roo.get(config.el);
34462 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34467 monitorWindowResize : true,
34473 onRender : function(ct, position)
34476 this.el = Roo.get(ct);
34479 //this.fireEvent('render',this);
34483 initEvents: function()
34487 // ie scrollbar fix
34488 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34489 document.body.scroll = "no";
34490 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34491 this.el.position('relative');
34493 this.id = this.el.id;
34494 this.el.addClass("roo-layout-container");
34495 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34496 if(this.el.dom != document.body ) {
34497 this.el.on('resize', this.layout,this);
34498 this.el.on('show', this.layout,this);
34504 * Returns true if this layout is currently being updated
34505 * @return {Boolean}
34507 isUpdating : function(){
34508 return this.updating;
34512 * Suspend the LayoutManager from doing auto-layouts while
34513 * making multiple add or remove calls
34515 beginUpdate : function(){
34516 this.updating = true;
34520 * Restore auto-layouts and optionally disable the manager from performing a layout
34521 * @param {Boolean} noLayout true to disable a layout update
34523 endUpdate : function(noLayout){
34524 this.updating = false;
34530 layout: function(){
34534 onRegionResized : function(region, newSize){
34535 this.fireEvent("regionresized", region, newSize);
34539 onRegionCollapsed : function(region){
34540 this.fireEvent("regioncollapsed", region);
34543 onRegionExpanded : function(region){
34544 this.fireEvent("regionexpanded", region);
34548 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34549 * performs box-model adjustments.
34550 * @return {Object} The size as an object {width: (the width), height: (the height)}
34552 getViewSize : function()
34555 if(this.el.dom != document.body){
34556 size = this.el.getSize();
34558 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34560 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34561 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34566 * Returns the Element this layout is bound to.
34567 * @return {Roo.Element}
34569 getEl : function(){
34574 * Returns the specified region.
34575 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34576 * @return {Roo.LayoutRegion}
34578 getRegion : function(target){
34579 return this.regions[target.toLowerCase()];
34582 onWindowResize : function(){
34583 if(this.monitorWindowResize){
34590 * Ext JS Library 1.1.1
34591 * Copyright(c) 2006-2007, Ext JS, LLC.
34593 * Originally Released Under LGPL - original licence link has changed is not relivant.
34596 * <script type="text/javascript">
34599 * @class Roo.bootstrap.layout.Border
34600 * @extends Roo.bootstrap.layout.Manager
34601 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34602 * please see: examples/bootstrap/nested.html<br><br>
34604 <b>The container the layout is rendered into can be either the body element or any other element.
34605 If it is not the body element, the container needs to either be an absolute positioned element,
34606 or you will need to add "position:relative" to the css of the container. You will also need to specify
34607 the container size if it is not the body element.</b>
34610 * Create a new Border
34611 * @param {Object} config Configuration options
34613 Roo.bootstrap.layout.Border = function(config){
34614 config = config || {};
34615 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34619 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34620 if(config[region]){
34621 config[region].region = region;
34622 this.addRegion(config[region]);
34628 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34630 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34632 * Creates and adds a new region if it doesn't already exist.
34633 * @param {String} target The target region key (north, south, east, west or center).
34634 * @param {Object} config The regions config object
34635 * @return {BorderLayoutRegion} The new region
34637 addRegion : function(config)
34639 if(!this.regions[config.region]){
34640 var r = this.factory(config);
34641 this.bindRegion(r);
34643 return this.regions[config.region];
34647 bindRegion : function(r){
34648 this.regions[r.config.region] = r;
34650 r.on("visibilitychange", this.layout, this);
34651 r.on("paneladded", this.layout, this);
34652 r.on("panelremoved", this.layout, this);
34653 r.on("invalidated", this.layout, this);
34654 r.on("resized", this.onRegionResized, this);
34655 r.on("collapsed", this.onRegionCollapsed, this);
34656 r.on("expanded", this.onRegionExpanded, this);
34660 * Performs a layout update.
34662 layout : function()
34664 if(this.updating) {
34668 // render all the rebions if they have not been done alreayd?
34669 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34670 if(this.regions[region] && !this.regions[region].bodyEl){
34671 this.regions[region].onRender(this.el)
34675 var size = this.getViewSize();
34676 var w = size.width;
34677 var h = size.height;
34682 //var x = 0, y = 0;
34684 var rs = this.regions;
34685 var north = rs["north"];
34686 var south = rs["south"];
34687 var west = rs["west"];
34688 var east = rs["east"];
34689 var center = rs["center"];
34690 //if(this.hideOnLayout){ // not supported anymore
34691 //c.el.setStyle("display", "none");
34693 if(north && north.isVisible()){
34694 var b = north.getBox();
34695 var m = north.getMargins();
34696 b.width = w - (m.left+m.right);
34699 centerY = b.height + b.y + m.bottom;
34700 centerH -= centerY;
34701 north.updateBox(this.safeBox(b));
34703 if(south && south.isVisible()){
34704 var b = south.getBox();
34705 var m = south.getMargins();
34706 b.width = w - (m.left+m.right);
34708 var totalHeight = (b.height + m.top + m.bottom);
34709 b.y = h - totalHeight + m.top;
34710 centerH -= totalHeight;
34711 south.updateBox(this.safeBox(b));
34713 if(west && west.isVisible()){
34714 var b = west.getBox();
34715 var m = west.getMargins();
34716 b.height = centerH - (m.top+m.bottom);
34718 b.y = centerY + m.top;
34719 var totalWidth = (b.width + m.left + m.right);
34720 centerX += totalWidth;
34721 centerW -= totalWidth;
34722 west.updateBox(this.safeBox(b));
34724 if(east && east.isVisible()){
34725 var b = east.getBox();
34726 var m = east.getMargins();
34727 b.height = centerH - (m.top+m.bottom);
34728 var totalWidth = (b.width + m.left + m.right);
34729 b.x = w - totalWidth + m.left;
34730 b.y = centerY + m.top;
34731 centerW -= totalWidth;
34732 east.updateBox(this.safeBox(b));
34735 var m = center.getMargins();
34737 x: centerX + m.left,
34738 y: centerY + m.top,
34739 width: centerW - (m.left+m.right),
34740 height: centerH - (m.top+m.bottom)
34742 //if(this.hideOnLayout){
34743 //center.el.setStyle("display", "block");
34745 center.updateBox(this.safeBox(centerBox));
34748 this.fireEvent("layout", this);
34752 safeBox : function(box){
34753 box.width = Math.max(0, box.width);
34754 box.height = Math.max(0, box.height);
34759 * Adds a ContentPanel (or subclass) to this layout.
34760 * @param {String} target The target region key (north, south, east, west or center).
34761 * @param {Roo.ContentPanel} panel The panel to add
34762 * @return {Roo.ContentPanel} The added panel
34764 add : function(target, panel){
34766 target = target.toLowerCase();
34767 return this.regions[target].add(panel);
34771 * Remove a ContentPanel (or subclass) to this layout.
34772 * @param {String} target The target region key (north, south, east, west or center).
34773 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34774 * @return {Roo.ContentPanel} The removed panel
34776 remove : function(target, panel){
34777 target = target.toLowerCase();
34778 return this.regions[target].remove(panel);
34782 * Searches all regions for a panel with the specified id
34783 * @param {String} panelId
34784 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34786 findPanel : function(panelId){
34787 var rs = this.regions;
34788 for(var target in rs){
34789 if(typeof rs[target] != "function"){
34790 var p = rs[target].getPanel(panelId);
34800 * Searches all regions for a panel with the specified id and activates (shows) it.
34801 * @param {String/ContentPanel} panelId The panels id or the panel itself
34802 * @return {Roo.ContentPanel} The shown panel or null
34804 showPanel : function(panelId) {
34805 var rs = this.regions;
34806 for(var target in rs){
34807 var r = rs[target];
34808 if(typeof r != "function"){
34809 if(r.hasPanel(panelId)){
34810 return r.showPanel(panelId);
34818 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34819 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34822 restoreState : function(provider){
34824 provider = Roo.state.Manager;
34826 var sm = new Roo.LayoutStateManager();
34827 sm.init(this, provider);
34833 * Adds a xtype elements to the layout.
34837 xtype : 'ContentPanel',
34844 xtype : 'NestedLayoutPanel',
34850 items : [ ... list of content panels or nested layout panels.. ]
34854 * @param {Object} cfg Xtype definition of item to add.
34856 addxtype : function(cfg)
34858 // basically accepts a pannel...
34859 // can accept a layout region..!?!?
34860 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34863 // theory? children can only be panels??
34865 //if (!cfg.xtype.match(/Panel$/)) {
34870 if (typeof(cfg.region) == 'undefined') {
34871 Roo.log("Failed to add Panel, region was not set");
34875 var region = cfg.region;
34881 xitems = cfg.items;
34888 case 'Content': // ContentPanel (el, cfg)
34889 case 'Scroll': // ContentPanel (el, cfg)
34891 cfg.autoCreate = true;
34892 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34894 // var el = this.el.createChild();
34895 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34898 this.add(region, ret);
34902 case 'TreePanel': // our new panel!
34903 cfg.el = this.el.createChild();
34904 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34905 this.add(region, ret);
34910 // create a new Layout (which is a Border Layout...
34912 var clayout = cfg.layout;
34913 clayout.el = this.el.createChild();
34914 clayout.items = clayout.items || [];
34918 // replace this exitems with the clayout ones..
34919 xitems = clayout.items;
34921 // force background off if it's in center...
34922 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34923 cfg.background = false;
34925 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34928 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34929 //console.log('adding nested layout panel ' + cfg.toSource());
34930 this.add(region, ret);
34931 nb = {}; /// find first...
34936 // needs grid and region
34938 //var el = this.getRegion(region).el.createChild();
34940 *var el = this.el.createChild();
34941 // create the grid first...
34942 cfg.grid.container = el;
34943 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34946 if (region == 'center' && this.active ) {
34947 cfg.background = false;
34950 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34952 this.add(region, ret);
34954 if (cfg.background) {
34955 // render grid on panel activation (if panel background)
34956 ret.on('activate', function(gp) {
34957 if (!gp.grid.rendered) {
34958 // gp.grid.render(el);
34962 // cfg.grid.render(el);
34968 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34969 // it was the old xcomponent building that caused this before.
34970 // espeically if border is the top element in the tree.
34980 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34982 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34983 this.add(region, ret);
34987 throw "Can not add '" + cfg.xtype + "' to Border";
34993 this.beginUpdate();
34997 Roo.each(xitems, function(i) {
34998 region = nb && i.region ? i.region : false;
35000 var add = ret.addxtype(i);
35003 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35004 if (!i.background) {
35005 abn[region] = nb[region] ;
35012 // make the last non-background panel active..
35013 //if (nb) { Roo.log(abn); }
35016 for(var r in abn) {
35017 region = this.getRegion(r);
35019 // tried using nb[r], but it does not work..
35021 region.showPanel(abn[r]);
35032 factory : function(cfg)
35035 var validRegions = Roo.bootstrap.layout.Border.regions;
35037 var target = cfg.region;
35040 var r = Roo.bootstrap.layout;
35044 return new r.North(cfg);
35046 return new r.South(cfg);
35048 return new r.East(cfg);
35050 return new r.West(cfg);
35052 return new r.Center(cfg);
35054 throw 'Layout region "'+target+'" not supported.';
35061 * Ext JS Library 1.1.1
35062 * Copyright(c) 2006-2007, Ext JS, LLC.
35064 * Originally Released Under LGPL - original licence link has changed is not relivant.
35067 * <script type="text/javascript">
35071 * @class Roo.bootstrap.layout.Basic
35072 * @extends Roo.util.Observable
35073 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35074 * and does not have a titlebar, tabs or any other features. All it does is size and position
35075 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35076 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35077 * @cfg {string} region the region that it inhabits..
35078 * @cfg {bool} skipConfig skip config?
35082 Roo.bootstrap.layout.Basic = function(config){
35084 this.mgr = config.mgr;
35086 this.position = config.region;
35088 var skipConfig = config.skipConfig;
35092 * @scope Roo.BasicLayoutRegion
35096 * @event beforeremove
35097 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35098 * @param {Roo.LayoutRegion} this
35099 * @param {Roo.ContentPanel} panel The panel
35100 * @param {Object} e The cancel event object
35102 "beforeremove" : true,
35104 * @event invalidated
35105 * Fires when the layout for this region is changed.
35106 * @param {Roo.LayoutRegion} this
35108 "invalidated" : true,
35110 * @event visibilitychange
35111 * Fires when this region is shown or hidden
35112 * @param {Roo.LayoutRegion} this
35113 * @param {Boolean} visibility true or false
35115 "visibilitychange" : true,
35117 * @event paneladded
35118 * Fires when a panel is added.
35119 * @param {Roo.LayoutRegion} this
35120 * @param {Roo.ContentPanel} panel The panel
35122 "paneladded" : true,
35124 * @event panelremoved
35125 * Fires when a panel is removed.
35126 * @param {Roo.LayoutRegion} this
35127 * @param {Roo.ContentPanel} panel The panel
35129 "panelremoved" : true,
35131 * @event beforecollapse
35132 * Fires when this region before collapse.
35133 * @param {Roo.LayoutRegion} this
35135 "beforecollapse" : true,
35138 * Fires when this region is collapsed.
35139 * @param {Roo.LayoutRegion} this
35141 "collapsed" : true,
35144 * Fires when this region is expanded.
35145 * @param {Roo.LayoutRegion} this
35150 * Fires when this region is slid into view.
35151 * @param {Roo.LayoutRegion} this
35153 "slideshow" : true,
35156 * Fires when this region slides out of view.
35157 * @param {Roo.LayoutRegion} this
35159 "slidehide" : true,
35161 * @event panelactivated
35162 * Fires when a panel is activated.
35163 * @param {Roo.LayoutRegion} this
35164 * @param {Roo.ContentPanel} panel The activated panel
35166 "panelactivated" : true,
35169 * Fires when the user resizes this region.
35170 * @param {Roo.LayoutRegion} this
35171 * @param {Number} newSize The new size (width for east/west, height for north/south)
35175 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35176 this.panels = new Roo.util.MixedCollection();
35177 this.panels.getKey = this.getPanelId.createDelegate(this);
35179 this.activePanel = null;
35180 // ensure listeners are added...
35182 if (config.listeners || config.events) {
35183 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35184 listeners : config.listeners || {},
35185 events : config.events || {}
35189 if(skipConfig !== true){
35190 this.applyConfig(config);
35194 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35196 getPanelId : function(p){
35200 applyConfig : function(config){
35201 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35202 this.config = config;
35207 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35208 * the width, for horizontal (north, south) the height.
35209 * @param {Number} newSize The new width or height
35211 resizeTo : function(newSize){
35212 var el = this.el ? this.el :
35213 (this.activePanel ? this.activePanel.getEl() : null);
35215 switch(this.position){
35218 el.setWidth(newSize);
35219 this.fireEvent("resized", this, newSize);
35223 el.setHeight(newSize);
35224 this.fireEvent("resized", this, newSize);
35230 getBox : function(){
35231 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35234 getMargins : function(){
35235 return this.margins;
35238 updateBox : function(box){
35240 var el = this.activePanel.getEl();
35241 el.dom.style.left = box.x + "px";
35242 el.dom.style.top = box.y + "px";
35243 this.activePanel.setSize(box.width, box.height);
35247 * Returns the container element for this region.
35248 * @return {Roo.Element}
35250 getEl : function(){
35251 return this.activePanel;
35255 * Returns true if this region is currently visible.
35256 * @return {Boolean}
35258 isVisible : function(){
35259 return this.activePanel ? true : false;
35262 setActivePanel : function(panel){
35263 panel = this.getPanel(panel);
35264 if(this.activePanel && this.activePanel != panel){
35265 this.activePanel.setActiveState(false);
35266 this.activePanel.getEl().setLeftTop(-10000,-10000);
35268 this.activePanel = panel;
35269 panel.setActiveState(true);
35271 panel.setSize(this.box.width, this.box.height);
35273 this.fireEvent("panelactivated", this, panel);
35274 this.fireEvent("invalidated");
35278 * Show the specified panel.
35279 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35280 * @return {Roo.ContentPanel} The shown panel or null
35282 showPanel : function(panel){
35283 panel = this.getPanel(panel);
35285 this.setActivePanel(panel);
35291 * Get the active panel for this region.
35292 * @return {Roo.ContentPanel} The active panel or null
35294 getActivePanel : function(){
35295 return this.activePanel;
35299 * Add the passed ContentPanel(s)
35300 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35301 * @return {Roo.ContentPanel} The panel added (if only one was added)
35303 add : function(panel){
35304 if(arguments.length > 1){
35305 for(var i = 0, len = arguments.length; i < len; i++) {
35306 this.add(arguments[i]);
35310 if(this.hasPanel(panel)){
35311 this.showPanel(panel);
35314 var el = panel.getEl();
35315 if(el.dom.parentNode != this.mgr.el.dom){
35316 this.mgr.el.dom.appendChild(el.dom);
35318 if(panel.setRegion){
35319 panel.setRegion(this);
35321 this.panels.add(panel);
35322 el.setStyle("position", "absolute");
35323 if(!panel.background){
35324 this.setActivePanel(panel);
35325 if(this.config.initialSize && this.panels.getCount()==1){
35326 this.resizeTo(this.config.initialSize);
35329 this.fireEvent("paneladded", this, panel);
35334 * Returns true if the panel is in this region.
35335 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35336 * @return {Boolean}
35338 hasPanel : function(panel){
35339 if(typeof panel == "object"){ // must be panel obj
35340 panel = panel.getId();
35342 return this.getPanel(panel) ? true : false;
35346 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35347 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35348 * @param {Boolean} preservePanel Overrides the config preservePanel option
35349 * @return {Roo.ContentPanel} The panel that was removed
35351 remove : function(panel, preservePanel){
35352 panel = this.getPanel(panel);
35357 this.fireEvent("beforeremove", this, panel, e);
35358 if(e.cancel === true){
35361 var panelId = panel.getId();
35362 this.panels.removeKey(panelId);
35367 * Returns the panel specified or null if it's not in this region.
35368 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35369 * @return {Roo.ContentPanel}
35371 getPanel : function(id){
35372 if(typeof id == "object"){ // must be panel obj
35375 return this.panels.get(id);
35379 * Returns this regions position (north/south/east/west/center).
35382 getPosition: function(){
35383 return this.position;
35387 * Ext JS Library 1.1.1
35388 * Copyright(c) 2006-2007, Ext JS, LLC.
35390 * Originally Released Under LGPL - original licence link has changed is not relivant.
35393 * <script type="text/javascript">
35397 * @class Roo.bootstrap.layout.Region
35398 * @extends Roo.bootstrap.layout.Basic
35399 * This class represents a region in a layout manager.
35401 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35402 * @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})
35403 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35404 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35405 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35406 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35407 * @cfg {String} title The title for the region (overrides panel titles)
35408 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35409 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35410 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35411 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35412 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35413 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35414 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35415 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35416 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35417 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35419 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35420 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35421 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35422 * @cfg {Number} width For East/West panels
35423 * @cfg {Number} height For North/South panels
35424 * @cfg {Boolean} split To show the splitter
35425 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35427 * @cfg {string} cls Extra CSS classes to add to region
35429 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35430 * @cfg {string} region the region that it inhabits..
35433 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35434 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35436 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35437 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35438 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35440 Roo.bootstrap.layout.Region = function(config)
35442 this.applyConfig(config);
35444 var mgr = config.mgr;
35445 var pos = config.region;
35446 config.skipConfig = true;
35447 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35450 this.onRender(mgr.el);
35453 this.visible = true;
35454 this.collapsed = false;
35455 this.unrendered_panels = [];
35458 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35460 position: '', // set by wrapper (eg. north/south etc..)
35461 unrendered_panels : null, // unrendered panels.
35462 createBody : function(){
35463 /** This region's body element
35464 * @type Roo.Element */
35465 this.bodyEl = this.el.createChild({
35467 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35471 onRender: function(ctr, pos)
35473 var dh = Roo.DomHelper;
35474 /** This region's container element
35475 * @type Roo.Element */
35476 this.el = dh.append(ctr.dom, {
35478 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35480 /** This region's title element
35481 * @type Roo.Element */
35483 this.titleEl = dh.append(this.el.dom,
35486 unselectable: "on",
35487 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35489 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35490 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35493 this.titleEl.enableDisplayMode();
35494 /** This region's title text element
35495 * @type HTMLElement */
35496 this.titleTextEl = this.titleEl.dom.firstChild;
35497 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35499 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35500 this.closeBtn.enableDisplayMode();
35501 this.closeBtn.on("click", this.closeClicked, this);
35502 this.closeBtn.hide();
35504 this.createBody(this.config);
35505 if(this.config.hideWhenEmpty){
35507 this.on("paneladded", this.validateVisibility, this);
35508 this.on("panelremoved", this.validateVisibility, this);
35510 if(this.autoScroll){
35511 this.bodyEl.setStyle("overflow", "auto");
35513 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35515 //if(c.titlebar !== false){
35516 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35517 this.titleEl.hide();
35519 this.titleEl.show();
35520 if(this.config.title){
35521 this.titleTextEl.innerHTML = this.config.title;
35525 if(this.config.collapsed){
35526 this.collapse(true);
35528 if(this.config.hidden){
35532 if (this.unrendered_panels && this.unrendered_panels.length) {
35533 for (var i =0;i< this.unrendered_panels.length; i++) {
35534 this.add(this.unrendered_panels[i]);
35536 this.unrendered_panels = null;
35542 applyConfig : function(c)
35545 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35546 var dh = Roo.DomHelper;
35547 if(c.titlebar !== false){
35548 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35549 this.collapseBtn.on("click", this.collapse, this);
35550 this.collapseBtn.enableDisplayMode();
35552 if(c.showPin === true || this.showPin){
35553 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35554 this.stickBtn.enableDisplayMode();
35555 this.stickBtn.on("click", this.expand, this);
35556 this.stickBtn.hide();
35561 /** This region's collapsed element
35562 * @type Roo.Element */
35565 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35566 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35569 if(c.floatable !== false){
35570 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35571 this.collapsedEl.on("click", this.collapseClick, this);
35574 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35575 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35576 id: "message", unselectable: "on", style:{"float":"left"}});
35577 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35579 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35580 this.expandBtn.on("click", this.expand, this);
35584 if(this.collapseBtn){
35585 this.collapseBtn.setVisible(c.collapsible == true);
35588 this.cmargins = c.cmargins || this.cmargins ||
35589 (this.position == "west" || this.position == "east" ?
35590 {top: 0, left: 2, right:2, bottom: 0} :
35591 {top: 2, left: 0, right:0, bottom: 2});
35593 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35596 this.bottomTabs = c.tabPosition != "top";
35598 this.autoScroll = c.autoScroll || false;
35603 this.duration = c.duration || .30;
35604 this.slideDuration = c.slideDuration || .45;
35609 * Returns true if this region is currently visible.
35610 * @return {Boolean}
35612 isVisible : function(){
35613 return this.visible;
35617 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35618 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35620 //setCollapsedTitle : function(title){
35621 // title = title || " ";
35622 // if(this.collapsedTitleTextEl){
35623 // this.collapsedTitleTextEl.innerHTML = title;
35627 getBox : function(){
35629 // if(!this.collapsed){
35630 b = this.el.getBox(false, true);
35632 // b = this.collapsedEl.getBox(false, true);
35637 getMargins : function(){
35638 return this.margins;
35639 //return this.collapsed ? this.cmargins : this.margins;
35642 highlight : function(){
35643 this.el.addClass("x-layout-panel-dragover");
35646 unhighlight : function(){
35647 this.el.removeClass("x-layout-panel-dragover");
35650 updateBox : function(box)
35652 if (!this.bodyEl) {
35653 return; // not rendered yet..
35657 if(!this.collapsed){
35658 this.el.dom.style.left = box.x + "px";
35659 this.el.dom.style.top = box.y + "px";
35660 this.updateBody(box.width, box.height);
35662 this.collapsedEl.dom.style.left = box.x + "px";
35663 this.collapsedEl.dom.style.top = box.y + "px";
35664 this.collapsedEl.setSize(box.width, box.height);
35667 this.tabs.autoSizeTabs();
35671 updateBody : function(w, h)
35674 this.el.setWidth(w);
35675 w -= this.el.getBorderWidth("rl");
35676 if(this.config.adjustments){
35677 w += this.config.adjustments[0];
35680 if(h !== null && h > 0){
35681 this.el.setHeight(h);
35682 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35683 h -= this.el.getBorderWidth("tb");
35684 if(this.config.adjustments){
35685 h += this.config.adjustments[1];
35687 this.bodyEl.setHeight(h);
35689 h = this.tabs.syncHeight(h);
35692 if(this.panelSize){
35693 w = w !== null ? w : this.panelSize.width;
35694 h = h !== null ? h : this.panelSize.height;
35696 if(this.activePanel){
35697 var el = this.activePanel.getEl();
35698 w = w !== null ? w : el.getWidth();
35699 h = h !== null ? h : el.getHeight();
35700 this.panelSize = {width: w, height: h};
35701 this.activePanel.setSize(w, h);
35703 if(Roo.isIE && this.tabs){
35704 this.tabs.el.repaint();
35709 * Returns the container element for this region.
35710 * @return {Roo.Element}
35712 getEl : function(){
35717 * Hides this region.
35720 //if(!this.collapsed){
35721 this.el.dom.style.left = "-2000px";
35724 // this.collapsedEl.dom.style.left = "-2000px";
35725 // this.collapsedEl.hide();
35727 this.visible = false;
35728 this.fireEvent("visibilitychange", this, false);
35732 * Shows this region if it was previously hidden.
35735 //if(!this.collapsed){
35738 // this.collapsedEl.show();
35740 this.visible = true;
35741 this.fireEvent("visibilitychange", this, true);
35744 closeClicked : function(){
35745 if(this.activePanel){
35746 this.remove(this.activePanel);
35750 collapseClick : function(e){
35752 e.stopPropagation();
35755 e.stopPropagation();
35761 * Collapses this region.
35762 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35765 collapse : function(skipAnim, skipCheck = false){
35766 if(this.collapsed) {
35770 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35772 this.collapsed = true;
35774 this.split.el.hide();
35776 if(this.config.animate && skipAnim !== true){
35777 this.fireEvent("invalidated", this);
35778 this.animateCollapse();
35780 this.el.setLocation(-20000,-20000);
35782 this.collapsedEl.show();
35783 this.fireEvent("collapsed", this);
35784 this.fireEvent("invalidated", this);
35790 animateCollapse : function(){
35795 * Expands this region if it was previously collapsed.
35796 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35797 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35800 expand : function(e, skipAnim){
35802 e.stopPropagation();
35804 if(!this.collapsed || this.el.hasActiveFx()) {
35808 this.afterSlideIn();
35811 this.collapsed = false;
35812 if(this.config.animate && skipAnim !== true){
35813 this.animateExpand();
35817 this.split.el.show();
35819 this.collapsedEl.setLocation(-2000,-2000);
35820 this.collapsedEl.hide();
35821 this.fireEvent("invalidated", this);
35822 this.fireEvent("expanded", this);
35826 animateExpand : function(){
35830 initTabs : function()
35832 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35834 var ts = new Roo.bootstrap.panel.Tabs({
35835 el: this.bodyEl.dom,
35836 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35837 disableTooltips: this.config.disableTabTips,
35838 toolbar : this.config.toolbar
35841 if(this.config.hideTabs){
35842 ts.stripWrap.setDisplayed(false);
35845 ts.resizeTabs = this.config.resizeTabs === true;
35846 ts.minTabWidth = this.config.minTabWidth || 40;
35847 ts.maxTabWidth = this.config.maxTabWidth || 250;
35848 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35849 ts.monitorResize = false;
35850 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35851 ts.bodyEl.addClass('roo-layout-tabs-body');
35852 this.panels.each(this.initPanelAsTab, this);
35855 initPanelAsTab : function(panel){
35856 var ti = this.tabs.addTab(
35860 this.config.closeOnTab && panel.isClosable(),
35863 if(panel.tabTip !== undefined){
35864 ti.setTooltip(panel.tabTip);
35866 ti.on("activate", function(){
35867 this.setActivePanel(panel);
35870 if(this.config.closeOnTab){
35871 ti.on("beforeclose", function(t, e){
35873 this.remove(panel);
35877 panel.tabItem = ti;
35882 updatePanelTitle : function(panel, title)
35884 if(this.activePanel == panel){
35885 this.updateTitle(title);
35888 var ti = this.tabs.getTab(panel.getEl().id);
35890 if(panel.tabTip !== undefined){
35891 ti.setTooltip(panel.tabTip);
35896 updateTitle : function(title){
35897 if(this.titleTextEl && !this.config.title){
35898 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35902 setActivePanel : function(panel)
35904 panel = this.getPanel(panel);
35905 if(this.activePanel && this.activePanel != panel){
35906 if(this.activePanel.setActiveState(false) === false){
35910 this.activePanel = panel;
35911 panel.setActiveState(true);
35912 if(this.panelSize){
35913 panel.setSize(this.panelSize.width, this.panelSize.height);
35916 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35918 this.updateTitle(panel.getTitle());
35920 this.fireEvent("invalidated", this);
35922 this.fireEvent("panelactivated", this, panel);
35926 * Shows the specified panel.
35927 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35928 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35930 showPanel : function(panel)
35932 panel = this.getPanel(panel);
35935 var tab = this.tabs.getTab(panel.getEl().id);
35936 if(tab.isHidden()){
35937 this.tabs.unhideTab(tab.id);
35941 this.setActivePanel(panel);
35948 * Get the active panel for this region.
35949 * @return {Roo.ContentPanel} The active panel or null
35951 getActivePanel : function(){
35952 return this.activePanel;
35955 validateVisibility : function(){
35956 if(this.panels.getCount() < 1){
35957 this.updateTitle(" ");
35958 this.closeBtn.hide();
35961 if(!this.isVisible()){
35968 * Adds the passed ContentPanel(s) to this region.
35969 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35970 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35972 add : function(panel)
35974 if(arguments.length > 1){
35975 for(var i = 0, len = arguments.length; i < len; i++) {
35976 this.add(arguments[i]);
35981 // if we have not been rendered yet, then we can not really do much of this..
35982 if (!this.bodyEl) {
35983 this.unrendered_panels.push(panel);
35990 if(this.hasPanel(panel)){
35991 this.showPanel(panel);
35994 panel.setRegion(this);
35995 this.panels.add(panel);
35996 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35997 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35998 // and hide them... ???
35999 this.bodyEl.dom.appendChild(panel.getEl().dom);
36000 if(panel.background !== true){
36001 this.setActivePanel(panel);
36003 this.fireEvent("paneladded", this, panel);
36010 this.initPanelAsTab(panel);
36014 if(panel.background !== true){
36015 this.tabs.activate(panel.getEl().id);
36017 this.fireEvent("paneladded", this, panel);
36022 * Hides the tab for the specified panel.
36023 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36025 hidePanel : function(panel){
36026 if(this.tabs && (panel = this.getPanel(panel))){
36027 this.tabs.hideTab(panel.getEl().id);
36032 * Unhides the tab for a previously hidden panel.
36033 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36035 unhidePanel : function(panel){
36036 if(this.tabs && (panel = this.getPanel(panel))){
36037 this.tabs.unhideTab(panel.getEl().id);
36041 clearPanels : function(){
36042 while(this.panels.getCount() > 0){
36043 this.remove(this.panels.first());
36048 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36049 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36050 * @param {Boolean} preservePanel Overrides the config preservePanel option
36051 * @return {Roo.ContentPanel} The panel that was removed
36053 remove : function(panel, preservePanel)
36055 panel = this.getPanel(panel);
36060 this.fireEvent("beforeremove", this, panel, e);
36061 if(e.cancel === true){
36064 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36065 var panelId = panel.getId();
36066 this.panels.removeKey(panelId);
36068 document.body.appendChild(panel.getEl().dom);
36071 this.tabs.removeTab(panel.getEl().id);
36072 }else if (!preservePanel){
36073 this.bodyEl.dom.removeChild(panel.getEl().dom);
36075 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36076 var p = this.panels.first();
36077 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36078 tempEl.appendChild(p.getEl().dom);
36079 this.bodyEl.update("");
36080 this.bodyEl.dom.appendChild(p.getEl().dom);
36082 this.updateTitle(p.getTitle());
36084 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36085 this.setActivePanel(p);
36087 panel.setRegion(null);
36088 if(this.activePanel == panel){
36089 this.activePanel = null;
36091 if(this.config.autoDestroy !== false && preservePanel !== true){
36092 try{panel.destroy();}catch(e){}
36094 this.fireEvent("panelremoved", this, panel);
36099 * Returns the TabPanel component used by this region
36100 * @return {Roo.TabPanel}
36102 getTabs : function(){
36106 createTool : function(parentEl, className){
36107 var btn = Roo.DomHelper.append(parentEl, {
36109 cls: "x-layout-tools-button",
36112 cls: "roo-layout-tools-button-inner " + className,
36116 btn.addClassOnOver("roo-layout-tools-button-over");
36121 * Ext JS Library 1.1.1
36122 * Copyright(c) 2006-2007, Ext JS, LLC.
36124 * Originally Released Under LGPL - original licence link has changed is not relivant.
36127 * <script type="text/javascript">
36133 * @class Roo.SplitLayoutRegion
36134 * @extends Roo.LayoutRegion
36135 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36137 Roo.bootstrap.layout.Split = function(config){
36138 this.cursor = config.cursor;
36139 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36142 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36144 splitTip : "Drag to resize.",
36145 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36146 useSplitTips : false,
36148 applyConfig : function(config){
36149 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36152 onRender : function(ctr,pos) {
36154 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36155 if(!this.config.split){
36160 var splitEl = Roo.DomHelper.append(ctr.dom, {
36162 id: this.el.id + "-split",
36163 cls: "roo-layout-split roo-layout-split-"+this.position,
36166 /** The SplitBar for this region
36167 * @type Roo.SplitBar */
36168 // does not exist yet...
36169 Roo.log([this.position, this.orientation]);
36171 this.split = new Roo.bootstrap.SplitBar({
36172 dragElement : splitEl,
36173 resizingElement: this.el,
36174 orientation : this.orientation
36177 this.split.on("moved", this.onSplitMove, this);
36178 this.split.useShim = this.config.useShim === true;
36179 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36180 if(this.useSplitTips){
36181 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36183 //if(config.collapsible){
36184 // this.split.el.on("dblclick", this.collapse, this);
36187 if(typeof this.config.minSize != "undefined"){
36188 this.split.minSize = this.config.minSize;
36190 if(typeof this.config.maxSize != "undefined"){
36191 this.split.maxSize = this.config.maxSize;
36193 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36194 this.hideSplitter();
36199 getHMaxSize : function(){
36200 var cmax = this.config.maxSize || 10000;
36201 var center = this.mgr.getRegion("center");
36202 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36205 getVMaxSize : function(){
36206 var cmax = this.config.maxSize || 10000;
36207 var center = this.mgr.getRegion("center");
36208 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36211 onSplitMove : function(split, newSize){
36212 this.fireEvent("resized", this, newSize);
36216 * Returns the {@link Roo.SplitBar} for this region.
36217 * @return {Roo.SplitBar}
36219 getSplitBar : function(){
36224 this.hideSplitter();
36225 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36228 hideSplitter : function(){
36230 this.split.el.setLocation(-2000,-2000);
36231 this.split.el.hide();
36237 this.split.el.show();
36239 Roo.bootstrap.layout.Split.superclass.show.call(this);
36242 beforeSlide: function(){
36243 if(Roo.isGecko){// firefox overflow auto bug workaround
36244 this.bodyEl.clip();
36246 this.tabs.bodyEl.clip();
36248 if(this.activePanel){
36249 this.activePanel.getEl().clip();
36251 if(this.activePanel.beforeSlide){
36252 this.activePanel.beforeSlide();
36258 afterSlide : function(){
36259 if(Roo.isGecko){// firefox overflow auto bug workaround
36260 this.bodyEl.unclip();
36262 this.tabs.bodyEl.unclip();
36264 if(this.activePanel){
36265 this.activePanel.getEl().unclip();
36266 if(this.activePanel.afterSlide){
36267 this.activePanel.afterSlide();
36273 initAutoHide : function(){
36274 if(this.autoHide !== false){
36275 if(!this.autoHideHd){
36276 var st = new Roo.util.DelayedTask(this.slideIn, this);
36277 this.autoHideHd = {
36278 "mouseout": function(e){
36279 if(!e.within(this.el, true)){
36283 "mouseover" : function(e){
36289 this.el.on(this.autoHideHd);
36293 clearAutoHide : function(){
36294 if(this.autoHide !== false){
36295 this.el.un("mouseout", this.autoHideHd.mouseout);
36296 this.el.un("mouseover", this.autoHideHd.mouseover);
36300 clearMonitor : function(){
36301 Roo.get(document).un("click", this.slideInIf, this);
36304 // these names are backwards but not changed for compat
36305 slideOut : function(){
36306 if(this.isSlid || this.el.hasActiveFx()){
36309 this.isSlid = true;
36310 if(this.collapseBtn){
36311 this.collapseBtn.hide();
36313 this.closeBtnState = this.closeBtn.getStyle('display');
36314 this.closeBtn.hide();
36316 this.stickBtn.show();
36319 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36320 this.beforeSlide();
36321 this.el.setStyle("z-index", 10001);
36322 this.el.slideIn(this.getSlideAnchor(), {
36323 callback: function(){
36325 this.initAutoHide();
36326 Roo.get(document).on("click", this.slideInIf, this);
36327 this.fireEvent("slideshow", this);
36334 afterSlideIn : function(){
36335 this.clearAutoHide();
36336 this.isSlid = false;
36337 this.clearMonitor();
36338 this.el.setStyle("z-index", "");
36339 if(this.collapseBtn){
36340 this.collapseBtn.show();
36342 this.closeBtn.setStyle('display', this.closeBtnState);
36344 this.stickBtn.hide();
36346 this.fireEvent("slidehide", this);
36349 slideIn : function(cb){
36350 if(!this.isSlid || this.el.hasActiveFx()){
36354 this.isSlid = false;
36355 this.beforeSlide();
36356 this.el.slideOut(this.getSlideAnchor(), {
36357 callback: function(){
36358 this.el.setLeftTop(-10000, -10000);
36360 this.afterSlideIn();
36368 slideInIf : function(e){
36369 if(!e.within(this.el)){
36374 animateCollapse : function(){
36375 this.beforeSlide();
36376 this.el.setStyle("z-index", 20000);
36377 var anchor = this.getSlideAnchor();
36378 this.el.slideOut(anchor, {
36379 callback : function(){
36380 this.el.setStyle("z-index", "");
36381 this.collapsedEl.slideIn(anchor, {duration:.3});
36383 this.el.setLocation(-10000,-10000);
36385 this.fireEvent("collapsed", this);
36392 animateExpand : function(){
36393 this.beforeSlide();
36394 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36395 this.el.setStyle("z-index", 20000);
36396 this.collapsedEl.hide({
36399 this.el.slideIn(this.getSlideAnchor(), {
36400 callback : function(){
36401 this.el.setStyle("z-index", "");
36404 this.split.el.show();
36406 this.fireEvent("invalidated", this);
36407 this.fireEvent("expanded", this);
36435 getAnchor : function(){
36436 return this.anchors[this.position];
36439 getCollapseAnchor : function(){
36440 return this.canchors[this.position];
36443 getSlideAnchor : function(){
36444 return this.sanchors[this.position];
36447 getAlignAdj : function(){
36448 var cm = this.cmargins;
36449 switch(this.position){
36465 getExpandAdj : function(){
36466 var c = this.collapsedEl, cm = this.cmargins;
36467 switch(this.position){
36469 return [-(cm.right+c.getWidth()+cm.left), 0];
36472 return [cm.right+c.getWidth()+cm.left, 0];
36475 return [0, -(cm.top+cm.bottom+c.getHeight())];
36478 return [0, cm.top+cm.bottom+c.getHeight()];
36484 * Ext JS Library 1.1.1
36485 * Copyright(c) 2006-2007, Ext JS, LLC.
36487 * Originally Released Under LGPL - original licence link has changed is not relivant.
36490 * <script type="text/javascript">
36493 * These classes are private internal classes
36495 Roo.bootstrap.layout.Center = function(config){
36496 config.region = "center";
36497 Roo.bootstrap.layout.Region.call(this, config);
36498 this.visible = true;
36499 this.minWidth = config.minWidth || 20;
36500 this.minHeight = config.minHeight || 20;
36503 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36505 // center panel can't be hidden
36509 // center panel can't be hidden
36512 getMinWidth: function(){
36513 return this.minWidth;
36516 getMinHeight: function(){
36517 return this.minHeight;
36530 Roo.bootstrap.layout.North = function(config)
36532 config.region = 'north';
36533 config.cursor = 'n-resize';
36535 Roo.bootstrap.layout.Split.call(this, config);
36539 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36540 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36541 this.split.el.addClass("roo-layout-split-v");
36543 var size = config.initialSize || config.height;
36544 if(typeof size != "undefined"){
36545 this.el.setHeight(size);
36548 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36550 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36554 getBox : function(){
36555 if(this.collapsed){
36556 return this.collapsedEl.getBox();
36558 var box = this.el.getBox();
36560 box.height += this.split.el.getHeight();
36565 updateBox : function(box){
36566 if(this.split && !this.collapsed){
36567 box.height -= this.split.el.getHeight();
36568 this.split.el.setLeft(box.x);
36569 this.split.el.setTop(box.y+box.height);
36570 this.split.el.setWidth(box.width);
36572 if(this.collapsed){
36573 this.updateBody(box.width, null);
36575 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36583 Roo.bootstrap.layout.South = function(config){
36584 config.region = 'south';
36585 config.cursor = 's-resize';
36586 Roo.bootstrap.layout.Split.call(this, config);
36588 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36589 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36590 this.split.el.addClass("roo-layout-split-v");
36592 var size = config.initialSize || config.height;
36593 if(typeof size != "undefined"){
36594 this.el.setHeight(size);
36598 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36599 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36600 getBox : function(){
36601 if(this.collapsed){
36602 return this.collapsedEl.getBox();
36604 var box = this.el.getBox();
36606 var sh = this.split.el.getHeight();
36613 updateBox : function(box){
36614 if(this.split && !this.collapsed){
36615 var sh = this.split.el.getHeight();
36618 this.split.el.setLeft(box.x);
36619 this.split.el.setTop(box.y-sh);
36620 this.split.el.setWidth(box.width);
36622 if(this.collapsed){
36623 this.updateBody(box.width, null);
36625 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36629 Roo.bootstrap.layout.East = function(config){
36630 config.region = "east";
36631 config.cursor = "e-resize";
36632 Roo.bootstrap.layout.Split.call(this, config);
36634 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36635 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36636 this.split.el.addClass("roo-layout-split-h");
36638 var size = config.initialSize || config.width;
36639 if(typeof size != "undefined"){
36640 this.el.setWidth(size);
36643 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36644 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36645 getBox : function(){
36646 if(this.collapsed){
36647 return this.collapsedEl.getBox();
36649 var box = this.el.getBox();
36651 var sw = this.split.el.getWidth();
36658 updateBox : function(box){
36659 if(this.split && !this.collapsed){
36660 var sw = this.split.el.getWidth();
36662 this.split.el.setLeft(box.x);
36663 this.split.el.setTop(box.y);
36664 this.split.el.setHeight(box.height);
36667 if(this.collapsed){
36668 this.updateBody(null, box.height);
36670 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36674 Roo.bootstrap.layout.West = function(config){
36675 config.region = "west";
36676 config.cursor = "w-resize";
36678 Roo.bootstrap.layout.Split.call(this, config);
36680 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36681 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36682 this.split.el.addClass("roo-layout-split-h");
36686 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36687 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36689 onRender: function(ctr, pos)
36691 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36692 var size = this.config.initialSize || this.config.width;
36693 if(typeof size != "undefined"){
36694 this.el.setWidth(size);
36698 getBox : function(){
36699 if(this.collapsed){
36700 return this.collapsedEl.getBox();
36702 var box = this.el.getBox();
36704 box.width += this.split.el.getWidth();
36709 updateBox : function(box){
36710 if(this.split && !this.collapsed){
36711 var sw = this.split.el.getWidth();
36713 this.split.el.setLeft(box.x+box.width);
36714 this.split.el.setTop(box.y);
36715 this.split.el.setHeight(box.height);
36717 if(this.collapsed){
36718 this.updateBody(null, box.height);
36720 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36723 Roo.namespace("Roo.bootstrap.panel");/*
36725 * Ext JS Library 1.1.1
36726 * Copyright(c) 2006-2007, Ext JS, LLC.
36728 * Originally Released Under LGPL - original licence link has changed is not relivant.
36731 * <script type="text/javascript">
36734 * @class Roo.ContentPanel
36735 * @extends Roo.util.Observable
36736 * A basic ContentPanel element.
36737 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36738 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36739 * @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
36740 * @cfg {Boolean} closable True if the panel can be closed/removed
36741 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36742 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36743 * @cfg {Toolbar} toolbar A toolbar for this panel
36744 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36745 * @cfg {String} title The title for this panel
36746 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36747 * @cfg {String} url Calls {@link #setUrl} with this value
36748 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36749 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36750 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36751 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36752 * @cfg {Boolean} badges render the badges
36755 * Create a new ContentPanel.
36756 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36757 * @param {String/Object} config A string to set only the title or a config object
36758 * @param {String} content (optional) Set the HTML content for this panel
36759 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36761 Roo.bootstrap.panel.Content = function( config){
36763 this.tpl = config.tpl || false;
36765 var el = config.el;
36766 var content = config.content;
36768 if(config.autoCreate){ // xtype is available if this is called from factory
36771 this.el = Roo.get(el);
36772 if(!this.el && config && config.autoCreate){
36773 if(typeof config.autoCreate == "object"){
36774 if(!config.autoCreate.id){
36775 config.autoCreate.id = config.id||el;
36777 this.el = Roo.DomHelper.append(document.body,
36778 config.autoCreate, true);
36780 var elcfg = { tag: "div",
36781 cls: "roo-layout-inactive-content",
36785 elcfg.html = config.html;
36789 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36792 this.closable = false;
36793 this.loaded = false;
36794 this.active = false;
36797 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36799 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36801 this.wrapEl = this.el; //this.el.wrap();
36803 if (config.toolbar.items) {
36804 ti = config.toolbar.items ;
36805 delete config.toolbar.items ;
36809 this.toolbar.render(this.wrapEl, 'before');
36810 for(var i =0;i < ti.length;i++) {
36811 // Roo.log(['add child', items[i]]);
36812 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36814 this.toolbar.items = nitems;
36815 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36816 delete config.toolbar;
36820 // xtype created footer. - not sure if will work as we normally have to render first..
36821 if (this.footer && !this.footer.el && this.footer.xtype) {
36822 if (!this.wrapEl) {
36823 this.wrapEl = this.el.wrap();
36826 this.footer.container = this.wrapEl.createChild();
36828 this.footer = Roo.factory(this.footer, Roo);
36833 if(typeof config == "string"){
36834 this.title = config;
36836 Roo.apply(this, config);
36840 this.resizeEl = Roo.get(this.resizeEl, true);
36842 this.resizeEl = this.el;
36844 // handle view.xtype
36852 * Fires when this panel is activated.
36853 * @param {Roo.ContentPanel} this
36857 * @event deactivate
36858 * Fires when this panel is activated.
36859 * @param {Roo.ContentPanel} this
36861 "deactivate" : true,
36865 * Fires when this panel is resized if fitToFrame is true.
36866 * @param {Roo.ContentPanel} this
36867 * @param {Number} width The width after any component adjustments
36868 * @param {Number} height The height after any component adjustments
36874 * Fires when this tab is created
36875 * @param {Roo.ContentPanel} this
36886 if(this.autoScroll){
36887 this.resizeEl.setStyle("overflow", "auto");
36889 // fix randome scrolling
36890 //this.el.on('scroll', function() {
36891 // Roo.log('fix random scolling');
36892 // this.scrollTo('top',0);
36895 content = content || this.content;
36897 this.setContent(content);
36899 if(config && config.url){
36900 this.setUrl(this.url, this.params, this.loadOnce);
36905 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36907 if (this.view && typeof(this.view.xtype) != 'undefined') {
36908 this.view.el = this.el.appendChild(document.createElement("div"));
36909 this.view = Roo.factory(this.view);
36910 this.view.render && this.view.render(false, '');
36914 this.fireEvent('render', this);
36917 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36921 setRegion : function(region){
36922 this.region = region;
36923 this.setActiveClass(region && !this.background);
36927 setActiveClass: function(state)
36930 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36931 this.el.setStyle('position','relative');
36933 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36934 this.el.setStyle('position', 'absolute');
36939 * Returns the toolbar for this Panel if one was configured.
36940 * @return {Roo.Toolbar}
36942 getToolbar : function(){
36943 return this.toolbar;
36946 setActiveState : function(active)
36948 this.active = active;
36949 this.setActiveClass(active);
36951 if(this.fireEvent("deactivate", this) === false){
36956 this.fireEvent("activate", this);
36960 * Updates this panel's element
36961 * @param {String} content The new content
36962 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36964 setContent : function(content, loadScripts){
36965 this.el.update(content, loadScripts);
36968 ignoreResize : function(w, h){
36969 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36972 this.lastSize = {width: w, height: h};
36977 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36978 * @return {Roo.UpdateManager} The UpdateManager
36980 getUpdateManager : function(){
36981 return this.el.getUpdateManager();
36984 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36985 * @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:
36988 url: "your-url.php",
36989 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36990 callback: yourFunction,
36991 scope: yourObject, //(optional scope)
36994 text: "Loading...",
36999 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37000 * 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.
37001 * @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}
37002 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37003 * @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.
37004 * @return {Roo.ContentPanel} this
37007 var um = this.el.getUpdateManager();
37008 um.update.apply(um, arguments);
37014 * 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.
37015 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37016 * @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)
37017 * @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)
37018 * @return {Roo.UpdateManager} The UpdateManager
37020 setUrl : function(url, params, loadOnce){
37021 if(this.refreshDelegate){
37022 this.removeListener("activate", this.refreshDelegate);
37024 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37025 this.on("activate", this.refreshDelegate);
37026 return this.el.getUpdateManager();
37029 _handleRefresh : function(url, params, loadOnce){
37030 if(!loadOnce || !this.loaded){
37031 var updater = this.el.getUpdateManager();
37032 updater.update(url, params, this._setLoaded.createDelegate(this));
37036 _setLoaded : function(){
37037 this.loaded = true;
37041 * Returns this panel's id
37044 getId : function(){
37049 * Returns this panel's element - used by regiosn to add.
37050 * @return {Roo.Element}
37052 getEl : function(){
37053 return this.wrapEl || this.el;
37058 adjustForComponents : function(width, height)
37060 //Roo.log('adjustForComponents ');
37061 if(this.resizeEl != this.el){
37062 width -= this.el.getFrameWidth('lr');
37063 height -= this.el.getFrameWidth('tb');
37066 var te = this.toolbar.getEl();
37067 te.setWidth(width);
37068 height -= te.getHeight();
37071 var te = this.footer.getEl();
37072 te.setWidth(width);
37073 height -= te.getHeight();
37077 if(this.adjustments){
37078 width += this.adjustments[0];
37079 height += this.adjustments[1];
37081 return {"width": width, "height": height};
37084 setSize : function(width, height){
37085 if(this.fitToFrame && !this.ignoreResize(width, height)){
37086 if(this.fitContainer && this.resizeEl != this.el){
37087 this.el.setSize(width, height);
37089 var size = this.adjustForComponents(width, height);
37090 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37091 this.fireEvent('resize', this, size.width, size.height);
37096 * Returns this panel's title
37099 getTitle : function(){
37101 if (typeof(this.title) != 'object') {
37106 for (var k in this.title) {
37107 if (!this.title.hasOwnProperty(k)) {
37111 if (k.indexOf('-') >= 0) {
37112 var s = k.split('-');
37113 for (var i = 0; i<s.length; i++) {
37114 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37117 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37124 * Set this panel's title
37125 * @param {String} title
37127 setTitle : function(title){
37128 this.title = title;
37130 this.region.updatePanelTitle(this, title);
37135 * Returns true is this panel was configured to be closable
37136 * @return {Boolean}
37138 isClosable : function(){
37139 return this.closable;
37142 beforeSlide : function(){
37144 this.resizeEl.clip();
37147 afterSlide : function(){
37149 this.resizeEl.unclip();
37153 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37154 * Will fail silently if the {@link #setUrl} method has not been called.
37155 * This does not activate the panel, just updates its content.
37157 refresh : function(){
37158 if(this.refreshDelegate){
37159 this.loaded = false;
37160 this.refreshDelegate();
37165 * Destroys this panel
37167 destroy : function(){
37168 this.el.removeAllListeners();
37169 var tempEl = document.createElement("span");
37170 tempEl.appendChild(this.el.dom);
37171 tempEl.innerHTML = "";
37177 * form - if the content panel contains a form - this is a reference to it.
37178 * @type {Roo.form.Form}
37182 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37183 * This contains a reference to it.
37189 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37199 * @param {Object} cfg Xtype definition of item to add.
37203 getChildContainer: function () {
37204 return this.getEl();
37209 var ret = new Roo.factory(cfg);
37214 if (cfg.xtype.match(/^Form$/)) {
37217 //if (this.footer) {
37218 // el = this.footer.container.insertSibling(false, 'before');
37220 el = this.el.createChild();
37223 this.form = new Roo.form.Form(cfg);
37226 if ( this.form.allItems.length) {
37227 this.form.render(el.dom);
37231 // should only have one of theses..
37232 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37233 // views.. should not be just added - used named prop 'view''
37235 cfg.el = this.el.appendChild(document.createElement("div"));
37238 var ret = new Roo.factory(cfg);
37240 ret.render && ret.render(false, ''); // render blank..
37250 * @class Roo.bootstrap.panel.Grid
37251 * @extends Roo.bootstrap.panel.Content
37253 * Create a new GridPanel.
37254 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37255 * @param {Object} config A the config object
37261 Roo.bootstrap.panel.Grid = function(config)
37265 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37266 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37268 config.el = this.wrapper;
37269 //this.el = this.wrapper;
37271 if (config.container) {
37272 // ctor'ed from a Border/panel.grid
37275 this.wrapper.setStyle("overflow", "hidden");
37276 this.wrapper.addClass('roo-grid-container');
37281 if(config.toolbar){
37282 var tool_el = this.wrapper.createChild();
37283 this.toolbar = Roo.factory(config.toolbar);
37285 if (config.toolbar.items) {
37286 ti = config.toolbar.items ;
37287 delete config.toolbar.items ;
37291 this.toolbar.render(tool_el);
37292 for(var i =0;i < ti.length;i++) {
37293 // Roo.log(['add child', items[i]]);
37294 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37296 this.toolbar.items = nitems;
37298 delete config.toolbar;
37301 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37302 config.grid.scrollBody = true;;
37303 config.grid.monitorWindowResize = false; // turn off autosizing
37304 config.grid.autoHeight = false;
37305 config.grid.autoWidth = false;
37307 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37309 if (config.background) {
37310 // render grid on panel activation (if panel background)
37311 this.on('activate', function(gp) {
37312 if (!gp.grid.rendered) {
37313 gp.grid.render(this.wrapper);
37314 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37319 this.grid.render(this.wrapper);
37320 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37323 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37324 // ??? needed ??? config.el = this.wrapper;
37329 // xtype created footer. - not sure if will work as we normally have to render first..
37330 if (this.footer && !this.footer.el && this.footer.xtype) {
37332 var ctr = this.grid.getView().getFooterPanel(true);
37333 this.footer.dataSource = this.grid.dataSource;
37334 this.footer = Roo.factory(this.footer, Roo);
37335 this.footer.render(ctr);
37345 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37346 getId : function(){
37347 return this.grid.id;
37351 * Returns the grid for this panel
37352 * @return {Roo.bootstrap.Table}
37354 getGrid : function(){
37358 setSize : function(width, height){
37359 if(!this.ignoreResize(width, height)){
37360 var grid = this.grid;
37361 var size = this.adjustForComponents(width, height);
37362 var gridel = grid.getGridEl();
37363 gridel.setSize(size.width, size.height);
37365 var thd = grid.getGridEl().select('thead',true).first();
37366 var tbd = grid.getGridEl().select('tbody', true).first();
37368 tbd.setSize(width, height - thd.getHeight());
37377 beforeSlide : function(){
37378 this.grid.getView().scroller.clip();
37381 afterSlide : function(){
37382 this.grid.getView().scroller.unclip();
37385 destroy : function(){
37386 this.grid.destroy();
37388 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37393 * @class Roo.bootstrap.panel.Nest
37394 * @extends Roo.bootstrap.panel.Content
37396 * Create a new Panel, that can contain a layout.Border.
37399 * @param {Roo.BorderLayout} layout The layout for this panel
37400 * @param {String/Object} config A string to set only the title or a config object
37402 Roo.bootstrap.panel.Nest = function(config)
37404 // construct with only one argument..
37405 /* FIXME - implement nicer consturctors
37406 if (layout.layout) {
37408 layout = config.layout;
37409 delete config.layout;
37411 if (layout.xtype && !layout.getEl) {
37412 // then layout needs constructing..
37413 layout = Roo.factory(layout, Roo);
37417 config.el = config.layout.getEl();
37419 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37421 config.layout.monitorWindowResize = false; // turn off autosizing
37422 this.layout = config.layout;
37423 this.layout.getEl().addClass("roo-layout-nested-layout");
37430 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37432 setSize : function(width, height){
37433 if(!this.ignoreResize(width, height)){
37434 var size = this.adjustForComponents(width, height);
37435 var el = this.layout.getEl();
37436 if (size.height < 1) {
37437 el.setWidth(size.width);
37439 el.setSize(size.width, size.height);
37441 var touch = el.dom.offsetWidth;
37442 this.layout.layout();
37443 // ie requires a double layout on the first pass
37444 if(Roo.isIE && !this.initialized){
37445 this.initialized = true;
37446 this.layout.layout();
37451 // activate all subpanels if not currently active..
37453 setActiveState : function(active){
37454 this.active = active;
37455 this.setActiveClass(active);
37458 this.fireEvent("deactivate", this);
37462 this.fireEvent("activate", this);
37463 // not sure if this should happen before or after..
37464 if (!this.layout) {
37465 return; // should not happen..
37468 for (var r in this.layout.regions) {
37469 reg = this.layout.getRegion(r);
37470 if (reg.getActivePanel()) {
37471 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37472 reg.setActivePanel(reg.getActivePanel());
37475 if (!reg.panels.length) {
37478 reg.showPanel(reg.getPanel(0));
37487 * Returns the nested BorderLayout for this panel
37488 * @return {Roo.BorderLayout}
37490 getLayout : function(){
37491 return this.layout;
37495 * Adds a xtype elements to the layout of the nested panel
37499 xtype : 'ContentPanel',
37506 xtype : 'NestedLayoutPanel',
37512 items : [ ... list of content panels or nested layout panels.. ]
37516 * @param {Object} cfg Xtype definition of item to add.
37518 addxtype : function(cfg) {
37519 return this.layout.addxtype(cfg);
37524 * Ext JS Library 1.1.1
37525 * Copyright(c) 2006-2007, Ext JS, LLC.
37527 * Originally Released Under LGPL - original licence link has changed is not relivant.
37530 * <script type="text/javascript">
37533 * @class Roo.TabPanel
37534 * @extends Roo.util.Observable
37535 * A lightweight tab container.
37539 // basic tabs 1, built from existing content
37540 var tabs = new Roo.TabPanel("tabs1");
37541 tabs.addTab("script", "View Script");
37542 tabs.addTab("markup", "View Markup");
37543 tabs.activate("script");
37545 // more advanced tabs, built from javascript
37546 var jtabs = new Roo.TabPanel("jtabs");
37547 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37549 // set up the UpdateManager
37550 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37551 var updater = tab2.getUpdateManager();
37552 updater.setDefaultUrl("ajax1.htm");
37553 tab2.on('activate', updater.refresh, updater, true);
37555 // Use setUrl for Ajax loading
37556 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37557 tab3.setUrl("ajax2.htm", null, true);
37560 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37563 jtabs.activate("jtabs-1");
37566 * Create a new TabPanel.
37567 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37568 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37570 Roo.bootstrap.panel.Tabs = function(config){
37572 * The container element for this TabPanel.
37573 * @type Roo.Element
37575 this.el = Roo.get(config.el);
37578 if(typeof config == "boolean"){
37579 this.tabPosition = config ? "bottom" : "top";
37581 Roo.apply(this, config);
37585 if(this.tabPosition == "bottom"){
37586 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37587 this.el.addClass("roo-tabs-bottom");
37589 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37590 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37591 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37593 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37595 if(this.tabPosition != "bottom"){
37596 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37597 * @type Roo.Element
37599 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37600 this.el.addClass("roo-tabs-top");
37604 this.bodyEl.setStyle("position", "relative");
37606 this.active = null;
37607 this.activateDelegate = this.activate.createDelegate(this);
37612 * Fires when the active tab changes
37613 * @param {Roo.TabPanel} this
37614 * @param {Roo.TabPanelItem} activePanel The new active tab
37618 * @event beforetabchange
37619 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37620 * @param {Roo.TabPanel} this
37621 * @param {Object} e Set cancel to true on this object to cancel the tab change
37622 * @param {Roo.TabPanelItem} tab The tab being changed to
37624 "beforetabchange" : true
37627 Roo.EventManager.onWindowResize(this.onResize, this);
37628 this.cpad = this.el.getPadding("lr");
37629 this.hiddenCount = 0;
37632 // toolbar on the tabbar support...
37633 if (this.toolbar) {
37634 alert("no toolbar support yet");
37635 this.toolbar = false;
37637 var tcfg = this.toolbar;
37638 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37639 this.toolbar = new Roo.Toolbar(tcfg);
37640 if (Roo.isSafari) {
37641 var tbl = tcfg.container.child('table', true);
37642 tbl.setAttribute('width', '100%');
37650 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37653 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37655 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37657 tabPosition : "top",
37659 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37661 currentTabWidth : 0,
37663 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37667 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37671 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37673 preferredTabWidth : 175,
37675 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37677 resizeTabs : false,
37679 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37681 monitorResize : true,
37683 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37688 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37689 * @param {String} id The id of the div to use <b>or create</b>
37690 * @param {String} text The text for the tab
37691 * @param {String} content (optional) Content to put in the TabPanelItem body
37692 * @param {Boolean} closable (optional) True to create a close icon on the tab
37693 * @return {Roo.TabPanelItem} The created TabPanelItem
37695 addTab : function(id, text, content, closable, tpl)
37697 var item = new Roo.bootstrap.panel.TabItem({
37701 closable : closable,
37704 this.addTabItem(item);
37706 item.setContent(content);
37712 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37713 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37714 * @return {Roo.TabPanelItem}
37716 getTab : function(id){
37717 return this.items[id];
37721 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37722 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37724 hideTab : function(id){
37725 var t = this.items[id];
37728 this.hiddenCount++;
37729 this.autoSizeTabs();
37734 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37735 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37737 unhideTab : function(id){
37738 var t = this.items[id];
37740 t.setHidden(false);
37741 this.hiddenCount--;
37742 this.autoSizeTabs();
37747 * Adds an existing {@link Roo.TabPanelItem}.
37748 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37750 addTabItem : function(item){
37751 this.items[item.id] = item;
37752 this.items.push(item);
37753 // if(this.resizeTabs){
37754 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37755 // this.autoSizeTabs();
37757 // item.autoSize();
37762 * Removes a {@link Roo.TabPanelItem}.
37763 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37765 removeTab : function(id){
37766 var items = this.items;
37767 var tab = items[id];
37768 if(!tab) { return; }
37769 var index = items.indexOf(tab);
37770 if(this.active == tab && items.length > 1){
37771 var newTab = this.getNextAvailable(index);
37776 this.stripEl.dom.removeChild(tab.pnode.dom);
37777 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37778 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37780 items.splice(index, 1);
37781 delete this.items[tab.id];
37782 tab.fireEvent("close", tab);
37783 tab.purgeListeners();
37784 this.autoSizeTabs();
37787 getNextAvailable : function(start){
37788 var items = this.items;
37790 // look for a next tab that will slide over to
37791 // replace the one being removed
37792 while(index < items.length){
37793 var item = items[++index];
37794 if(item && !item.isHidden()){
37798 // if one isn't found select the previous tab (on the left)
37801 var item = items[--index];
37802 if(item && !item.isHidden()){
37810 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37811 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37813 disableTab : function(id){
37814 var tab = this.items[id];
37815 if(tab && this.active != tab){
37821 * Enables a {@link Roo.TabPanelItem} that is disabled.
37822 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37824 enableTab : function(id){
37825 var tab = this.items[id];
37830 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37831 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37832 * @return {Roo.TabPanelItem} The TabPanelItem.
37834 activate : function(id){
37835 var tab = this.items[id];
37839 if(tab == this.active || tab.disabled){
37843 this.fireEvent("beforetabchange", this, e, tab);
37844 if(e.cancel !== true && !tab.disabled){
37846 this.active.hide();
37848 this.active = this.items[id];
37849 this.active.show();
37850 this.fireEvent("tabchange", this, this.active);
37856 * Gets the active {@link Roo.TabPanelItem}.
37857 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37859 getActiveTab : function(){
37860 return this.active;
37864 * Updates the tab body element to fit the height of the container element
37865 * for overflow scrolling
37866 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37868 syncHeight : function(targetHeight){
37869 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37870 var bm = this.bodyEl.getMargins();
37871 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37872 this.bodyEl.setHeight(newHeight);
37876 onResize : function(){
37877 if(this.monitorResize){
37878 this.autoSizeTabs();
37883 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37885 beginUpdate : function(){
37886 this.updating = true;
37890 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37892 endUpdate : function(){
37893 this.updating = false;
37894 this.autoSizeTabs();
37898 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37900 autoSizeTabs : function(){
37901 var count = this.items.length;
37902 var vcount = count - this.hiddenCount;
37903 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37906 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37907 var availWidth = Math.floor(w / vcount);
37908 var b = this.stripBody;
37909 if(b.getWidth() > w){
37910 var tabs = this.items;
37911 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37912 if(availWidth < this.minTabWidth){
37913 /*if(!this.sleft){ // incomplete scrolling code
37914 this.createScrollButtons();
37917 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37920 if(this.currentTabWidth < this.preferredTabWidth){
37921 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37927 * Returns the number of tabs in this TabPanel.
37930 getCount : function(){
37931 return this.items.length;
37935 * Resizes all the tabs to the passed width
37936 * @param {Number} The new width
37938 setTabWidth : function(width){
37939 this.currentTabWidth = width;
37940 for(var i = 0, len = this.items.length; i < len; i++) {
37941 if(!this.items[i].isHidden()) {
37942 this.items[i].setWidth(width);
37948 * Destroys this TabPanel
37949 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37951 destroy : function(removeEl){
37952 Roo.EventManager.removeResizeListener(this.onResize, this);
37953 for(var i = 0, len = this.items.length; i < len; i++){
37954 this.items[i].purgeListeners();
37956 if(removeEl === true){
37957 this.el.update("");
37962 createStrip : function(container)
37964 var strip = document.createElement("nav");
37965 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37966 container.appendChild(strip);
37970 createStripList : function(strip)
37972 // div wrapper for retard IE
37973 // returns the "tr" element.
37974 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37975 //'<div class="x-tabs-strip-wrap">'+
37976 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37977 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37978 return strip.firstChild; //.firstChild.firstChild.firstChild;
37980 createBody : function(container)
37982 var body = document.createElement("div");
37983 Roo.id(body, "tab-body");
37984 //Roo.fly(body).addClass("x-tabs-body");
37985 Roo.fly(body).addClass("tab-content");
37986 container.appendChild(body);
37989 createItemBody :function(bodyEl, id){
37990 var body = Roo.getDom(id);
37992 body = document.createElement("div");
37995 //Roo.fly(body).addClass("x-tabs-item-body");
37996 Roo.fly(body).addClass("tab-pane");
37997 bodyEl.insertBefore(body, bodyEl.firstChild);
38001 createStripElements : function(stripEl, text, closable, tpl)
38003 var td = document.createElement("li"); // was td..
38006 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38009 stripEl.appendChild(td);
38011 td.className = "x-tabs-closable";
38012 if(!this.closeTpl){
38013 this.closeTpl = new Roo.Template(
38014 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38015 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38016 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38019 var el = this.closeTpl.overwrite(td, {"text": text});
38020 var close = el.getElementsByTagName("div")[0];
38021 var inner = el.getElementsByTagName("em")[0];
38022 return {"el": el, "close": close, "inner": inner};
38025 // not sure what this is..
38026 // if(!this.tabTpl){
38027 //this.tabTpl = new Roo.Template(
38028 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38029 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38031 // this.tabTpl = new Roo.Template(
38032 // '<a href="#">' +
38033 // '<span unselectable="on"' +
38034 // (this.disableTooltips ? '' : ' title="{text}"') +
38035 // ' >{text}</span></a>'
38041 var template = tpl || this.tabTpl || false;
38045 template = new Roo.Template(
38047 '<span unselectable="on"' +
38048 (this.disableTooltips ? '' : ' title="{text}"') +
38049 ' >{text}</span></a>'
38053 switch (typeof(template)) {
38057 template = new Roo.Template(template);
38063 var el = template.overwrite(td, {"text": text});
38065 var inner = el.getElementsByTagName("span")[0];
38067 return {"el": el, "inner": inner};
38075 * @class Roo.TabPanelItem
38076 * @extends Roo.util.Observable
38077 * Represents an individual item (tab plus body) in a TabPanel.
38078 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38079 * @param {String} id The id of this TabPanelItem
38080 * @param {String} text The text for the tab of this TabPanelItem
38081 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38083 Roo.bootstrap.panel.TabItem = function(config){
38085 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38086 * @type Roo.TabPanel
38088 this.tabPanel = config.panel;
38090 * The id for this TabPanelItem
38093 this.id = config.id;
38095 this.disabled = false;
38097 this.text = config.text;
38099 this.loaded = false;
38100 this.closable = config.closable;
38103 * The body element for this TabPanelItem.
38104 * @type Roo.Element
38106 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38107 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38108 this.bodyEl.setStyle("display", "block");
38109 this.bodyEl.setStyle("zoom", "1");
38110 //this.hideAction();
38112 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38114 this.el = Roo.get(els.el);
38115 this.inner = Roo.get(els.inner, true);
38116 this.textEl = Roo.get(this.el.dom.firstChild, true);
38117 this.pnode = Roo.get(els.el.parentNode, true);
38118 // this.el.on("mousedown", this.onTabMouseDown, this);
38119 this.el.on("click", this.onTabClick, this);
38121 if(config.closable){
38122 var c = Roo.get(els.close, true);
38123 c.dom.title = this.closeText;
38124 c.addClassOnOver("close-over");
38125 c.on("click", this.closeClick, this);
38131 * Fires when this tab becomes the active tab.
38132 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38133 * @param {Roo.TabPanelItem} this
38137 * @event beforeclose
38138 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38139 * @param {Roo.TabPanelItem} this
38140 * @param {Object} e Set cancel to true on this object to cancel the close.
38142 "beforeclose": true,
38145 * Fires when this tab is closed.
38146 * @param {Roo.TabPanelItem} this
38150 * @event deactivate
38151 * Fires when this tab is no longer the active tab.
38152 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38153 * @param {Roo.TabPanelItem} this
38155 "deactivate" : true
38157 this.hidden = false;
38159 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38162 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38164 purgeListeners : function(){
38165 Roo.util.Observable.prototype.purgeListeners.call(this);
38166 this.el.removeAllListeners();
38169 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38172 this.pnode.addClass("active");
38175 this.tabPanel.stripWrap.repaint();
38177 this.fireEvent("activate", this.tabPanel, this);
38181 * Returns true if this tab is the active tab.
38182 * @return {Boolean}
38184 isActive : function(){
38185 return this.tabPanel.getActiveTab() == this;
38189 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38192 this.pnode.removeClass("active");
38194 this.fireEvent("deactivate", this.tabPanel, this);
38197 hideAction : function(){
38198 this.bodyEl.hide();
38199 this.bodyEl.setStyle("position", "absolute");
38200 this.bodyEl.setLeft("-20000px");
38201 this.bodyEl.setTop("-20000px");
38204 showAction : function(){
38205 this.bodyEl.setStyle("position", "relative");
38206 this.bodyEl.setTop("");
38207 this.bodyEl.setLeft("");
38208 this.bodyEl.show();
38212 * Set the tooltip for the tab.
38213 * @param {String} tooltip The tab's tooltip
38215 setTooltip : function(text){
38216 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38217 this.textEl.dom.qtip = text;
38218 this.textEl.dom.removeAttribute('title');
38220 this.textEl.dom.title = text;
38224 onTabClick : function(e){
38225 e.preventDefault();
38226 this.tabPanel.activate(this.id);
38229 onTabMouseDown : function(e){
38230 e.preventDefault();
38231 this.tabPanel.activate(this.id);
38234 getWidth : function(){
38235 return this.inner.getWidth();
38238 setWidth : function(width){
38239 var iwidth = width - this.pnode.getPadding("lr");
38240 this.inner.setWidth(iwidth);
38241 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38242 this.pnode.setWidth(width);
38246 * Show or hide the tab
38247 * @param {Boolean} hidden True to hide or false to show.
38249 setHidden : function(hidden){
38250 this.hidden = hidden;
38251 this.pnode.setStyle("display", hidden ? "none" : "");
38255 * Returns true if this tab is "hidden"
38256 * @return {Boolean}
38258 isHidden : function(){
38259 return this.hidden;
38263 * Returns the text for this tab
38266 getText : function(){
38270 autoSize : function(){
38271 //this.el.beginMeasure();
38272 this.textEl.setWidth(1);
38274 * #2804 [new] Tabs in Roojs
38275 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38277 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38278 //this.el.endMeasure();
38282 * Sets the text for the tab (Note: this also sets the tooltip text)
38283 * @param {String} text The tab's text and tooltip
38285 setText : function(text){
38287 this.textEl.update(text);
38288 this.setTooltip(text);
38289 //if(!this.tabPanel.resizeTabs){
38290 // this.autoSize();
38294 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38296 activate : function(){
38297 this.tabPanel.activate(this.id);
38301 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38303 disable : function(){
38304 if(this.tabPanel.active != this){
38305 this.disabled = true;
38306 this.pnode.addClass("disabled");
38311 * Enables this TabPanelItem if it was previously disabled.
38313 enable : function(){
38314 this.disabled = false;
38315 this.pnode.removeClass("disabled");
38319 * Sets the content for this TabPanelItem.
38320 * @param {String} content The content
38321 * @param {Boolean} loadScripts true to look for and load scripts
38323 setContent : function(content, loadScripts){
38324 this.bodyEl.update(content, loadScripts);
38328 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38329 * @return {Roo.UpdateManager} The UpdateManager
38331 getUpdateManager : function(){
38332 return this.bodyEl.getUpdateManager();
38336 * Set a URL to be used to load the content for this TabPanelItem.
38337 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38338 * @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)
38339 * @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)
38340 * @return {Roo.UpdateManager} The UpdateManager
38342 setUrl : function(url, params, loadOnce){
38343 if(this.refreshDelegate){
38344 this.un('activate', this.refreshDelegate);
38346 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38347 this.on("activate", this.refreshDelegate);
38348 return this.bodyEl.getUpdateManager();
38352 _handleRefresh : function(url, params, loadOnce){
38353 if(!loadOnce || !this.loaded){
38354 var updater = this.bodyEl.getUpdateManager();
38355 updater.update(url, params, this._setLoaded.createDelegate(this));
38360 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38361 * Will fail silently if the setUrl method has not been called.
38362 * This does not activate the panel, just updates its content.
38364 refresh : function(){
38365 if(this.refreshDelegate){
38366 this.loaded = false;
38367 this.refreshDelegate();
38372 _setLoaded : function(){
38373 this.loaded = true;
38377 closeClick : function(e){
38380 this.fireEvent("beforeclose", this, o);
38381 if(o.cancel !== true){
38382 this.tabPanel.removeTab(this.id);
38386 * The text displayed in the tooltip for the close icon.
38389 closeText : "Close this tab"
38392 * This script refer to:
38393 * Title: International Telephone Input
38394 * Author: Jack O'Connor
38395 * Code version: v12.1.12
38396 * Availability: https://github.com/jackocnr/intl-tel-input.git
38399 Roo.bootstrap.PhoneInputData = function() {
38402 "Afghanistan (افغانستان)",
38407 "Albania (Shqipëri)",
38412 "Algeria (الجزائر)",
38437 "Antigua and Barbuda",
38447 "Armenia (Հայաստան)",
38463 "Austria (Österreich)",
38468 "Azerbaijan (Azərbaycan)",
38478 "Bahrain (البحرين)",
38483 "Bangladesh (বাংলাদেশ)",
38493 "Belarus (Беларусь)",
38498 "Belgium (België)",
38528 "Bosnia and Herzegovina (Босна и Херцеговина)",
38543 "British Indian Ocean Territory",
38548 "British Virgin Islands",
38558 "Bulgaria (България)",
38568 "Burundi (Uburundi)",
38573 "Cambodia (កម្ពុជា)",
38578 "Cameroon (Cameroun)",
38587 ["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"]
38590 "Cape Verde (Kabu Verdi)",
38595 "Caribbean Netherlands",
38606 "Central African Republic (République centrafricaine)",
38626 "Christmas Island",
38632 "Cocos (Keeling) Islands",
38643 "Comoros (جزر القمر)",
38648 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38653 "Congo (Republic) (Congo-Brazzaville)",
38673 "Croatia (Hrvatska)",
38694 "Czech Republic (Česká republika)",
38699 "Denmark (Danmark)",
38714 "Dominican Republic (República Dominicana)",
38718 ["809", "829", "849"]
38736 "Equatorial Guinea (Guinea Ecuatorial)",
38756 "Falkland Islands (Islas Malvinas)",
38761 "Faroe Islands (Føroyar)",
38782 "French Guiana (Guyane française)",
38787 "French Polynesia (Polynésie française)",
38802 "Georgia (საქართველო)",
38807 "Germany (Deutschland)",
38827 "Greenland (Kalaallit Nunaat)",
38864 "Guinea-Bissau (Guiné Bissau)",
38889 "Hungary (Magyarország)",
38894 "Iceland (Ísland)",
38914 "Iraq (العراق)",
38930 "Israel (ישראל)",
38957 "Jordan (الأردن)",
38962 "Kazakhstan (Казахстан)",
38983 "Kuwait (الكويت)",
38988 "Kyrgyzstan (Кыргызстан)",
38998 "Latvia (Latvija)",
39003 "Lebanon (لبنان)",
39018 "Libya (ليبيا)",
39028 "Lithuania (Lietuva)",
39043 "Macedonia (FYROM) (Македонија)",
39048 "Madagascar (Madagasikara)",
39078 "Marshall Islands",
39088 "Mauritania (موريتانيا)",
39093 "Mauritius (Moris)",
39114 "Moldova (Republica Moldova)",
39124 "Mongolia (Монгол)",
39129 "Montenegro (Crna Gora)",
39139 "Morocco (المغرب)",
39145 "Mozambique (Moçambique)",
39150 "Myanmar (Burma) (မြန်မာ)",
39155 "Namibia (Namibië)",
39170 "Netherlands (Nederland)",
39175 "New Caledonia (Nouvelle-Calédonie)",
39210 "North Korea (조선 민주주의 인민 공화국)",
39215 "Northern Mariana Islands",
39231 "Pakistan (پاکستان)",
39241 "Palestine (فلسطين)",
39251 "Papua New Guinea",
39293 "Réunion (La Réunion)",
39299 "Romania (România)",
39315 "Saint Barthélemy",
39326 "Saint Kitts and Nevis",
39336 "Saint Martin (Saint-Martin (partie française))",
39342 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39347 "Saint Vincent and the Grenadines",
39362 "São Tomé and Príncipe (São Tomé e Príncipe)",
39367 "Saudi Arabia (المملكة العربية السعودية)",
39372 "Senegal (Sénégal)",
39402 "Slovakia (Slovensko)",
39407 "Slovenia (Slovenija)",
39417 "Somalia (Soomaaliya)",
39427 "South Korea (대한민국)",
39432 "South Sudan (جنوب السودان)",
39442 "Sri Lanka (ශ්රී ලංකාව)",
39447 "Sudan (السودان)",
39457 "Svalbard and Jan Mayen",
39468 "Sweden (Sverige)",
39473 "Switzerland (Schweiz)",
39478 "Syria (سوريا)",
39523 "Trinidad and Tobago",
39528 "Tunisia (تونس)",
39533 "Turkey (Türkiye)",
39543 "Turks and Caicos Islands",
39553 "U.S. Virgin Islands",
39563 "Ukraine (Україна)",
39568 "United Arab Emirates (الإمارات العربية المتحدة)",
39590 "Uzbekistan (Oʻzbekiston)",
39600 "Vatican City (Città del Vaticano)",
39611 "Vietnam (Việt Nam)",
39616 "Wallis and Futuna (Wallis-et-Futuna)",
39621 "Western Sahara (الصحراء الغربية)",
39627 "Yemen (اليمن)",
39651 * This script refer to:
39652 * Title: International Telephone Input
39653 * Author: Jack O'Connor
39654 * Code version: v12.1.12
39655 * Availability: https://github.com/jackocnr/intl-tel-input.git
39659 * @class Roo.bootstrap.PhoneInput
39660 * @extends Roo.bootstrap.TriggerField
39661 * An input with International dial-code selection
39663 * @cfg {String} defaultDialCode default '+852'
39664 * @cfg {Array} preferedCountries default []
39667 * Create a new PhoneInput.
39668 * @param {Object} config Configuration options
39671 Roo.bootstrap.PhoneInput = function(config) {
39672 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39675 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39677 listWidth: undefined,
39679 selectedClass: 'active',
39681 invalidClass : "has-warning",
39683 validClass: 'has-success',
39685 allowed: '0123456789',
39688 * @cfg {String} defaultDialCode The default dial code when initializing the input
39690 defaultDialCode: '+852',
39693 * @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
39695 preferedCountries: false,
39697 getAutoCreate : function()
39699 var data = Roo.bootstrap.PhoneInputData();
39700 var align = this.labelAlign || this.parentLabelAlign();
39703 this.allCountries = [];
39704 this.dialCodeMapping = [];
39706 for (var i = 0; i < data.length; i++) {
39708 this.allCountries[i] = {
39712 priority: c[3] || 0,
39713 areaCodes: c[4] || null
39715 this.dialCodeMapping[c[2]] = {
39718 priority: c[3] || 0,
39719 areaCodes: c[4] || null
39731 cls : 'form-control tel-input',
39732 autocomplete: 'new-password'
39735 var hiddenInput = {
39738 cls: 'hidden-tel-input'
39742 hiddenInput.name = this.name;
39745 if (this.disabled) {
39746 input.disabled = true;
39749 var flag_container = {
39766 cls: this.hasFeedback ? 'has-feedback' : '',
39772 cls: 'dial-code-holder',
39779 cls: 'roo-select2-container input-group',
39786 if (this.fieldLabel.length) {
39789 tooltip: 'This field is required'
39795 cls: 'control-label',
39801 html: this.fieldLabel
39804 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39810 if(this.indicatorpos == 'right') {
39811 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39818 if(align == 'left') {
39826 if(this.labelWidth > 12){
39827 label.style = "width: " + this.labelWidth + 'px';
39829 if(this.labelWidth < 13 && this.labelmd == 0){
39830 this.labelmd = this.labelWidth;
39832 if(this.labellg > 0){
39833 label.cls += ' col-lg-' + this.labellg;
39834 input.cls += ' col-lg-' + (12 - this.labellg);
39836 if(this.labelmd > 0){
39837 label.cls += ' col-md-' + this.labelmd;
39838 container.cls += ' col-md-' + (12 - this.labelmd);
39840 if(this.labelsm > 0){
39841 label.cls += ' col-sm-' + this.labelsm;
39842 container.cls += ' col-sm-' + (12 - this.labelsm);
39844 if(this.labelxs > 0){
39845 label.cls += ' col-xs-' + this.labelxs;
39846 container.cls += ' col-xs-' + (12 - this.labelxs);
39856 var settings = this;
39858 ['xs','sm','md','lg'].map(function(size){
39859 if (settings[size]) {
39860 cfg.cls += ' col-' + size + '-' + settings[size];
39864 this.store = new Roo.data.Store({
39865 proxy : new Roo.data.MemoryProxy({}),
39866 reader : new Roo.data.JsonReader({
39877 'name' : 'dialCode',
39881 'name' : 'priority',
39885 'name' : 'areaCodes',
39892 if(!this.preferedCountries) {
39893 this.preferedCountries = [
39900 var p = this.preferedCountries.reverse();
39903 for (var i = 0; i < p.length; i++) {
39904 for (var j = 0; j < this.allCountries.length; j++) {
39905 if(this.allCountries[j].iso2 == p[i]) {
39906 var t = this.allCountries[j];
39907 this.allCountries.splice(j,1);
39908 this.allCountries.unshift(t);
39914 this.store.proxy.data = {
39916 data: this.allCountries
39922 initEvents : function()
39925 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39927 this.indicator = this.indicatorEl();
39928 this.flag = this.flagEl();
39929 this.dialCodeHolder = this.dialCodeHolderEl();
39931 this.trigger = this.el.select('div.flag-box',true).first();
39932 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39937 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39938 _this.list.setWidth(lw);
39941 this.list.on('mouseover', this.onViewOver, this);
39942 this.list.on('mousemove', this.onViewMove, this);
39943 this.inputEl().on("keyup", this.onKeyUp, this);
39945 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39947 this.view = new Roo.View(this.list, this.tpl, {
39948 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39951 this.view.on('click', this.onViewClick, this);
39952 this.setValue(this.defaultDialCode);
39955 onTriggerClick : function(e)
39957 Roo.log('trigger click');
39962 if(this.isExpanded()){
39964 this.hasFocus = false;
39966 this.store.load({});
39967 this.hasFocus = true;
39972 isExpanded : function()
39974 return this.list.isVisible();
39977 collapse : function()
39979 if(!this.isExpanded()){
39983 Roo.get(document).un('mousedown', this.collapseIf, this);
39984 Roo.get(document).un('mousewheel', this.collapseIf, this);
39985 this.fireEvent('collapse', this);
39989 expand : function()
39993 if(this.isExpanded() || !this.hasFocus){
39997 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39998 this.list.setWidth(lw);
40001 this.restrictHeight();
40003 Roo.get(document).on('mousedown', this.collapseIf, this);
40004 Roo.get(document).on('mousewheel', this.collapseIf, this);
40006 this.fireEvent('expand', this);
40009 restrictHeight : function()
40011 this.list.alignTo(this.inputEl(), this.listAlign);
40012 this.list.alignTo(this.inputEl(), this.listAlign);
40015 onViewOver : function(e, t)
40017 if(this.inKeyMode){
40020 var item = this.view.findItemFromChild(t);
40023 var index = this.view.indexOf(item);
40024 this.select(index, false);
40029 onViewClick : function(view, doFocus, el, e)
40031 var index = this.view.getSelectedIndexes()[0];
40033 var r = this.store.getAt(index);
40036 this.onSelect(r, index);
40038 if(doFocus !== false && !this.blockFocus){
40039 this.inputEl().focus();
40043 onViewMove : function(e, t)
40045 this.inKeyMode = false;
40048 select : function(index, scrollIntoView)
40050 this.selectedIndex = index;
40051 this.view.select(index);
40052 if(scrollIntoView !== false){
40053 var el = this.view.getNode(index);
40055 this.list.scrollChildIntoView(el, false);
40060 createList : function()
40062 this.list = Roo.get(document.body).createChild({
40064 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40065 style: 'display:none'
40068 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40071 collapseIf : function(e)
40073 var in_combo = e.within(this.el);
40074 var in_list = e.within(this.list);
40075 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40077 if (in_combo || in_list || is_list) {
40083 onSelect : function(record, index)
40085 if(this.fireEvent('beforeselect', this, record, index) !== false){
40087 this.setFlagClass(record.data.iso2);
40088 this.setDialCode(record.data.dialCode);
40089 this.hasFocus = false;
40091 this.fireEvent('select', this, record, index);
40095 flagEl : function()
40097 var flag = this.el.select('div.flag',true).first();
40104 dialCodeHolderEl : function()
40106 var d = this.el.select('input.dial-code-holder',true).first();
40113 setDialCode : function(v)
40115 this.dialCodeHolder.dom.value = '+'+v;
40118 setFlagClass : function(n)
40120 this.flag.dom.className = 'flag '+n;
40123 getValue : function()
40125 var v = this.inputEl().getValue();
40126 if(this.dialCodeHolder) {
40127 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40132 setValue : function(v)
40134 var d = this.getDialCode(v);
40136 //invalid dial code
40137 if(v.length == 0 || !d || d.length == 0) {
40139 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40140 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40146 this.setFlagClass(this.dialCodeMapping[d].iso2);
40147 this.setDialCode(d);
40148 this.inputEl().dom.value = v.replace('+'+d,'');
40149 this.hiddenEl().dom.value = this.getValue();
40154 getDialCode : function(v)
40158 if (v.length == 0) {
40159 return this.dialCodeHolder.dom.value;
40163 if (v.charAt(0) != "+") {
40166 var numericChars = "";
40167 for (var i = 1; i < v.length; i++) {
40168 var c = v.charAt(i);
40171 if (this.dialCodeMapping[numericChars]) {
40172 dialCode = v.substr(1, i);
40174 if (numericChars.length == 4) {
40184 this.setValue(this.defaultDialCode);
40188 hiddenEl : function()
40190 return this.el.select('input.hidden-tel-input',true).first();
40193 onKeyUp : function(e){
40195 var k = e.getKey();
40196 var c = e.getCharCode();
40199 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40200 this.allowed.indexOf(String.fromCharCode(c)) === -1
40205 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40208 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40212 this.setValue(this.getValue());
40217 * @class Roo.bootstrap.MoneyField
40218 * @extends Roo.bootstrap.ComboBox
40219 * Bootstrap MoneyField class
40222 * Create a new MoneyField.
40223 * @param {Object} config Configuration options
40226 Roo.bootstrap.MoneyField = function(config) {
40228 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40232 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40235 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40237 allowDecimals : true,
40239 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40241 decimalSeparator : ".",
40243 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40245 decimalPrecision : 0,
40247 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40249 allowNegative : true,
40251 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40255 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40257 minValue : Number.NEGATIVE_INFINITY,
40259 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40261 maxValue : Number.MAX_VALUE,
40263 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40265 minText : "The minimum value for this field is {0}",
40267 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40269 maxText : "The maximum value for this field is {0}",
40271 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40272 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40274 nanText : "{0} is not a valid number",
40276 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40280 * @cfg {String} defaults currency of the MoneyField
40281 * value should be in lkey
40283 defaultCurrency : false,
40285 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40287 thousandsDelimiter : false,
40297 getAutoCreate : function()
40299 var align = this.labelAlign || this.parentLabelAlign();
40311 cls : 'form-control roo-money-amount-input',
40312 autocomplete: 'new-password'
40315 var hiddenInput = {
40319 cls: 'hidden-number-input'
40323 hiddenInput.name = this.name;
40326 if (this.disabled) {
40327 input.disabled = true;
40330 var clg = 12 - this.inputlg;
40331 var cmd = 12 - this.inputmd;
40332 var csm = 12 - this.inputsm;
40333 var cxs = 12 - this.inputxs;
40337 cls : 'row roo-money-field',
40341 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40345 cls: 'roo-select2-container input-group',
40349 cls : 'form-control roo-money-currency-input',
40350 autocomplete: 'new-password',
40352 name : this.currencyName
40356 cls : 'input-group-addon',
40370 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40374 cls: this.hasFeedback ? 'has-feedback' : '',
40385 if (this.fieldLabel.length) {
40388 tooltip: 'This field is required'
40394 cls: 'control-label',
40400 html: this.fieldLabel
40403 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40409 if(this.indicatorpos == 'right') {
40410 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40417 if(align == 'left') {
40425 if(this.labelWidth > 12){
40426 label.style = "width: " + this.labelWidth + 'px';
40428 if(this.labelWidth < 13 && this.labelmd == 0){
40429 this.labelmd = this.labelWidth;
40431 if(this.labellg > 0){
40432 label.cls += ' col-lg-' + this.labellg;
40433 input.cls += ' col-lg-' + (12 - this.labellg);
40435 if(this.labelmd > 0){
40436 label.cls += ' col-md-' + this.labelmd;
40437 container.cls += ' col-md-' + (12 - this.labelmd);
40439 if(this.labelsm > 0){
40440 label.cls += ' col-sm-' + this.labelsm;
40441 container.cls += ' col-sm-' + (12 - this.labelsm);
40443 if(this.labelxs > 0){
40444 label.cls += ' col-xs-' + this.labelxs;
40445 container.cls += ' col-xs-' + (12 - this.labelxs);
40456 var settings = this;
40458 ['xs','sm','md','lg'].map(function(size){
40459 if (settings[size]) {
40460 cfg.cls += ' col-' + size + '-' + settings[size];
40467 initEvents : function()
40469 this.indicator = this.indicatorEl();
40471 this.initCurrencyEvent();
40473 this.initNumberEvent();
40476 initCurrencyEvent : function()
40479 throw "can not find store for combo";
40482 this.store = Roo.factory(this.store, Roo.data);
40483 this.store.parent = this;
40487 this.triggerEl = this.el.select('.input-group-addon', true).first();
40489 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40494 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40495 _this.list.setWidth(lw);
40498 this.list.on('mouseover', this.onViewOver, this);
40499 this.list.on('mousemove', this.onViewMove, this);
40500 this.list.on('scroll', this.onViewScroll, this);
40503 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40506 this.view = new Roo.View(this.list, this.tpl, {
40507 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40510 this.view.on('click', this.onViewClick, this);
40512 this.store.on('beforeload', this.onBeforeLoad, this);
40513 this.store.on('load', this.onLoad, this);
40514 this.store.on('loadexception', this.onLoadException, this);
40516 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40517 "up" : function(e){
40518 this.inKeyMode = true;
40522 "down" : function(e){
40523 if(!this.isExpanded()){
40524 this.onTriggerClick();
40526 this.inKeyMode = true;
40531 "enter" : function(e){
40534 if(this.fireEvent("specialkey", this, e)){
40535 this.onViewClick(false);
40541 "esc" : function(e){
40545 "tab" : function(e){
40548 if(this.fireEvent("specialkey", this, e)){
40549 this.onViewClick(false);
40557 doRelay : function(foo, bar, hname){
40558 if(hname == 'down' || this.scope.isExpanded()){
40559 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40567 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40571 initNumberEvent : function(e)
40573 this.inputEl().on("keydown" , this.fireKey, this);
40574 this.inputEl().on("focus", this.onFocus, this);
40575 this.inputEl().on("blur", this.onBlur, this);
40577 this.inputEl().relayEvent('keyup', this);
40579 if(this.indicator){
40580 this.indicator.addClass('invisible');
40583 this.originalValue = this.getValue();
40585 if(this.validationEvent == 'keyup'){
40586 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40587 this.inputEl().on('keyup', this.filterValidation, this);
40589 else if(this.validationEvent !== false){
40590 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40593 if(this.selectOnFocus){
40594 this.on("focus", this.preFocus, this);
40597 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40598 this.inputEl().on("keypress", this.filterKeys, this);
40600 this.inputEl().relayEvent('keypress', this);
40603 var allowed = "0123456789";
40605 if(this.allowDecimals){
40606 allowed += this.decimalSeparator;
40609 if(this.allowNegative){
40613 if(this.thousandsDelimiter) {
40617 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40619 var keyPress = function(e){
40621 var k = e.getKey();
40623 var c = e.getCharCode();
40626 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40627 allowed.indexOf(String.fromCharCode(c)) === -1
40633 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40637 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40642 this.inputEl().on("keypress", keyPress, this);
40646 onTriggerClick : function(e)
40653 this.loadNext = false;
40655 if(this.isExpanded()){
40660 this.hasFocus = true;
40662 if(this.triggerAction == 'all') {
40663 this.doQuery(this.allQuery, true);
40667 this.doQuery(this.getRawValue());
40670 getCurrency : function()
40672 var v = this.currencyEl().getValue();
40677 restrictHeight : function()
40679 this.list.alignTo(this.currencyEl(), this.listAlign);
40680 this.list.alignTo(this.currencyEl(), this.listAlign);
40683 onViewClick : function(view, doFocus, el, e)
40685 var index = this.view.getSelectedIndexes()[0];
40687 var r = this.store.getAt(index);
40690 this.onSelect(r, index);
40694 onSelect : function(record, index){
40696 if(this.fireEvent('beforeselect', this, record, index) !== false){
40698 this.setFromCurrencyData(index > -1 ? record.data : false);
40702 this.fireEvent('select', this, record, index);
40706 setFromCurrencyData : function(o)
40710 this.lastCurrency = o;
40712 if (this.currencyField) {
40713 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40715 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40718 this.lastSelectionText = currency;
40720 //setting default currency
40721 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40722 this.setCurrency(this.defaultCurrency);
40726 this.setCurrency(currency);
40729 setFromData : function(o)
40733 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40735 this.setFromCurrencyData(c);
40740 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40742 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40745 this.setValue(value);
40749 setCurrency : function(v)
40751 this.currencyValue = v;
40754 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40759 setValue : function(v)
40761 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40767 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40769 this.inputEl().dom.value = (v == '') ? '' :
40770 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40772 if(!this.allowZero && v === '0') {
40773 this.hiddenEl().dom.value = '';
40774 this.inputEl().dom.value = '';
40781 getRawValue : function()
40783 var v = this.inputEl().getValue();
40788 getValue : function()
40790 return this.fixPrecision(this.parseValue(this.getRawValue()));
40793 parseValue : function(value)
40795 if(this.thousandsDelimiter) {
40797 r = new RegExp(",", "g");
40798 value = value.replace(r, "");
40801 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40802 return isNaN(value) ? '' : value;
40806 fixPrecision : function(value)
40808 if(this.thousandsDelimiter) {
40810 r = new RegExp(",", "g");
40811 value = value.replace(r, "");
40814 var nan = isNaN(value);
40816 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40817 return nan ? '' : value;
40819 return parseFloat(value).toFixed(this.decimalPrecision);
40822 decimalPrecisionFcn : function(v)
40824 return Math.floor(v);
40827 validateValue : function(value)
40829 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40833 var num = this.parseValue(value);
40836 this.markInvalid(String.format(this.nanText, value));
40840 if(num < this.minValue){
40841 this.markInvalid(String.format(this.minText, this.minValue));
40845 if(num > this.maxValue){
40846 this.markInvalid(String.format(this.maxText, this.maxValue));
40853 validate : function()
40855 if(this.disabled || this.allowBlank){
40860 var currency = this.getCurrency();
40862 if(this.validateValue(this.getRawValue()) && currency.length){
40867 this.markInvalid();
40871 getName: function()
40876 beforeBlur : function()
40882 var v = this.parseValue(this.getRawValue());
40889 onBlur : function()
40893 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40894 //this.el.removeClass(this.focusClass);
40897 this.hasFocus = false;
40899 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40903 var v = this.getValue();
40905 if(String(v) !== String(this.startValue)){
40906 this.fireEvent('change', this, v, this.startValue);
40909 this.fireEvent("blur", this);
40912 inputEl : function()
40914 return this.el.select('.roo-money-amount-input', true).first();
40917 currencyEl : function()
40919 return this.el.select('.roo-money-currency-input', true).first();
40922 hiddenEl : function()
40924 return this.el.select('input.hidden-number-input',true).first();