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)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
104 cfg.id = this.id || Roo.id();
106 // fill in the extra attributes
107 if (this.xattr && typeof(this.xattr) =='object') {
108 for (var i in this.xattr) {
109 cfg[i] = this.xattr[i];
114 cfg.dataId = this.dataId;
118 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121 if (this.style) { // fixme needs to support more complex style data.
122 cfg.style = this.style;
126 cfg.name = this.name;
129 this.el = ct.createChild(cfg, position);
132 this.tooltipEl().attr('tooltip', this.tooltip);
135 if(this.tabIndex !== undefined){
136 this.el.dom.setAttribute('tabIndex', this.tabIndex);
143 * Fetch the element to add children to
144 * @return {Roo.Element} defaults to this.el
146 getChildContainer : function()
151 * Fetch the element to display the tooltip on.
152 * @return {Roo.Element} defaults to this.el
154 tooltipEl : function()
159 addxtype : function(tree,cntr)
163 cn = Roo.factory(tree);
165 cn.parentType = this.xtype; //??
166 cn.parentId = this.id;
168 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169 if (typeof(cn.container_method) == 'string') {
170 cntr = cn.container_method;
174 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
176 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
178 var build_from_html = Roo.XComponent.build_from_html;
180 var is_body = (tree.xtype == 'Body') ;
182 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184 var self_cntr_el = Roo.get(this[cntr](false));
186 // do not try and build conditional elements
187 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193 return this.addxtypeChild(tree,cntr);
196 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202 Roo.log('skipping render');
208 if (!build_from_html) {
212 // this i think handles overlaying multiple children of the same type
213 // with the sam eelement.. - which might be buggy..
215 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
221 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
230 addxtypeChild : function (tree, cntr)
232 Roo.debug && Roo.log('addxtypeChild:' + cntr);
234 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238 (typeof(tree['flexy:foreach']) != 'undefined');
242 skip_children = false;
243 // render the element if it's not BODY.
244 if (tree.xtype != 'Body') {
246 cn = Roo.factory(tree);
248 cn.parentType = this.xtype; //??
249 cn.parentId = this.id;
251 var build_from_html = Roo.XComponent.build_from_html;
254 // does the container contain child eleemnts with 'xtype' attributes.
255 // that match this xtype..
256 // note - when we render we create these as well..
257 // so we should check to see if body has xtype set.
258 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260 var self_cntr_el = Roo.get(this[cntr](false));
261 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263 //Roo.log(Roo.XComponent.build_from_html);
264 //Roo.log("got echild:");
267 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268 // and are not displayed -this causes this to use up the wrong element when matching.
269 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
279 //echild.dom.removeAttribute('xtype');
281 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282 Roo.debug && Roo.log(self_cntr_el);
283 Roo.debug && Roo.log(echild);
284 Roo.debug && Roo.log(cn);
290 // if object has flexy:if - then it may or may not be rendered.
291 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
292 // skip a flexy if element.
293 Roo.debug && Roo.log('skipping render');
294 Roo.debug && Roo.log(tree);
296 Roo.debug && Roo.log('skipping all children');
297 skip_children = true;
302 // actually if flexy:foreach is found, we really want to create
303 // multiple copies here...
305 //Roo.log(this[cntr]());
306 cn.render(this[cntr](true));
308 // then add the element..
316 if (typeof (tree.menu) != 'undefined') {
317 tree.menu.parentType = cn.xtype;
318 tree.menu.triggerEl = cn.el;
319 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323 if (!tree.items || !tree.items.length) {
327 var items = tree.items;
330 //Roo.log(items.length);
332 if (!skip_children) {
333 for(var i =0;i < items.length;i++) {
334 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
340 this.fireEvent('childrenrendered', this);
345 * Show a component - removes 'hidden' class
350 this.el.removeClass('hidden');
354 * Hide a component - adds 'hidden' class
358 if (this.el && !this.el.hasClass('hidden')) {
359 this.el.addClass('hidden');
373 * @class Roo.bootstrap.Body
374 * @extends Roo.bootstrap.Component
375 * Bootstrap Body class
379 * @param {Object} config The config object
382 Roo.bootstrap.Body = function(config){
383 Roo.bootstrap.Body.superclass.constructor.call(this, config);
384 this.el = Roo.get(document.body);
385 if (this.cls && this.cls.length) {
386 Roo.get(document.body).addClass(this.cls);
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
395 onRender : function(ct, position)
397 /* Roo.log("Roo.bootstrap.Body - onRender");
398 if (this.cls && this.cls.length) {
399 Roo.get(document.body).addClass(this.cls);
419 * @class Roo.bootstrap.ButtonGroup
420 * @extends Roo.bootstrap.Component
421 * Bootstrap ButtonGroup class
422 * @cfg {String} size lg | sm | xs (default empty normal)
423 * @cfg {String} align vertical | justified (default none)
424 * @cfg {String} direction up | down (default down)
425 * @cfg {Boolean} toolbar false | true
426 * @cfg {Boolean} btn true | false
431 * @param {Object} config The config object
434 Roo.bootstrap.ButtonGroup = function(config){
435 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
446 getAutoCreate : function(){
452 cfg.html = this.html || cfg.html;
463 if (['vertical','justified'].indexOf(this.align)!==-1) {
464 cfg.cls = 'btn-group-' + this.align;
466 if (this.align == 'justified') {
467 console.log(this.items);
471 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472 cfg.cls += ' btn-group-' + this.size;
475 if (this.direction == 'up') {
476 cfg.cls += ' dropup' ;
492 * @class Roo.bootstrap.Button
493 * @extends Roo.bootstrap.Component
494 * Bootstrap Button class
495 * @cfg {String} html The button content
496 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
497 * @cfg {String} size ( lg | sm | xs)
498 * @cfg {String} tag ( a | input | submit)
499 * @cfg {String} href empty or href
500 * @cfg {Boolean} disabled default false;
501 * @cfg {Boolean} isClose default false;
502 * @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)
503 * @cfg {String} badge text for badge
504 * @cfg {String} theme default
505 * @cfg {Boolean} inverse
506 * @cfg {Boolean} toggle
507 * @cfg {String} ontext text for on toggle state
508 * @cfg {String} offtext text for off toggle state
509 * @cfg {Boolean} defaulton
510 * @cfg {Boolean} preventDefault default true
511 * @cfg {Boolean} removeClass remove the standard class..
512 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
515 * Create a new button
516 * @param {Object} config The config object
520 Roo.bootstrap.Button = function(config){
521 Roo.bootstrap.Button.superclass.constructor.call(this, config);
526 * When a butotn is pressed
527 * @param {Roo.bootstrap.Button} this
528 * @param {Roo.EventObject} e
533 * After the button has been toggles
534 * @param {Roo.EventObject} e
535 * @param {boolean} pressed (also available as button.pressed)
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
559 preventDefault: true,
568 getAutoCreate : function(){
576 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
582 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584 if (this.toggle == true) {
587 cls: 'slider-frame roo-button',
592 'data-off-text':'OFF',
593 cls: 'slider-button',
599 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600 cfg.cls += ' '+this.weight;
609 cfg["aria-hidden"] = true;
611 cfg.html = "×";
617 if (this.theme==='default') {
618 cfg.cls = 'btn roo-button';
620 //if (this.parentType != 'Navbar') {
621 this.weight = this.weight.length ? this.weight : 'default';
623 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625 cfg.cls += ' btn-' + this.weight;
627 } else if (this.theme==='glow') {
630 cfg.cls = 'btn-glow roo-button';
632 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' ' + this.weight;
640 this.cls += ' inverse';
645 cfg.cls += ' active';
649 cfg.disabled = 'disabled';
653 Roo.log('changing to ul' );
655 this.glyphicon = 'caret';
658 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660 //gsRoo.log(this.parentType);
661 if (this.parentType === 'Navbar' && !this.parent().bar) {
662 Roo.log('changing to li?');
671 href : this.href || '#'
674 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
675 cfg.cls += ' dropdown';
682 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
684 if (this.glyphicon) {
685 cfg.html = ' ' + cfg.html;
690 cls: 'glyphicon glyphicon-' + this.glyphicon
700 // cfg.cls='btn roo-button';
704 var value = cfg.html;
709 cls: 'glyphicon glyphicon-' + this.glyphicon,
728 cfg.cls += ' dropdown';
729 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
732 if (cfg.tag !== 'a' && this.href !== '') {
733 throw "Tag must be a to set href.";
734 } else if (this.href.length > 0) {
735 cfg.href = this.href;
738 if(this.removeClass){
743 cfg.target = this.target;
748 initEvents: function() {
749 // Roo.log('init events?');
750 // Roo.log(this.el.dom);
753 if (typeof (this.menu) != 'undefined') {
754 this.menu.parentType = this.xtype;
755 this.menu.triggerEl = this.el;
756 this.addxtype(Roo.apply({}, this.menu));
760 if (this.el.hasClass('roo-button')) {
761 this.el.on('click', this.onClick, this);
763 this.el.select('.roo-button').on('click', this.onClick, this);
766 if(this.removeClass){
767 this.el.on('click', this.onClick, this);
770 this.el.enableDisplayMode();
773 onClick : function(e)
780 Roo.log('button on click ');
781 if(this.preventDefault){
784 if (this.pressed === true || this.pressed === false) {
785 this.pressed = !this.pressed;
786 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787 this.fireEvent('toggle', this, e, this.pressed);
791 this.fireEvent('click', this, e);
795 * Enables this button
799 this.disabled = false;
800 this.el.removeClass('disabled');
804 * Disable this button
808 this.disabled = true;
809 this.el.addClass('disabled');
812 * sets the active state on/off,
813 * @param {Boolean} state (optional) Force a particular state
815 setActive : function(v) {
817 this.el[v ? 'addClass' : 'removeClass']('active');
820 * toggles the current active state
822 toggleActive : function()
824 var active = this.el.hasClass('active');
825 this.setActive(!active);
829 setText : function(str)
831 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858 * @class Roo.bootstrap.Column
859 * @extends Roo.bootstrap.Component
860 * Bootstrap Column class
861 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
871 * @cfg {Boolean} hidden (true|false) hide the element
872 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873 * @cfg {String} fa (ban|check|...) font awesome icon
874 * @cfg {Number} fasize (1|2|....) font awsome size
876 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {String} html content of column.
881 * Create a new Column
882 * @param {Object} config The config object
885 Roo.bootstrap.Column = function(config){
886 Roo.bootstrap.Column.superclass.constructor.call(this, config);
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
907 getAutoCreate : function(){
908 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
916 ['xs','sm','md','lg'].map(function(size){
917 //Roo.log( size + ':' + settings[size]);
919 if (settings[size+'off'] !== false) {
920 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
923 if (settings[size] === false) {
926 Roo.log(settings[size]);
927 if (!settings[size]) { // 0 = hidden
928 cfg.cls += ' hidden-' + size;
931 cfg.cls += ' col-' + size + '-' + settings[size];
936 cfg.cls += ' hidden';
939 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940 cfg.cls +=' alert alert-' + this.alert;
944 if (this.html.length) {
945 cfg.html = this.html;
949 if (this.fasize > 1) {
950 fasize = ' fa-' + this.fasize + 'x';
952 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
957 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
976 * @class Roo.bootstrap.Container
977 * @extends Roo.bootstrap.Component
978 * Bootstrap Container class
979 * @cfg {Boolean} jumbotron is it a jumbotron element
980 * @cfg {String} html content of element
981 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983 * @cfg {String} header content of header (for panel)
984 * @cfg {String} footer content of footer (for panel)
985 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986 * @cfg {String} tag (header|aside|section) type of HTML tag.
987 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988 * @cfg {String} fa (ban|check|...) font awesome icon
989 * @cfg {String} icon (info-sign|check|...) glyphicon name
990 * @cfg {Boolean} hidden (true|false) hide the element
991 * @cfg {Boolean} expandable (true|false) default false
992 * @cfg {Boolean} expanded (true|false) default true
993 * @cfg {String} rheader contet on the right of header
997 * Create a new Container
998 * @param {Object} config The config object
1001 Roo.bootstrap.Container = function(config){
1002 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1008 * After the panel has been expand
1010 * @param {Roo.bootstrap.Container} this
1015 * After the panel has been collapsed
1017 * @param {Roo.bootstrap.Container} this
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1040 getChildContainer : function() {
1046 if (this.panel.length) {
1047 return this.el.select('.panel-body',true).first();
1054 getAutoCreate : function(){
1057 tag : this.tag || 'div',
1061 if (this.jumbotron) {
1062 cfg.cls = 'jumbotron';
1067 // - this is applied by the parent..
1069 // cfg.cls = this.cls + '';
1072 if (this.sticky.length) {
1074 var bd = Roo.get(document.body);
1075 if (!bd.hasClass('bootstrap-sticky')) {
1076 bd.addClass('bootstrap-sticky');
1077 Roo.select('html',true).setStyle('height', '100%');
1080 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1084 if (this.well.length) {
1085 switch (this.well) {
1088 cfg.cls +=' well well-' +this.well;
1097 cfg.cls += ' hidden';
1101 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102 cfg.cls +=' alert alert-' + this.alert;
1107 if (this.panel.length) {
1108 cfg.cls += ' panel panel-' + this.panel;
1110 if (this.header.length) {
1114 if(this.expandable){
1116 cfg.cls = cfg.cls + ' expandable';
1120 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1128 cls : 'panel-title',
1129 html : (this.expandable ? ' ' : '') + this.header
1133 cls: 'panel-header-right',
1139 cls : 'panel-heading',
1140 style : this.expandable ? 'cursor: pointer' : '',
1148 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1153 if (this.footer.length) {
1155 cls : 'panel-footer',
1164 body.html = this.html || cfg.html;
1165 // prefix with the icons..
1167 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1170 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1175 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176 cfg.cls = 'container';
1182 initEvents: function()
1184 if(!this.expandable){
1188 var headerEl = this.headerEl();
1194 headerEl.on('click', this.onToggleClick, this);
1198 onToggleClick : function()
1200 var headerEl = this.headerEl();
1216 if(this.fireEvent('expand', this)) {
1218 this.expanded = true;
1220 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1222 this.el.select('.panel-body',true).first().removeClass('hide');
1224 var toggleEl = this.toggleEl();
1230 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1235 collapse : function()
1237 if(this.fireEvent('collapse', this)) {
1239 this.expanded = false;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242 this.el.select('.panel-body',true).first().addClass('hide');
1244 var toggleEl = this.toggleEl();
1250 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1254 toggleEl : function()
1256 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1260 return this.el.select('.panel-heading .fa',true).first();
1263 headerEl : function()
1265 if(!this.el || !this.panel.length || !this.header.length){
1269 return this.el.select('.panel-heading',true).first()
1272 titleEl : function()
1274 if(!this.el || !this.panel.length || !this.header.length){
1278 return this.el.select('.panel-title',true).first();
1281 setTitle : function(v)
1283 var titleEl = this.titleEl();
1289 titleEl.dom.innerHTML = v;
1292 getTitle : function()
1295 var titleEl = this.titleEl();
1301 return titleEl.dom.innerHTML;
1304 setRightTitle : function(v)
1306 var t = this.el.select('.panel-header-right',true).first();
1312 t.dom.innerHTML = v;
1326 * @class Roo.bootstrap.Img
1327 * @extends Roo.bootstrap.Component
1328 * Bootstrap Img class
1329 * @cfg {Boolean} imgResponsive false | true
1330 * @cfg {String} border rounded | circle | thumbnail
1331 * @cfg {String} src image source
1332 * @cfg {String} alt image alternative text
1333 * @cfg {String} href a tag href
1334 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335 * @cfg {String} xsUrl xs image source
1336 * @cfg {String} smUrl sm image source
1337 * @cfg {String} mdUrl md image source
1338 * @cfg {String} lgUrl lg image source
1341 * Create a new Input
1342 * @param {Object} config The config object
1345 Roo.bootstrap.Img = function(config){
1346 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1352 * The img click event for the img.
1353 * @param {Roo.EventObject} e
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1361 imgResponsive: true,
1371 getAutoCreate : function()
1373 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374 return this.createSingleImg();
1379 cls: 'roo-image-responsive-group',
1384 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1386 if(!_this[size + 'Url']){
1392 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393 html: _this.html || cfg.html,
1394 src: _this[size + 'Url']
1397 img.cls += ' roo-image-responsive-' + size;
1399 var s = ['xs', 'sm', 'md', 'lg'];
1401 s.splice(s.indexOf(size), 1);
1403 Roo.each(s, function(ss){
1404 img.cls += ' hidden-' + ss;
1407 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408 cfg.cls += ' img-' + _this.border;
1412 cfg.alt = _this.alt;
1425 a.target = _this.target;
1429 cfg.cn.push((_this.href) ? a : img);
1436 createSingleImg : function()
1440 cls: (this.imgResponsive) ? 'img-responsive' : '',
1444 cfg.html = this.html || cfg.html;
1446 cfg.src = this.src || cfg.src;
1448 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449 cfg.cls += ' img-' + this.border;
1466 a.target = this.target;
1471 return (this.href) ? a : cfg;
1474 initEvents: function()
1477 this.el.on('click', this.onClick, this);
1482 onClick : function(e)
1484 Roo.log('img onclick');
1485 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Link
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Link Class
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505 * @cfg {String} html the content of the link.
1506 * @cfg {String} anchor name for the anchor link
1508 * @cfg {Boolean} preventDefault (true | false) default false
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Link = function(config){
1517 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1534 preventDefault: false,
1538 getAutoCreate : function()
1544 // anchor's do not require html/href...
1545 if (this.anchor === false) {
1546 cfg.html = this.html || '';
1547 cfg.href = this.href || '#';
1549 cfg.name = this.anchor;
1550 if (this.html !== false) {
1551 cfg.html = this.html;
1553 if (this.href !== false) {
1554 cfg.href = this.href;
1558 if(this.alt !== false){
1563 if(this.target !== false) {
1564 cfg.target = this.target;
1570 initEvents: function() {
1572 if(!this.href || this.preventDefault){
1573 this.el.on('click', this.onClick, this);
1577 onClick : function(e)
1579 if(this.preventDefault){
1582 //Roo.log('img onclick');
1583 this.fireEvent('click', this, e);
1596 * @class Roo.bootstrap.Header
1597 * @extends Roo.bootstrap.Component
1598 * Bootstrap Header class
1599 * @cfg {String} html content of header
1600 * @cfg {Number} level (1|2|3|4|5|6) default 1
1603 * Create a new Header
1604 * @param {Object} config The config object
1608 Roo.bootstrap.Header = function(config){
1609 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1620 getAutoCreate : function(){
1625 tag: 'h' + (1 *this.level),
1626 html: this.html || ''
1638 * Ext JS Library 1.1.1
1639 * Copyright(c) 2006-2007, Ext JS, LLC.
1641 * Originally Released Under LGPL - original licence link has changed is not relivant.
1644 * <script type="text/javascript">
1648 * @class Roo.bootstrap.MenuMgr
1649 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1652 Roo.bootstrap.MenuMgr = function(){
1653 var menus, active, groups = {}, attached = false, lastShow = new Date();
1655 // private - called when first menu is created
1658 active = new Roo.util.MixedCollection();
1659 Roo.get(document).addKeyListener(27, function(){
1660 if(active.length > 0){
1668 if(active && active.length > 0){
1669 var c = active.clone();
1679 if(active.length < 1){
1680 Roo.get(document).un("mouseup", onMouseDown);
1688 var last = active.last();
1689 lastShow = new Date();
1692 Roo.get(document).on("mouseup", onMouseDown);
1697 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698 m.parentMenu.activeChild = m;
1699 }else if(last && last.isVisible()){
1700 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1705 function onBeforeHide(m){
1707 m.activeChild.hide();
1709 if(m.autoHideTimer){
1710 clearTimeout(m.autoHideTimer);
1711 delete m.autoHideTimer;
1716 function onBeforeShow(m){
1717 var pm = m.parentMenu;
1718 if(!pm && !m.allowOtherMenus){
1720 }else if(pm && pm.activeChild && active != m){
1721 pm.activeChild.hide();
1725 // private this should really trigger on mouseup..
1726 function onMouseDown(e){
1727 Roo.log("on Mouse Up");
1728 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1738 function onBeforeCheck(mi, state){
1740 var g = groups[mi.group];
1741 for(var i = 0, l = g.length; i < l; i++){
1743 g[i].setChecked(false);
1752 * Hides all menus that are currently visible
1754 hideAll : function(){
1759 register : function(menu){
1763 menus[menu.id] = menu;
1764 menu.on("beforehide", onBeforeHide);
1765 menu.on("hide", onHide);
1766 menu.on("beforeshow", onBeforeShow);
1767 menu.on("show", onShow);
1769 if(g && menu.events["checkchange"]){
1773 groups[g].push(menu);
1774 menu.on("checkchange", onCheck);
1779 * Returns a {@link Roo.menu.Menu} object
1780 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781 * be used to generate and return a new Menu instance.
1783 get : function(menu){
1784 if(typeof menu == "string"){ // menu id
1786 }else if(menu.events){ // menu instance
1789 /*else if(typeof menu.length == 'number'){ // array of menu items?
1790 return new Roo.bootstrap.Menu({items:menu});
1791 }else{ // otherwise, must be a config
1792 return new Roo.bootstrap.Menu(menu);
1799 unregister : function(menu){
1800 delete menus[menu.id];
1801 menu.un("beforehide", onBeforeHide);
1802 menu.un("hide", onHide);
1803 menu.un("beforeshow", onBeforeShow);
1804 menu.un("show", onShow);
1806 if(g && menu.events["checkchange"]){
1807 groups[g].remove(menu);
1808 menu.un("checkchange", onCheck);
1813 registerCheckable : function(menuItem){
1814 var g = menuItem.group;
1819 groups[g].push(menuItem);
1820 menuItem.on("beforecheckchange", onBeforeCheck);
1825 unregisterCheckable : function(menuItem){
1826 var g = menuItem.group;
1828 groups[g].remove(menuItem);
1829 menuItem.un("beforecheckchange", onBeforeCheck);
1841 * @class Roo.bootstrap.Menu
1842 * @extends Roo.bootstrap.Component
1843 * Bootstrap Menu class - container for MenuItems
1844 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1848 * @param {Object} config The config object
1852 Roo.bootstrap.Menu = function(config){
1853 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854 if (this.registerMenu) {
1855 Roo.bootstrap.MenuMgr.register(this);
1860 * Fires before this menu is displayed
1861 * @param {Roo.menu.Menu} this
1866 * Fires before this menu is hidden
1867 * @param {Roo.menu.Menu} this
1872 * Fires after this menu is displayed
1873 * @param {Roo.menu.Menu} this
1878 * Fires after this menu is hidden
1879 * @param {Roo.menu.Menu} this
1884 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885 * @param {Roo.menu.Menu} this
1886 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887 * @param {Roo.EventObject} e
1892 * Fires when the mouse is hovering over this menu
1893 * @param {Roo.menu.Menu} this
1894 * @param {Roo.EventObject} e
1895 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1900 * Fires when the mouse exits this menu
1901 * @param {Roo.menu.Menu} this
1902 * @param {Roo.EventObject} e
1903 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908 * Fires when a menu item contained in this menu is clicked
1909 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910 * @param {Roo.EventObject} e
1914 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1921 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1924 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926 registerMenu : true,
1928 menuItems :false, // stores the menu items..
1934 getChildContainer : function() {
1938 getAutoCreate : function(){
1940 //if (['right'].indexOf(this.align)!==-1) {
1941 // cfg.cn[1].cls += ' pull-right'
1947 cls : 'dropdown-menu' ,
1948 style : 'z-index:1000'
1952 if (this.type === 'submenu') {
1953 cfg.cls = 'submenu active';
1955 if (this.type === 'treeview') {
1956 cfg.cls = 'treeview-menu';
1961 initEvents : function() {
1963 // Roo.log("ADD event");
1964 // Roo.log(this.triggerEl.dom);
1965 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967 this.triggerEl.addClass('dropdown-toggle');
1968 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1970 this.el.on("mouseover", this.onMouseOver, this);
1971 this.el.on("mouseout", this.onMouseOut, this);
1975 findTargetItem : function(e){
1976 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1980 //Roo.log(t); Roo.log(t.id);
1982 //Roo.log(this.menuitems);
1983 return this.menuitems.get(t.id);
1985 //return this.items.get(t.menuItemId);
1990 onClick : function(e){
1991 Roo.log("menu.onClick");
1992 var t = this.findTargetItem(e);
1993 if(!t || t.isContainer){
1998 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1999 if(t == this.activeItem && t.shouldDeactivate(e)){
2000 this.activeItem.deactivate();
2001 delete this.activeItem;
2005 this.setActiveItem(t, true);
2013 Roo.log('pass click event');
2017 this.fireEvent("click", this, t, e);
2021 onMouseOver : function(e){
2022 var t = this.findTargetItem(e);
2025 // if(t.canActivate && !t.disabled){
2026 // this.setActiveItem(t, true);
2030 this.fireEvent("mouseover", this, e, t);
2032 isVisible : function(){
2033 return !this.hidden;
2035 onMouseOut : function(e){
2036 var t = this.findTargetItem(e);
2039 // if(t == this.activeItem && t.shouldDeactivate(e)){
2040 // this.activeItem.deactivate();
2041 // delete this.activeItem;
2044 this.fireEvent("mouseout", this, e, t);
2049 * Displays this menu relative to another element
2050 * @param {String/HTMLElement/Roo.Element} element The element to align to
2051 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052 * the element (defaults to this.defaultAlign)
2053 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2055 show : function(el, pos, parentMenu){
2056 this.parentMenu = parentMenu;
2060 this.fireEvent("beforeshow", this);
2061 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2064 * Displays this menu at a specific xy position
2065 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068 showAt : function(xy, parentMenu, /* private: */_e){
2069 this.parentMenu = parentMenu;
2074 this.fireEvent("beforeshow", this);
2075 //xy = this.el.adjustForConstraints(xy);
2079 this.hideMenuItems();
2080 this.hidden = false;
2081 this.triggerEl.addClass('open');
2083 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2089 this.fireEvent("show", this);
2095 this.doFocus.defer(50, this);
2099 doFocus : function(){
2101 this.focusEl.focus();
2106 * Hides this menu and optionally all parent menus
2107 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2109 hide : function(deep){
2111 this.hideMenuItems();
2112 if(this.el && this.isVisible()){
2113 this.fireEvent("beforehide", this);
2114 if(this.activeItem){
2115 this.activeItem.deactivate();
2116 this.activeItem = null;
2118 this.triggerEl.removeClass('open');;
2120 this.fireEvent("hide", this);
2122 if(deep === true && this.parentMenu){
2123 this.parentMenu.hide(true);
2127 onTriggerPress : function(e)
2130 Roo.log('trigger press');
2131 //Roo.log(e.getTarget());
2132 // Roo.log(this.triggerEl.dom);
2133 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2137 if (this.isVisible()) {
2142 this.show(this.triggerEl, false, false);
2151 hideMenuItems : function()
2153 //$(backdrop).remove()
2154 Roo.select('.open',true).each(function(aa) {
2156 aa.removeClass('open');
2157 //var parent = getParent($(this))
2158 //var relatedTarget = { relatedTarget: this }
2160 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161 //if (e.isDefaultPrevented()) return
2162 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2165 addxtypeChild : function (tree, cntr) {
2166 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2168 this.menuitems.add(comp);
2189 * @class Roo.bootstrap.MenuItem
2190 * @extends Roo.bootstrap.Component
2191 * Bootstrap MenuItem class
2192 * @cfg {String} html the menu label
2193 * @cfg {String} href the link
2194 * @cfg {Boolean} preventDefault (true | false) default true
2195 * @cfg {Boolean} isContainer (true | false) default false
2199 * Create a new MenuItem
2200 * @param {Object} config The config object
2204 Roo.bootstrap.MenuItem = function(config){
2205 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2210 * The raw click event for the entire grid.
2211 * @param {Roo.bootstrap.MenuItem} this
2212 * @param {Roo.EventObject} e
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2222 preventDefault: true,
2223 isContainer : false,
2225 getAutoCreate : function(){
2227 if(this.isContainer){
2230 cls: 'dropdown-menu-item'
2236 cls: 'dropdown-menu-item',
2245 if (this.parent().type == 'treeview') {
2246 cfg.cls = 'treeview-menu';
2249 cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2254 initEvents: function() {
2256 //this.el.select('a').on('click', this.onClick, this);
2259 onClick : function(e)
2261 Roo.log('item on click ');
2262 //if(this.preventDefault){
2263 // e.preventDefault();
2265 //this.parent().hideMenuItems();
2267 this.fireEvent('click', this, e);
2286 * @class Roo.bootstrap.MenuSeparator
2287 * @extends Roo.bootstrap.Component
2288 * Bootstrap MenuSeparator class
2291 * Create a new MenuItem
2292 * @param {Object} config The config object
2296 Roo.bootstrap.MenuSeparator = function(config){
2297 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2302 getAutoCreate : function(){
2321 * @class Roo.bootstrap.Modal
2322 * @extends Roo.bootstrap.Component
2323 * Bootstrap Modal class
2324 * @cfg {String} title Title of dialog
2325 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2327 * @cfg {Boolean} specificTitle default false
2328 * @cfg {Array} buttons Array of buttons or standard button set..
2329 * @cfg {String} buttonPosition (left|right|center) default right
2330 * @cfg {Boolean} animate default true
2331 * @cfg {Boolean} allow_close default true
2334 * Create a new Modal Dialog
2335 * @param {Object} config The config object
2338 Roo.bootstrap.Modal = function(config){
2339 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2344 * The raw btnclick event for the button
2345 * @param {Roo.EventObject} e
2349 this.buttons = this.buttons || [];
2352 this.tmpl = Roo.factory(this.tmpl);
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2359 title : 'test dialog',
2369 specificTitle: false,
2371 buttonPosition: 'right',
2385 onRender : function(ct, position)
2387 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2390 var cfg = Roo.apply({}, this.getAutoCreate());
2393 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2395 //if (!cfg.name.length) {
2399 cfg.cls += ' ' + this.cls;
2402 cfg.style = this.style;
2404 this.el = Roo.get(document.body).createChild(cfg, position);
2406 //var type = this.el.dom.type;
2411 if(this.tabIndex !== undefined){
2412 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2416 this.bodyEl = this.el.select('.modal-body',true).first();
2417 this.closeEl = this.el.select('.modal-header .close', true).first();
2418 this.footerEl = this.el.select('.modal-footer',true).first();
2419 this.titleEl = this.el.select('.modal-title',true).first();
2423 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424 this.maskEl.enableDisplayMode("block");
2426 //this.el.addClass("x-dlg-modal");
2428 if (this.buttons.length) {
2429 Roo.each(this.buttons, function(bb) {
2430 var b = Roo.apply({}, bb);
2431 b.xns = b.xns || Roo.bootstrap;
2432 b.xtype = b.xtype || 'Button';
2433 if (typeof(b.listeners) == 'undefined') {
2434 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2437 var btn = Roo.factory(b);
2439 btn.onRender(this.el.select('.modal-footer div').first());
2443 // render the children.
2446 if(typeof(this.items) != 'undefined'){
2447 var items = this.items;
2450 for(var i =0;i < items.length;i++) {
2451 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2455 this.items = nitems;
2457 // where are these used - they used to be body/close/footer
2461 //this.el.addClass([this.fieldClass, this.cls]);
2465 getAutoCreate : function(){
2470 html : this.html || ''
2475 cls : 'modal-title',
2479 if(this.specificTitle){
2485 if (this.allow_close) {
2496 style : 'display: none',
2499 cls: "modal-dialog",
2502 cls : "modal-content",
2505 cls : 'modal-header',
2510 cls : 'modal-footer',
2514 cls: 'btn-' + this.buttonPosition
2531 modal.cls += ' fade';
2537 getChildContainer : function() {
2542 getButtonContainer : function() {
2543 return this.el.select('.modal-footer div',true).first();
2546 initEvents : function()
2548 if (this.allow_close) {
2549 this.closeEl.on('click', this.hide, this);
2554 window.addEventListener("resize", function() { _this.resize(); } );
2560 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2565 if (!this.rendered) {
2569 this.el.setStyle('display', 'block');
2573 (function(){ _this.el.addClass('in'); }).defer(50);
2575 this.el.addClass('in');
2578 // not sure how we can show data in here..
2580 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2583 Roo.get(document.body).addClass("x-body-masked");
2584 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2586 this.el.setStyle('zIndex', '10001');
2588 this.fireEvent('show', this);
2595 Roo.get(document.body).removeClass("x-body-masked");
2596 this.el.removeClass('in');
2600 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2602 this.el.setStyle('display', 'none');
2605 this.fireEvent('hide', this);
2608 addButton : function(str, cb)
2612 var b = Roo.apply({}, { html : str } );
2613 b.xns = b.xns || Roo.bootstrap;
2614 b.xtype = b.xtype || 'Button';
2615 if (typeof(b.listeners) == 'undefined') {
2616 b.listeners = { click : cb.createDelegate(this) };
2619 var btn = Roo.factory(b);
2621 btn.onRender(this.el.select('.modal-footer div').first());
2627 setDefaultButton : function(btn)
2629 //this.el.select('.modal-footer').()
2631 resizeTo: function(w,h)
2635 setContentSize : function(w, h)
2639 onButtonClick: function(btn,e)
2642 this.fireEvent('btnclick', btn.name, e);
2645 * Set the title of the Dialog
2646 * @param {String} str new Title
2648 setTitle: function(str) {
2649 this.titleEl.dom.innerHTML = str;
2652 * Set the body of the Dialog
2653 * @param {String} str new Title
2655 setBody: function(str) {
2656 this.bodyEl.dom.innerHTML = str;
2659 * Set the body of the Dialog using the template
2660 * @param {Obj} data - apply this data to the template and replace the body contents.
2662 applyBody: function(obj)
2665 Roo.log("Error - using apply Body without a template");
2668 this.tmpl.overwrite(this.bodyEl, obj);
2674 Roo.apply(Roo.bootstrap.Modal, {
2676 * Button config that displays a single OK button
2685 * Button config that displays Yes and No buttons
2701 * Button config that displays OK and Cancel buttons
2716 * Button config that displays Yes, No and Cancel buttons
2739 * messagebox - can be used as a replace
2743 * @class Roo.MessageBox
2744 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2753 // process text value...
2757 // Show a dialog using config options:
2759 title:'Save Changes?',
2760 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761 buttons: Roo.Msg.YESNOCANCEL,
2768 Roo.bootstrap.MessageBox = function(){
2769 var dlg, opt, mask, waitTimer;
2770 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771 var buttons, activeTextEl, bwidth;
2775 var handleButton = function(button){
2777 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2781 var handleHide = function(){
2783 dlg.el.removeClass(opt.cls);
2786 // Roo.TaskMgr.stop(waitTimer);
2787 // waitTimer = null;
2792 var updateButtons = function(b){
2795 buttons["ok"].hide();
2796 buttons["cancel"].hide();
2797 buttons["yes"].hide();
2798 buttons["no"].hide();
2799 //dlg.footer.dom.style.display = 'none';
2802 dlg.footerEl.dom.style.display = '';
2803 for(var k in buttons){
2804 if(typeof buttons[k] != "function"){
2807 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808 width += buttons[k].el.getWidth()+15;
2818 var handleEsc = function(d, k, e){
2819 if(opt && opt.closable !== false){
2829 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830 * @return {Roo.BasicDialog} The BasicDialog element
2832 getDialog : function(){
2834 dlg = new Roo.bootstrap.Modal( {
2837 //constraintoviewport:false,
2839 //collapsible : false,
2844 //buttonAlign:"center",
2845 closeClick : function(){
2846 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2849 handleButton("cancel");
2854 dlg.on("hide", handleHide);
2856 //dlg.addKeyListener(27, handleEsc);
2858 this.buttons = buttons;
2859 var bt = this.buttonText;
2860 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2865 bodyEl = dlg.bodyEl.createChild({
2867 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868 '<textarea class="roo-mb-textarea"></textarea>' +
2869 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2871 msgEl = bodyEl.dom.firstChild;
2872 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873 textboxEl.enableDisplayMode();
2874 textboxEl.addKeyListener([10,13], function(){
2875 if(dlg.isVisible() && opt && opt.buttons){
2878 }else if(opt.buttons.yes){
2879 handleButton("yes");
2883 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884 textareaEl.enableDisplayMode();
2885 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886 progressEl.enableDisplayMode();
2887 var pf = progressEl.dom.firstChild;
2889 pp = Roo.get(pf.firstChild);
2890 pp.setHeight(pf.offsetHeight);
2898 * Updates the message box body text
2899 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900 * the XHTML-compliant non-breaking space character '&#160;')
2901 * @return {Roo.MessageBox} This message box
2903 updateText : function(text){
2904 if(!dlg.isVisible() && !opt.width){
2905 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2907 msgEl.innerHTML = text || ' ';
2909 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2912 Math.min(opt.width || cw , this.maxWidth),
2913 Math.max(opt.minWidth || this.minWidth, bwidth)
2916 activeTextEl.setWidth(w);
2918 if(dlg.isVisible()){
2919 dlg.fixedcenter = false;
2921 // to big, make it scroll. = But as usual stupid IE does not support
2924 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2928 bodyEl.dom.style.height = '';
2929 bodyEl.dom.style.overflowY = '';
2932 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2934 bodyEl.dom.style.overflowX = '';
2937 dlg.setContentSize(w, bodyEl.getHeight());
2938 if(dlg.isVisible()){
2939 dlg.fixedcenter = true;
2945 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2946 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949 * @return {Roo.MessageBox} This message box
2951 updateProgress : function(value, text){
2953 this.updateText(text);
2955 if (pp) { // weird bug on my firefox - for some reason this is not defined
2956 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2962 * Returns true if the message box is currently displayed
2963 * @return {Boolean} True if the message box is visible, else false
2965 isVisible : function(){
2966 return dlg && dlg.isVisible();
2970 * Hides the message box if it is displayed
2973 if(this.isVisible()){
2979 * Displays a new message box, or reinitializes an existing message box, based on the config options
2980 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981 * The following config object properties are supported:
2983 Property Type Description
2984 ---------- --------------- ------------------------------------------------------------------------------------
2985 animEl String/Element An id or Element from which the message box should animate as it opens and
2986 closes (defaults to undefined)
2987 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable Boolean False to hide the top-right close button (defaults to true). Note that
2990 progress and wait dialogs will ignore this property and always hide the
2991 close button as they can only be closed programmatically.
2992 cls String A custom CSS class to apply to the message box element
2993 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2994 displayed (defaults to 75)
2995 fn Function A callback function to execute after closing the dialog. The arguments to the
2996 function will be btn (the name of the button that was clicked, if applicable,
2997 e.g. "ok"), and text (the value of the active text field, if applicable).
2998 Progress and wait dialogs will ignore this option since they do not respond to
2999 user actions and can only be closed programmatically, so any required function
3000 should be called by the same code after it closes the dialog.
3001 icon String A CSS class that provides a background image to be used as an icon for
3002 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3004 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3005 modal Boolean False to allow user interaction with the page while the message box is
3006 displayed (defaults to true)
3007 msg String A string that will replace the existing message box body text (defaults
3008 to the XHTML-compliant non-breaking space character ' ')
3009 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3010 progress Boolean True to display a progress bar (defaults to false)
3011 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3014 title String The title text
3015 value String The string value to set into the active textbox element if displayed
3016 wait Boolean True to display a progress bar (defaults to false)
3017 width Number The width of the dialog in pixels
3024 msg: 'Please enter your address:',
3026 buttons: Roo.MessageBox.OKCANCEL,
3029 animEl: 'addAddressBtn'
3032 * @param {Object} config Configuration options
3033 * @return {Roo.MessageBox} This message box
3035 show : function(options)
3038 // this causes nightmares if you show one dialog after another
3039 // especially on callbacks..
3041 if(this.isVisible()){
3044 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3046 Roo.log("New Dialog Message:" + options.msg )
3047 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3051 var d = this.getDialog();
3053 d.setTitle(opt.title || " ");
3054 d.closeEl.setDisplayed(opt.closable !== false);
3055 activeTextEl = textboxEl;
3056 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3061 textareaEl.setHeight(typeof opt.multiline == "number" ?
3062 opt.multiline : this.defaultTextHeight);
3063 activeTextEl = textareaEl;
3072 progressEl.setDisplayed(opt.progress === true);
3073 this.updateProgress(0);
3074 activeTextEl.dom.value = opt.value || "";
3076 dlg.setDefaultButton(activeTextEl);
3078 var bs = opt.buttons;
3082 }else if(bs && bs.yes){
3083 db = buttons["yes"];
3085 dlg.setDefaultButton(db);
3087 bwidth = updateButtons(opt.buttons);
3088 this.updateText(opt.msg);
3090 d.el.addClass(opt.cls);
3092 d.proxyDrag = opt.proxyDrag === true;
3093 d.modal = opt.modal !== false;
3094 d.mask = opt.modal !== false ? mask : false;
3096 // force it to the end of the z-index stack so it gets a cursor in FF
3097 document.body.appendChild(dlg.el.dom);
3098 d.animateTarget = null;
3099 d.show(options.animEl);
3105 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3106 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107 * and closing the message box when the process is complete.
3108 * @param {String} title The title bar text
3109 * @param {String} msg The message box body text
3110 * @return {Roo.MessageBox} This message box
3112 progress : function(title, msg){
3119 minWidth: this.minProgressWidth,
3126 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127 * If a callback function is passed it will be called after the user clicks the button, and the
3128 * id of the button that was clicked will be passed as the only parameter to the callback
3129 * (could also be the top-right close button).
3130 * @param {String} title The title bar text
3131 * @param {String} msg The message box body text
3132 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133 * @param {Object} scope (optional) The scope of the callback function
3134 * @return {Roo.MessageBox} This message box
3136 alert : function(title, msg, fn, scope){
3149 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3150 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151 * You are responsible for closing the message box when the process is complete.
3152 * @param {String} msg The message box body text
3153 * @param {String} title (optional) The title bar text
3154 * @return {Roo.MessageBox} This message box
3156 wait : function(msg, title){
3167 waitTimer = Roo.TaskMgr.start({
3169 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3177 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180 * @param {String} title The title bar text
3181 * @param {String} msg The message box body text
3182 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183 * @param {Object} scope (optional) The scope of the callback function
3184 * @return {Roo.MessageBox} This message box
3186 confirm : function(title, msg, fn, scope){
3190 buttons: this.YESNO,
3199 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3201 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202 * (could also be the top-right close button) and the text that was entered will be passed as the two
3203 * parameters to the callback.
3204 * @param {String} title The title bar text
3205 * @param {String} msg The message box body text
3206 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207 * @param {Object} scope (optional) The scope of the callback function
3208 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210 * @return {Roo.MessageBox} This message box
3212 prompt : function(title, msg, fn, scope, multiline){
3216 buttons: this.OKCANCEL,
3221 multiline: multiline,
3228 * Button config that displays a single OK button
3233 * Button config that displays Yes and No buttons
3236 YESNO : {yes:true, no:true},
3238 * Button config that displays OK and Cancel buttons
3241 OKCANCEL : {ok:true, cancel:true},
3243 * Button config that displays Yes, No and Cancel buttons
3246 YESNOCANCEL : {yes:true, no:true, cancel:true},
3249 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3252 defaultTextHeight : 75,
3254 * The maximum width in pixels of the message box (defaults to 600)
3259 * The minimum width in pixels of the message box (defaults to 100)
3264 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3265 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3268 minProgressWidth : 250,
3270 * An object containing the default button text strings that can be overriden for localized language support.
3271 * Supported properties are: ok, cancel, yes and no.
3272 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3285 * Shorthand for {@link Roo.MessageBox}
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3297 * @class Roo.bootstrap.Navbar
3298 * @extends Roo.bootstrap.Component
3299 * Bootstrap Navbar class
3302 * Create a new Navbar
3303 * @param {Object} config The config object
3307 Roo.bootstrap.Navbar = function(config){
3308 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3321 getAutoCreate : function(){
3324 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3328 initEvents :function ()
3330 //Roo.log(this.el.select('.navbar-toggle',true));
3331 this.el.select('.navbar-toggle',true).on('click', function() {
3332 // Roo.log('click');
3333 this.el.select('.navbar-collapse',true).toggleClass('in');
3341 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3343 var size = this.el.getSize();
3344 this.maskEl.setSize(size.width, size.height);
3345 this.maskEl.enableDisplayMode("block");
3354 getChildContainer : function()
3356 if (this.el.select('.collapse').getCount()) {
3357 return this.el.select('.collapse',true).first();
3390 * @class Roo.bootstrap.NavSimplebar
3391 * @extends Roo.bootstrap.Navbar
3392 * Bootstrap Sidebar class
3394 * @cfg {Boolean} inverse is inverted color
3396 * @cfg {String} type (nav | pills | tabs)
3397 * @cfg {Boolean} arrangement stacked | justified
3398 * @cfg {String} align (left | right) alignment
3400 * @cfg {Boolean} main (true|false) main nav bar? default false
3401 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3403 * @cfg {String} tag (header|footer|nav|div) default is nav
3409 * Create a new Sidebar
3410 * @param {Object} config The config object
3414 Roo.bootstrap.NavSimplebar = function(config){
3415 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3434 getAutoCreate : function(){
3438 tag : this.tag || 'div',
3451 this.type = this.type || 'nav';
3452 if (['tabs','pills'].indexOf(this.type)!==-1) {
3453 cfg.cn[0].cls += ' nav-' + this.type
3457 if (this.type!=='nav') {
3458 Roo.log('nav type must be nav/tabs/pills')
3460 cfg.cn[0].cls += ' navbar-nav'
3466 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467 cfg.cn[0].cls += ' nav-' + this.arrangement;
3471 if (this.align === 'right') {
3472 cfg.cn[0].cls += ' navbar-right';
3476 cfg.cls += ' navbar-inverse';
3503 * @class Roo.bootstrap.NavHeaderbar
3504 * @extends Roo.bootstrap.NavSimplebar
3505 * Bootstrap Sidebar class
3507 * @cfg {String} brand what is brand
3508 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509 * @cfg {String} brand_href href of the brand
3510 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3511 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3516 * Create a new Sidebar
3517 * @param {Object} config The config object
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3533 desktopCenter : false,
3536 getAutoCreate : function(){
3539 tag: this.nav || 'nav',
3546 if (this.desktopCenter) {
3547 cn.push({cls : 'container', cn : []});
3554 cls: 'navbar-header',
3559 cls: 'navbar-toggle',
3560 'data-toggle': 'collapse',
3565 html: 'Toggle navigation'
3587 cls: 'collapse navbar-collapse',
3591 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3593 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594 cfg.cls += ' navbar-' + this.position;
3596 // tag can override this..
3598 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3601 if (this.brand !== '') {
3604 href: this.brand_href ? this.brand_href : '#',
3605 cls: 'navbar-brand',
3613 cfg.cls += ' main-nav';
3621 getHeaderChildContainer : function()
3623 if (this.el.select('.navbar-header').getCount()) {
3624 return this.el.select('.navbar-header',true).first();
3627 return this.getChildContainer();
3631 initEvents : function()
3633 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3635 if (this.autohide) {
3640 Roo.get(document).on('scroll',function(e) {
3641 var ns = Roo.get(document).getScroll().top;
3642 var os = prevScroll;
3646 ft.removeClass('slideDown');
3647 ft.addClass('slideUp');
3650 ft.removeClass('slideUp');
3651 ft.addClass('slideDown');
3672 * @class Roo.bootstrap.NavSidebar
3673 * @extends Roo.bootstrap.Navbar
3674 * Bootstrap Sidebar class
3677 * Create a new Sidebar
3678 * @param {Object} config The config object
3682 Roo.bootstrap.NavSidebar = function(config){
3683 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3688 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3690 getAutoCreate : function(){
3695 cls: 'sidebar sidebar-nav'
3717 * @class Roo.bootstrap.NavGroup
3718 * @extends Roo.bootstrap.Component
3719 * Bootstrap NavGroup class
3720 * @cfg {String} align (left|right)
3721 * @cfg {Boolean} inverse
3722 * @cfg {String} type (nav|pills|tab) default nav
3723 * @cfg {String} navId - reference Id for navbar.
3727 * Create a new nav group
3728 * @param {Object} config The config object
3731 Roo.bootstrap.NavGroup = function(config){
3732 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3735 Roo.bootstrap.NavGroup.register(this);
3739 * Fires when the active item changes
3740 * @param {Roo.bootstrap.NavGroup} this
3741 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3760 getAutoCreate : function()
3762 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3769 if (['tabs','pills'].indexOf(this.type)!==-1) {
3770 cfg.cls += ' nav-' + this.type
3772 if (this.type!=='nav') {
3773 Roo.log('nav type must be nav/tabs/pills')
3775 cfg.cls += ' navbar-nav'
3778 if (this.parent().sidebar) {
3781 cls: 'dashboard-menu sidebar-menu'
3787 if (this.form === true) {
3793 if (this.align === 'right') {
3794 cfg.cls += ' navbar-right';
3796 cfg.cls += ' navbar-left';
3800 if (this.align === 'right') {
3801 cfg.cls += ' navbar-right';
3805 cfg.cls += ' navbar-inverse';
3813 * sets the active Navigation item
3814 * @param {Roo.bootstrap.NavItem} the new current navitem
3816 setActiveItem : function(item)
3819 Roo.each(this.navItems, function(v){
3824 v.setActive(false, true);
3831 item.setActive(true, true);
3832 this.fireEvent('changed', this, item, prev);
3837 * gets the active Navigation item
3838 * @return {Roo.bootstrap.NavItem} the current navitem
3840 getActive : function()
3844 Roo.each(this.navItems, function(v){
3855 indexOfNav : function()
3859 Roo.each(this.navItems, function(v,i){
3870 * adds a Navigation item
3871 * @param {Roo.bootstrap.NavItem} the navitem to add
3873 addItem : function(cfg)
3875 var cn = new Roo.bootstrap.NavItem(cfg);
3877 cn.parentId = this.id;
3878 cn.onRender(this.el, null);
3882 * register a Navigation item
3883 * @param {Roo.bootstrap.NavItem} the navitem to add
3885 register : function(item)
3887 this.navItems.push( item);
3888 item.navId = this.navId;
3893 * clear all the Navigation item
3896 clearAll : function()
3899 this.el.dom.innerHTML = '';
3902 getNavItem: function(tabId)
3905 Roo.each(this.navItems, function(e) {
3906 if (e.tabId == tabId) {
3916 setActiveNext : function()
3918 var i = this.indexOfNav(this.getActive());
3919 if (i > this.navItems.length) {
3922 this.setActiveItem(this.navItems[i+1]);
3924 setActivePrev : function()
3926 var i = this.indexOfNav(this.getActive());
3930 this.setActiveItem(this.navItems[i-1]);
3932 clearWasActive : function(except) {
3933 Roo.each(this.navItems, function(e) {
3934 if (e.tabId != except.tabId && e.was_active) {
3935 e.was_active = false;
3942 getWasActive : function ()
3945 Roo.each(this.navItems, function(e) {
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3964 * register a Navigation Group
3965 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3967 register : function(navgrp)
3969 this.groups[navgrp.navId] = navgrp;
3973 * fetch a Navigation Group based on the navigation ID
3974 * @param {string} the navgroup to add
3975 * @returns {Roo.bootstrap.NavGroup} the navgroup
3977 get: function(navId) {
3978 if (typeof(this.groups[navId]) == 'undefined') {
3980 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3982 return this.groups[navId] ;
3997 * @class Roo.bootstrap.NavItem
3998 * @extends Roo.bootstrap.Component
3999 * Bootstrap Navbar.NavItem class
4000 * @cfg {String} href link to
4001 * @cfg {String} html content of button
4002 * @cfg {String} badge text inside badge
4003 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004 * @cfg {String} glyphicon name of glyphicon
4005 * @cfg {String} icon name of font awesome icon
4006 * @cfg {Boolean} active Is item active
4007 * @cfg {Boolean} disabled Is item disabled
4009 * @cfg {Boolean} preventDefault (true | false) default false
4010 * @cfg {String} tabId the tab that this item activates.
4011 * @cfg {String} tagtype (a|span) render as a href or span?
4012 * @cfg {Boolean} animateRef (true|false) link to element default false
4015 * Create a new Navbar Item
4016 * @param {Object} config The config object
4018 Roo.bootstrap.NavItem = function(config){
4019 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4024 * The raw click event for the entire grid.
4025 * @param {Roo.EventObject} e
4030 * Fires when the active item active state changes
4031 * @param {Roo.bootstrap.NavItem} this
4032 * @param {boolean} state the new state
4038 * Fires when scroll to element
4039 * @param {Roo.bootstrap.NavItem} this
4040 * @param {Object} options
4041 * @param {Roo.EventObject} e
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4057 preventDefault : false,
4064 getAutoCreate : function(){
4072 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4074 if (this.disabled) {
4075 cfg.cls += ' disabled';
4078 if (this.href || this.html || this.glyphicon || this.icon) {
4082 href : this.href || "#",
4083 html: this.html || ''
4088 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4091 if(this.glyphicon) {
4092 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4097 cfg.cn[0].html += " <span class='caret'></span>";
4101 if (this.badge !== '') {
4103 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4111 initEvents: function()
4113 if (typeof (this.menu) != 'undefined') {
4114 this.menu.parentType = this.xtype;
4115 this.menu.triggerEl = this.el;
4116 this.menu = this.addxtype(Roo.apply({}, this.menu));
4119 this.el.select('a',true).on('click', this.onClick, this);
4121 if(this.tagtype == 'span'){
4122 this.el.select('span',true).on('click', this.onClick, this);
4125 // at this point parent should be available..
4126 this.parent().register(this);
4129 onClick : function(e)
4132 this.preventDefault ||
4139 if (this.disabled) {
4143 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144 if (tg && tg.transition) {
4145 Roo.log("waiting for the transitionend");
4151 //Roo.log("fire event clicked");
4152 if(this.fireEvent('click', this, e) === false){
4156 if(this.tagtype == 'span'){
4160 //Roo.log(this.href);
4161 var ael = this.el.select('a',true).first();
4164 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167 return; // ignore... - it's a 'hash' to another page.
4171 this.scrollToElement(e);
4175 var p = this.parent();
4177 if (['tabs','pills'].indexOf(p.type)!==-1) {
4178 if (typeof(p.setActiveItem) !== 'undefined') {
4179 p.setActiveItem(this);
4183 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185 // remove the collapsed menu expand...
4186 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4190 isActive: function () {
4193 setActive : function(state, fire, is_was_active)
4195 if (this.active && !state && this.navId) {
4196 this.was_active = true;
4197 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4199 nv.clearWasActive(this);
4203 this.active = state;
4206 this.el.removeClass('active');
4207 } else if (!this.el.hasClass('active')) {
4208 this.el.addClass('active');
4211 this.fireEvent('changed', this, state);
4214 // show a panel if it's registered and related..
4216 if (!this.navId || !this.tabId || !state || is_was_active) {
4220 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4224 var pan = tg.getPanelByName(this.tabId);
4228 // if we can not flip to new panel - go back to old nav highlight..
4229 if (false == tg.showPanel(pan)) {
4230 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232 var onav = nv.getWasActive();
4234 onav.setActive(true, false, true);
4243 // this should not be here...
4244 setDisabled : function(state)
4246 this.disabled = state;
4248 this.el.removeClass('disabled');
4249 } else if (!this.el.hasClass('disabled')) {
4250 this.el.addClass('disabled');
4256 * Fetch the element to display the tooltip on.
4257 * @return {Roo.Element} defaults to this.el
4259 tooltipEl : function()
4261 return this.el.select('' + this.tagtype + '', true).first();
4264 scrollToElement : function(e)
4266 var c = document.body;
4269 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4271 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272 c = document.documentElement;
4275 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4281 var o = target.calcOffsetsTo(c);
4288 this.fireEvent('scrollto', this, options, e);
4290 Roo.get(c).scrollTo('top', options.value, true);
4303 * <span> icon </span>
4304 * <span> text </span>
4305 * <span>badge </span>
4309 * @class Roo.bootstrap.NavSidebarItem
4310 * @extends Roo.bootstrap.NavItem
4311 * Bootstrap Navbar.NavSidebarItem class
4313 * Create a new Navbar Button
4314 * @param {Object} config The config object
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4322 * The raw click event for the entire grid.
4323 * @param {Roo.EventObject} e
4328 * Fires when the active item active state changes
4329 * @param {Roo.bootstrap.NavSidebarItem} this
4330 * @param {boolean} state the new state
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4341 getAutoCreate : function(){
4346 href : this.href || '#',
4358 html : this.html || ''
4363 cfg.cls += ' active';
4367 if (this.glyphicon || this.icon) {
4368 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4369 a.cn.push({ tag : 'i', cls : c }) ;
4374 if (this.badge !== '') {
4375 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
4379 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380 a.cls += 'dropdown-toggle treeview' ;
4404 * @class Roo.bootstrap.Row
4405 * @extends Roo.bootstrap.Component
4406 * Bootstrap Row class (contains columns...)
4410 * @param {Object} config The config object
4413 Roo.bootstrap.Row = function(config){
4414 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4419 getAutoCreate : function(){
4438 * @class Roo.bootstrap.Element
4439 * @extends Roo.bootstrap.Component
4440 * Bootstrap Element class
4441 * @cfg {String} html contents of the element
4442 * @cfg {String} tag tag of the element
4443 * @cfg {String} cls class of the element
4444 * @cfg {Boolean} preventDefault (true|false) default false
4445 * @cfg {Boolean} clickable (true|false) default false
4448 * Create a new Element
4449 * @param {Object} config The config object
4452 Roo.bootstrap.Element = function(config){
4453 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4459 * When a element is chick
4460 * @param {Roo.bootstrap.Element} this
4461 * @param {Roo.EventObject} e
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4472 preventDefault: false,
4475 getAutoCreate : function(){
4486 initEvents: function()
4488 Roo.bootstrap.Element.superclass.initEvents.call(this);
4491 this.el.on('click', this.onClick, this);
4496 onClick : function(e)
4498 if(this.preventDefault){
4502 this.fireEvent('click', this, e);
4505 getValue : function()
4507 return this.el.dom.innerHTML;
4510 setValue : function(value)
4512 this.el.dom.innerHTML = value;
4527 * @class Roo.bootstrap.Pagination
4528 * @extends Roo.bootstrap.Component
4529 * Bootstrap Pagination class
4530 * @cfg {String} size xs | sm | md | lg
4531 * @cfg {Boolean} inverse false | true
4534 * Create a new Pagination
4535 * @param {Object} config The config object
4538 Roo.bootstrap.Pagination = function(config){
4539 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4548 getAutoCreate : function(){
4554 cfg.cls += ' inverse';
4560 cfg.cls += " " + this.cls;
4578 * @class Roo.bootstrap.PaginationItem
4579 * @extends Roo.bootstrap.Component
4580 * Bootstrap PaginationItem class
4581 * @cfg {String} html text
4582 * @cfg {String} href the link
4583 * @cfg {Boolean} preventDefault (true | false) default true
4584 * @cfg {Boolean} active (true | false) default false
4585 * @cfg {Boolean} disabled default false
4589 * Create a new PaginationItem
4590 * @param {Object} config The config object
4594 Roo.bootstrap.PaginationItem = function(config){
4595 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4600 * The raw click event for the entire grid.
4601 * @param {Roo.EventObject} e
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4611 preventDefault: true,
4616 getAutoCreate : function(){
4622 href : this.href ? this.href : '#',
4623 html : this.html ? this.html : ''
4633 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4637 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4643 initEvents: function() {
4645 this.el.on('click', this.onClick, this);
4648 onClick : function(e)
4650 Roo.log('PaginationItem on click ');
4651 if(this.preventDefault){
4659 this.fireEvent('click', this, e);
4675 * @class Roo.bootstrap.Slider
4676 * @extends Roo.bootstrap.Component
4677 * Bootstrap Slider class
4680 * Create a new Slider
4681 * @param {Object} config The config object
4684 Roo.bootstrap.Slider = function(config){
4685 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4690 getAutoCreate : function(){
4694 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4698 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4710 * Ext JS Library 1.1.1
4711 * Copyright(c) 2006-2007, Ext JS, LLC.
4713 * Originally Released Under LGPL - original licence link has changed is not relivant.
4716 * <script type="text/javascript">
4721 * @class Roo.grid.ColumnModel
4722 * @extends Roo.util.Observable
4723 * This is the default implementation of a ColumnModel used by the Grid. It defines
4724 * the columns in the grid.
4727 var colModel = new Roo.grid.ColumnModel([
4728 {header: "Ticker", width: 60, sortable: true, locked: true},
4729 {header: "Company Name", width: 150, sortable: true},
4730 {header: "Market Cap.", width: 100, sortable: true},
4731 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732 {header: "Employees", width: 100, sortable: true, resizable: false}
4737 * The config options listed for this class are options which may appear in each
4738 * individual column definition.
4739 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4741 * @param {Object} config An Array of column config objects. See this class's
4742 * config objects for details.
4744 Roo.grid.ColumnModel = function(config){
4746 * The config passed into the constructor
4748 this.config = config;
4751 // if no id, create one
4752 // if the column does not have a dataIndex mapping,
4753 // map it to the order it is in the config
4754 for(var i = 0, len = config.length; i < len; i++){
4756 if(typeof c.dataIndex == "undefined"){
4759 if(typeof c.renderer == "string"){
4760 c.renderer = Roo.util.Format[c.renderer];
4762 if(typeof c.id == "undefined"){
4765 if(c.editor && c.editor.xtype){
4766 c.editor = Roo.factory(c.editor, Roo.grid);
4768 if(c.editor && c.editor.isFormField){
4769 c.editor = new Roo.grid.GridEditor(c.editor);
4771 this.lookup[c.id] = c;
4775 * The width of columns which have no width specified (defaults to 100)
4778 this.defaultWidth = 100;
4781 * Default sortable of columns which have no sortable specified (defaults to false)
4784 this.defaultSortable = false;
4788 * @event widthchange
4789 * Fires when the width of a column changes.
4790 * @param {ColumnModel} this
4791 * @param {Number} columnIndex The column index
4792 * @param {Number} newWidth The new width
4794 "widthchange": true,
4796 * @event headerchange
4797 * Fires when the text of a header changes.
4798 * @param {ColumnModel} this
4799 * @param {Number} columnIndex The column index
4800 * @param {Number} newText The new header text
4802 "headerchange": true,
4804 * @event hiddenchange
4805 * Fires when a column is hidden or "unhidden".
4806 * @param {ColumnModel} this
4807 * @param {Number} columnIndex The column index
4808 * @param {Boolean} hidden true if hidden, false otherwise
4810 "hiddenchange": true,
4812 * @event columnmoved
4813 * Fires when a column is moved.
4814 * @param {ColumnModel} this
4815 * @param {Number} oldIndex
4816 * @param {Number} newIndex
4818 "columnmoved" : true,
4820 * @event columlockchange
4821 * Fires when a column's locked state is changed
4822 * @param {ColumnModel} this
4823 * @param {Number} colIndex
4824 * @param {Boolean} locked true if locked
4826 "columnlockchange" : true
4828 Roo.grid.ColumnModel.superclass.constructor.call(this);
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4832 * @cfg {String} header The header text to display in the Grid view.
4835 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837 * specified, the column's index is used as an index into the Record's data Array.
4840 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4844 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845 * Defaults to the value of the {@link #defaultSortable} property.
4846 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4849 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4852 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4855 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4858 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4861 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4867 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4870 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4873 * @cfg {String} cursor (Optional)
4876 * @cfg {String} tooltip (Optional)
4879 * Returns the id of the column at the specified index.
4880 * @param {Number} index The column index
4881 * @return {String} the id
4883 getColumnId : function(index){
4884 return this.config[index].id;
4888 * Returns the column for a specified id.
4889 * @param {String} id The column id
4890 * @return {Object} the column
4892 getColumnById : function(id){
4893 return this.lookup[id];
4898 * Returns the column for a specified dataIndex.
4899 * @param {String} dataIndex The column dataIndex
4900 * @return {Object|Boolean} the column or false if not found
4902 getColumnByDataIndex: function(dataIndex){
4903 var index = this.findColumnIndex(dataIndex);
4904 return index > -1 ? this.config[index] : false;
4908 * Returns the index for a specified column id.
4909 * @param {String} id The column id
4910 * @return {Number} the index, or -1 if not found
4912 getIndexById : function(id){
4913 for(var i = 0, len = this.config.length; i < len; i++){
4914 if(this.config[i].id == id){
4922 * Returns the index for a specified column dataIndex.
4923 * @param {String} dataIndex The column dataIndex
4924 * @return {Number} the index, or -1 if not found
4927 findColumnIndex : function(dataIndex){
4928 for(var i = 0, len = this.config.length; i < len; i++){
4929 if(this.config[i].dataIndex == dataIndex){
4937 moveColumn : function(oldIndex, newIndex){
4938 var c = this.config[oldIndex];
4939 this.config.splice(oldIndex, 1);
4940 this.config.splice(newIndex, 0, c);
4941 this.dataMap = null;
4942 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4945 isLocked : function(colIndex){
4946 return this.config[colIndex].locked === true;
4949 setLocked : function(colIndex, value, suppressEvent){
4950 if(this.isLocked(colIndex) == value){
4953 this.config[colIndex].locked = value;
4955 this.fireEvent("columnlockchange", this, colIndex, value);
4959 getTotalLockedWidth : function(){
4961 for(var i = 0; i < this.config.length; i++){
4962 if(this.isLocked(i) && !this.isHidden(i)){
4963 this.totalWidth += this.getColumnWidth(i);
4969 getLockedCount : function(){
4970 for(var i = 0, len = this.config.length; i < len; i++){
4971 if(!this.isLocked(i)){
4978 * Returns the number of columns.
4981 getColumnCount : function(visibleOnly){
4982 if(visibleOnly === true){
4984 for(var i = 0, len = this.config.length; i < len; i++){
4985 if(!this.isHidden(i)){
4991 return this.config.length;
4995 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996 * @param {Function} fn
4997 * @param {Object} scope (optional)
4998 * @return {Array} result
5000 getColumnsBy : function(fn, scope){
5002 for(var i = 0, len = this.config.length; i < len; i++){
5003 var c = this.config[i];
5004 if(fn.call(scope||this, c, i) === true){
5012 * Returns true if the specified column is sortable.
5013 * @param {Number} col The column index
5016 isSortable : function(col){
5017 if(typeof this.config[col].sortable == "undefined"){
5018 return this.defaultSortable;
5020 return this.config[col].sortable;
5024 * Returns the rendering (formatting) function defined for the column.
5025 * @param {Number} col The column index.
5026 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5028 getRenderer : function(col){
5029 if(!this.config[col].renderer){
5030 return Roo.grid.ColumnModel.defaultRenderer;
5032 return this.config[col].renderer;
5036 * Sets the rendering (formatting) function for a column.
5037 * @param {Number} col The column index
5038 * @param {Function} fn The function to use to process the cell's raw data
5039 * to return HTML markup for the grid view. The render function is called with
5040 * the following parameters:<ul>
5041 * <li>Data value.</li>
5042 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043 * <li>css A CSS style string to apply to the table cell.</li>
5044 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046 * <li>Row index</li>
5047 * <li>Column index</li>
5048 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5050 setRenderer : function(col, fn){
5051 this.config[col].renderer = fn;
5055 * Returns the width for the specified column.
5056 * @param {Number} col The column index
5059 getColumnWidth : function(col){
5060 return this.config[col].width * 1 || this.defaultWidth;
5064 * Sets the width for a column.
5065 * @param {Number} col The column index
5066 * @param {Number} width The new width
5068 setColumnWidth : function(col, width, suppressEvent){
5069 this.config[col].width = width;
5070 this.totalWidth = null;
5072 this.fireEvent("widthchange", this, col, width);
5077 * Returns the total width of all columns.
5078 * @param {Boolean} includeHidden True to include hidden column widths
5081 getTotalWidth : function(includeHidden){
5082 if(!this.totalWidth){
5083 this.totalWidth = 0;
5084 for(var i = 0, len = this.config.length; i < len; i++){
5085 if(includeHidden || !this.isHidden(i)){
5086 this.totalWidth += this.getColumnWidth(i);
5090 return this.totalWidth;
5094 * Returns the header for the specified column.
5095 * @param {Number} col The column index
5098 getColumnHeader : function(col){
5099 return this.config[col].header;
5103 * Sets the header for a column.
5104 * @param {Number} col The column index
5105 * @param {String} header The new header
5107 setColumnHeader : function(col, header){
5108 this.config[col].header = header;
5109 this.fireEvent("headerchange", this, col, header);
5113 * Returns the tooltip for the specified column.
5114 * @param {Number} col The column index
5117 getColumnTooltip : function(col){
5118 return this.config[col].tooltip;
5121 * Sets the tooltip for a column.
5122 * @param {Number} col The column index
5123 * @param {String} tooltip The new tooltip
5125 setColumnTooltip : function(col, tooltip){
5126 this.config[col].tooltip = tooltip;
5130 * Returns the dataIndex for the specified column.
5131 * @param {Number} col The column index
5134 getDataIndex : function(col){
5135 return this.config[col].dataIndex;
5139 * Sets the dataIndex for a column.
5140 * @param {Number} col The column index
5141 * @param {Number} dataIndex The new dataIndex
5143 setDataIndex : function(col, dataIndex){
5144 this.config[col].dataIndex = dataIndex;
5150 * Returns true if the cell is editable.
5151 * @param {Number} colIndex The column index
5152 * @param {Number} rowIndex The row index
5155 isCellEditable : function(colIndex, rowIndex){
5156 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5160 * Returns the editor defined for the cell/column.
5161 * return false or null to disable editing.
5162 * @param {Number} colIndex The column index
5163 * @param {Number} rowIndex The row index
5166 getCellEditor : function(colIndex, rowIndex){
5167 return this.config[colIndex].editor;
5171 * Sets if a column is editable.
5172 * @param {Number} col The column index
5173 * @param {Boolean} editable True if the column is editable
5175 setEditable : function(col, editable){
5176 this.config[col].editable = editable;
5181 * Returns true if the column is hidden.
5182 * @param {Number} colIndex The column index
5185 isHidden : function(colIndex){
5186 return this.config[colIndex].hidden;
5191 * Returns true if the column width cannot be changed
5193 isFixed : function(colIndex){
5194 return this.config[colIndex].fixed;
5198 * Returns true if the column can be resized
5201 isResizable : function(colIndex){
5202 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5205 * Sets if a column is hidden.
5206 * @param {Number} colIndex The column index
5207 * @param {Boolean} hidden True if the column is hidden
5209 setHidden : function(colIndex, hidden){
5210 this.config[colIndex].hidden = hidden;
5211 this.totalWidth = null;
5212 this.fireEvent("hiddenchange", this, colIndex, hidden);
5216 * Sets the editor for a column.
5217 * @param {Number} col The column index
5218 * @param {Object} editor The editor object
5220 setEditor : function(col, editor){
5221 this.config[col].editor = editor;
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226 if(typeof value == "string" && value.length < 1){
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5236 * Ext JS Library 1.1.1
5237 * Copyright(c) 2006-2007, Ext JS, LLC.
5239 * Originally Released Under LGPL - original licence link has changed is not relivant.
5242 * <script type="text/javascript">
5246 * @class Roo.LoadMask
5247 * A simple utility class for generically masking elements while loading data. If the element being masked has
5248 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5250 * element's UpdateManager load indicator and will be destroyed after the initial load.
5252 * Create a new LoadMask
5253 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254 * @param {Object} config The config object
5256 Roo.LoadMask = function(el, config){
5257 this.el = Roo.get(el);
5258 Roo.apply(this, config);
5260 this.store.on('beforeload', this.onBeforeLoad, this);
5261 this.store.on('load', this.onLoad, this);
5262 this.store.on('loadexception', this.onLoadException, this);
5263 this.removeMask = false;
5265 var um = this.el.getUpdateManager();
5266 um.showLoadIndicator = false; // disable the default indicator
5267 um.on('beforeupdate', this.onBeforeLoad, this);
5268 um.on('update', this.onLoad, this);
5269 um.on('failure', this.onLoad, this);
5270 this.removeMask = true;
5274 Roo.LoadMask.prototype = {
5276 * @cfg {Boolean} removeMask
5277 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5282 * The text to display in a centered loading message box (defaults to 'Loading...')
5286 * @cfg {String} msgCls
5287 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5289 msgCls : 'x-mask-loading',
5292 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5298 * Disables the mask to prevent it from being displayed
5300 disable : function(){
5301 this.disabled = true;
5305 * Enables the mask so that it can be displayed
5307 enable : function(){
5308 this.disabled = false;
5311 onLoadException : function()
5315 if (typeof(arguments[3]) != 'undefined') {
5316 Roo.MessageBox.alert("Error loading",arguments[3]);
5320 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5330 this.el.unmask(this.removeMask);
5335 this.el.unmask(this.removeMask);
5339 onBeforeLoad : function(){
5341 this.el.mask(this.msg, this.msgCls);
5346 destroy : function(){
5348 this.store.un('beforeload', this.onBeforeLoad, this);
5349 this.store.un('load', this.onLoad, this);
5350 this.store.un('loadexception', this.onLoadException, this);
5352 var um = this.el.getUpdateManager();
5353 um.un('beforeupdate', this.onBeforeLoad, this);
5354 um.un('update', this.onLoad, this);
5355 um.un('failure', this.onLoad, this);
5366 * @class Roo.bootstrap.Table
5367 * @extends Roo.bootstrap.Component
5368 * Bootstrap Table class
5369 * @cfg {String} cls table class
5370 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371 * @cfg {String} bgcolor Specifies the background color for a table
5372 * @cfg {Number} border Specifies whether the table cells should have borders or not
5373 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374 * @cfg {Number} cellspacing Specifies the space between cells
5375 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377 * @cfg {String} sortable Specifies that the table should be sortable
5378 * @cfg {String} summary Specifies a summary of the content of a table
5379 * @cfg {Number} width Specifies the width of a table
5380 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5382 * @cfg {boolean} striped Should the rows be alternative striped
5383 * @cfg {boolean} bordered Add borders to the table
5384 * @cfg {boolean} hover Add hover highlighting
5385 * @cfg {boolean} condensed Format condensed
5386 * @cfg {boolean} responsive Format condensed
5387 * @cfg {Boolean} loadMask (true|false) default false
5388 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5389 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5390 * @cfg {Boolean} rowSelection (true|false) default false
5391 * @cfg {Boolean} cellSelection (true|false) default false
5392 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5396 * Create a new Table
5397 * @param {Object} config The config object
5400 Roo.bootstrap.Table = function(config){
5401 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5404 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5405 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5406 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5407 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5411 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5412 this.sm = this.selModel;
5413 this.sm.xmodule = this.xmodule || false;
5415 if (this.cm && typeof(this.cm.config) == 'undefined') {
5416 this.colModel = new Roo.grid.ColumnModel(this.cm);
5417 this.cm = this.colModel;
5418 this.cm.xmodule = this.xmodule || false;
5421 this.store= Roo.factory(this.store, Roo.data);
5422 this.ds = this.store;
5423 this.ds.xmodule = this.xmodule || false;
5426 if (this.footer && this.store) {
5427 this.footer.dataSource = this.ds;
5428 this.footer = Roo.factory(this.footer);
5435 * Fires when a cell is clicked
5436 * @param {Roo.bootstrap.Table} this
5437 * @param {Roo.Element} el
5438 * @param {Number} rowIndex
5439 * @param {Number} columnIndex
5440 * @param {Roo.EventObject} e
5444 * @event celldblclick
5445 * Fires when a cell is double clicked
5446 * @param {Roo.bootstrap.Table} this
5447 * @param {Roo.Element} el
5448 * @param {Number} rowIndex
5449 * @param {Number} columnIndex
5450 * @param {Roo.EventObject} e
5452 "celldblclick" : true,
5455 * Fires when a row is clicked
5456 * @param {Roo.bootstrap.Table} this
5457 * @param {Roo.Element} el
5458 * @param {Number} rowIndex
5459 * @param {Roo.EventObject} e
5463 * @event rowdblclick
5464 * Fires when a row is double clicked
5465 * @param {Roo.bootstrap.Table} this
5466 * @param {Roo.Element} el
5467 * @param {Number} rowIndex
5468 * @param {Roo.EventObject} e
5470 "rowdblclick" : true,
5473 * Fires when a mouseover occur
5474 * @param {Roo.bootstrap.Table} this
5475 * @param {Roo.Element} el
5476 * @param {Number} rowIndex
5477 * @param {Number} columnIndex
5478 * @param {Roo.EventObject} e
5483 * Fires when a mouseout occur
5484 * @param {Roo.bootstrap.Table} this
5485 * @param {Roo.Element} el
5486 * @param {Number} rowIndex
5487 * @param {Number} columnIndex
5488 * @param {Roo.EventObject} e
5493 * Fires when a row is rendered, so you can change add a style to it.
5494 * @param {Roo.bootstrap.Table} this
5495 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5499 * @event rowsrendered
5500 * Fires when all the rows have been rendered
5501 * @param {Roo.bootstrap.Table} this
5503 'rowsrendered' : true
5508 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5533 rowSelection : false,
5534 cellSelection : false,
5537 // Roo.Element - the tbody
5540 getAutoCreate : function(){
5541 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5550 cfg.cls += ' table-striped';
5554 cfg.cls += ' table-hover';
5556 if (this.bordered) {
5557 cfg.cls += ' table-bordered';
5559 if (this.condensed) {
5560 cfg.cls += ' table-condensed';
5562 if (this.responsive) {
5563 cfg.cls += ' table-responsive';
5567 cfg.cls+= ' ' +this.cls;
5570 // this lot should be simplifed...
5573 cfg.align=this.align;
5576 cfg.bgcolor=this.bgcolor;
5579 cfg.border=this.border;
5581 if (this.cellpadding) {
5582 cfg.cellpadding=this.cellpadding;
5584 if (this.cellspacing) {
5585 cfg.cellspacing=this.cellspacing;
5588 cfg.frame=this.frame;
5591 cfg.rules=this.rules;
5593 if (this.sortable) {
5594 cfg.sortable=this.sortable;
5597 cfg.summary=this.summary;
5600 cfg.width=this.width;
5603 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5606 if(this.store || this.cm){
5607 if(this.headerShow){
5608 cfg.cn.push(this.renderHeader());
5611 cfg.cn.push(this.renderBody());
5613 if(this.footerShow){
5614 cfg.cn.push(this.renderFooter());
5617 cfg.cls+= ' TableGrid';
5620 return { cn : [ cfg ] };
5623 initEvents : function()
5625 if(!this.store || !this.cm){
5629 //Roo.log('initEvents with ds!!!!');
5631 this.mainBody = this.el.select('tbody', true).first();
5636 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5637 e.on('click', _this.sort, _this);
5640 this.el.on("click", this.onClick, this);
5641 this.el.on("dblclick", this.onDblClick, this);
5643 // why is this done????? = it breaks dialogs??
5644 //this.parent().el.setStyle('position', 'relative');
5648 this.footer.parentId = this.id;
5649 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5652 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5654 this.store.on('load', this.onLoad, this);
5655 this.store.on('beforeload', this.onBeforeLoad, this);
5656 this.store.on('update', this.onUpdate, this);
5657 this.store.on('add', this.onAdd, this);
5661 onMouseover : function(e, el)
5663 var cell = Roo.get(el);
5669 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5670 cell = cell.findParent('td', false, true);
5673 var row = cell.findParent('tr', false, true);
5674 var cellIndex = cell.dom.cellIndex;
5675 var rowIndex = row.dom.rowIndex - 1; // start from 0
5677 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5681 onMouseout : function(e, el)
5683 var cell = Roo.get(el);
5689 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5690 cell = cell.findParent('td', false, true);
5693 var row = cell.findParent('tr', false, true);
5694 var cellIndex = cell.dom.cellIndex;
5695 var rowIndex = row.dom.rowIndex - 1; // start from 0
5697 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5701 onClick : function(e, el)
5703 var cell = Roo.get(el);
5705 if(!cell || (!this.cellSelection && !this.rowSelection)){
5709 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5710 cell = cell.findParent('td', false, true);
5713 if(!cell || typeof(cell) == 'undefined'){
5717 var row = cell.findParent('tr', false, true);
5719 if(!row || typeof(row) == 'undefined'){
5723 var cellIndex = cell.dom.cellIndex;
5724 var rowIndex = this.getRowIndex(row);
5726 // why??? - should these not be based on SelectionModel?
5727 if(this.cellSelection){
5728 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5731 if(this.rowSelection){
5732 this.fireEvent('rowclick', this, row, rowIndex, e);
5738 onDblClick : function(e,el)
5740 var cell = Roo.get(el);
5742 if(!cell || (!this.CellSelection && !this.RowSelection)){
5746 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5747 cell = cell.findParent('td', false, true);
5750 if(!cell || typeof(cell) == 'undefined'){
5754 var row = cell.findParent('tr', false, true);
5756 if(!row || typeof(row) == 'undefined'){
5760 var cellIndex = cell.dom.cellIndex;
5761 var rowIndex = this.getRowIndex(row);
5763 if(this.CellSelection){
5764 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5767 if(this.RowSelection){
5768 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5772 sort : function(e,el)
5774 var col = Roo.get(el);
5776 if(!col.hasClass('sortable')){
5780 var sort = col.attr('sort');
5783 if(col.hasClass('glyphicon-arrow-up')){
5787 this.store.sortInfo = {field : sort, direction : dir};
5790 Roo.log("calling footer first");
5791 this.footer.onClick('first');
5794 this.store.load({ params : { start : 0 } });
5798 renderHeader : function()
5807 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5809 var config = cm.config[i];
5814 html: cm.getColumnHeader(i)
5819 if(typeof(config.lgHeader) != 'undefined'){
5820 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5823 if(typeof(config.mdHeader) != 'undefined'){
5824 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5827 if(typeof(config.smHeader) != 'undefined'){
5828 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5831 if(typeof(config.xsHeader) != 'undefined'){
5832 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5839 if(typeof(config.tooltip) != 'undefined'){
5840 c.tooltip = config.tooltip;
5843 if(typeof(config.colspan) != 'undefined'){
5844 c.colspan = config.colspan;
5847 if(typeof(config.hidden) != 'undefined' && config.hidden){
5848 c.style += ' display:none;';
5851 if(typeof(config.dataIndex) != 'undefined'){
5852 c.sort = config.dataIndex;
5855 if(typeof(config.sortable) != 'undefined' && config.sortable){
5859 if(typeof(config.align) != 'undefined' && config.align.length){
5860 c.style += ' text-align:' + config.align + ';';
5863 if(typeof(config.width) != 'undefined'){
5864 c.style += ' width:' + config.width + 'px;';
5867 if(typeof(config.cls) != 'undefined'){
5868 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5877 renderBody : function()
5887 colspan : this.cm.getColumnCount()
5897 renderFooter : function()
5907 colspan : this.cm.getColumnCount()
5921 Roo.log('ds onload');
5926 var ds = this.store;
5928 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5929 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5931 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5932 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5935 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5936 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5940 var tbody = this.mainBody;
5942 if(ds.getCount() > 0){
5943 ds.data.each(function(d,rowIndex){
5944 var row = this.renderRow(cm, ds, rowIndex);
5946 tbody.createChild(row);
5950 if(row.cellObjects.length){
5951 Roo.each(row.cellObjects, function(r){
5952 _this.renderCellObject(r);
5959 Roo.each(this.el.select('tbody td', true).elements, function(e){
5960 e.on('mouseover', _this.onMouseover, _this);
5963 Roo.each(this.el.select('tbody td', true).elements, function(e){
5964 e.on('mouseout', _this.onMouseout, _this);
5966 this.fireEvent('rowsrendered', this);
5967 //if(this.loadMask){
5968 // this.maskEl.hide();
5973 onUpdate : function(ds,record)
5975 this.refreshRow(record);
5978 onRemove : function(ds, record, index, isUpdate){
5979 if(isUpdate !== true){
5980 this.fireEvent("beforerowremoved", this, index, record);
5982 var bt = this.mainBody.dom;
5984 var rows = this.el.select('tbody > tr', true).elements;
5986 if(typeof(rows[index]) != 'undefined'){
5987 bt.removeChild(rows[index].dom);
5990 // if(bt.rows[index]){
5991 // bt.removeChild(bt.rows[index]);
5994 if(isUpdate !== true){
5995 //this.stripeRows(index);
5996 //this.syncRowHeights(index, index);
5998 this.fireEvent("rowremoved", this, index, record);
6002 onAdd : function(ds, records, rowIndex)
6004 //Roo.log('on Add called');
6005 // - note this does not handle multiple adding very well..
6006 var bt = this.mainBody.dom;
6007 for (var i =0 ; i < records.length;i++) {
6008 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6009 //Roo.log(records[i]);
6010 //Roo.log(this.store.getAt(rowIndex+i));
6011 this.insertRow(this.store, rowIndex + i, false);
6018 refreshRow : function(record){
6019 var ds = this.store, index;
6020 if(typeof record == 'number'){
6022 record = ds.getAt(index);
6024 index = ds.indexOf(record);
6026 this.insertRow(ds, index, true);
6027 this.onRemove(ds, record, index+1, true);
6028 //this.syncRowHeights(index, index);
6030 this.fireEvent("rowupdated", this, index, record);
6033 insertRow : function(dm, rowIndex, isUpdate){
6036 this.fireEvent("beforerowsinserted", this, rowIndex);
6038 //var s = this.getScrollState();
6039 var row = this.renderRow(this.cm, this.store, rowIndex);
6040 // insert before rowIndex..
6041 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6045 if(row.cellObjects.length){
6046 Roo.each(row.cellObjects, function(r){
6047 _this.renderCellObject(r);
6052 this.fireEvent("rowsinserted", this, rowIndex);
6053 //this.syncRowHeights(firstRow, lastRow);
6054 //this.stripeRows(firstRow);
6061 getRowDom : function(rowIndex)
6063 var rows = this.el.select('tbody > tr', true).elements;
6065 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6068 // returns the object tree for a tr..
6071 renderRow : function(cm, ds, rowIndex)
6074 var d = ds.getAt(rowIndex);
6081 var cellObjects = [];
6083 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6084 var config = cm.config[i];
6086 var renderer = cm.getRenderer(i);
6090 if(typeof(renderer) !== 'undefined'){
6091 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6093 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6094 // and are rendered into the cells after the row is rendered - using the id for the element.
6096 if(typeof(value) === 'object'){
6106 rowIndex : rowIndex,
6111 this.fireEvent('rowclass', this, rowcfg);
6115 cls : rowcfg.rowClass,
6117 html: (typeof(value) === 'object') ? '' : value
6124 if(typeof(config.colspan) != 'undefined'){
6125 td.colspan = config.colspan;
6128 if(typeof(config.hidden) != 'undefined' && config.hidden){
6129 td.style += ' display:none;';
6132 if(typeof(config.align) != 'undefined' && config.align.length){
6133 td.style += ' text-align:' + config.align + ';';
6136 if(typeof(config.width) != 'undefined'){
6137 td.style += ' width:' + config.width + 'px;';
6140 if(typeof(config.cursor) != 'undefined'){
6141 td.style += ' cursor:' + config.cursor + ';';
6144 if(typeof(config.cls) != 'undefined'){
6145 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6152 row.cellObjects = cellObjects;
6160 onBeforeLoad : function()
6162 //Roo.log('ds onBeforeLoad');
6166 //if(this.loadMask){
6167 // this.maskEl.show();
6175 this.el.select('tbody', true).first().dom.innerHTML = '';
6178 * Show or hide a row.
6179 * @param {Number} rowIndex to show or hide
6180 * @param {Boolean} state hide
6182 setRowVisibility : function(rowIndex, state)
6184 var bt = this.mainBody.dom;
6186 var rows = this.el.select('tbody > tr', true).elements;
6188 if(typeof(rows[rowIndex]) == 'undefined'){
6191 rows[rowIndex].dom.style.display = state ? '' : 'none';
6195 getSelectionModel : function(){
6197 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6199 return this.selModel;
6202 * Render the Roo.bootstrap object from renderder
6204 renderCellObject : function(r)
6208 var t = r.cfg.render(r.container);
6211 Roo.each(r.cfg.cn, function(c){
6213 container: t.getChildContainer(),
6216 _this.renderCellObject(child);
6221 getRowIndex : function(row)
6225 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6248 * @class Roo.bootstrap.TableCell
6249 * @extends Roo.bootstrap.Component
6250 * Bootstrap TableCell class
6251 * @cfg {String} html cell contain text
6252 * @cfg {String} cls cell class
6253 * @cfg {String} tag cell tag (td|th) default td
6254 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6255 * @cfg {String} align Aligns the content in a cell
6256 * @cfg {String} axis Categorizes cells
6257 * @cfg {String} bgcolor Specifies the background color of a cell
6258 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6259 * @cfg {Number} colspan Specifies the number of columns a cell should span
6260 * @cfg {String} headers Specifies one or more header cells a cell is related to
6261 * @cfg {Number} height Sets the height of a cell
6262 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6263 * @cfg {Number} rowspan Sets the number of rows a cell should span
6264 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6265 * @cfg {String} valign Vertical aligns the content in a cell
6266 * @cfg {Number} width Specifies the width of a cell
6269 * Create a new TableCell
6270 * @param {Object} config The config object
6273 Roo.bootstrap.TableCell = function(config){
6274 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6277 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6297 getAutoCreate : function(){
6298 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6318 cfg.align=this.align
6324 cfg.bgcolor=this.bgcolor
6327 cfg.charoff=this.charoff
6330 cfg.colspan=this.colspan
6333 cfg.headers=this.headers
6336 cfg.height=this.height
6339 cfg.nowrap=this.nowrap
6342 cfg.rowspan=this.rowspan
6345 cfg.scope=this.scope
6348 cfg.valign=this.valign
6351 cfg.width=this.width
6370 * @class Roo.bootstrap.TableRow
6371 * @extends Roo.bootstrap.Component
6372 * Bootstrap TableRow class
6373 * @cfg {String} cls row class
6374 * @cfg {String} align Aligns the content in a table row
6375 * @cfg {String} bgcolor Specifies a background color for a table row
6376 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6377 * @cfg {String} valign Vertical aligns the content in a table row
6380 * Create a new TableRow
6381 * @param {Object} config The config object
6384 Roo.bootstrap.TableRow = function(config){
6385 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6388 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6396 getAutoCreate : function(){
6397 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6407 cfg.align = this.align;
6410 cfg.bgcolor = this.bgcolor;
6413 cfg.charoff = this.charoff;
6416 cfg.valign = this.valign;
6434 * @class Roo.bootstrap.TableBody
6435 * @extends Roo.bootstrap.Component
6436 * Bootstrap TableBody class
6437 * @cfg {String} cls element class
6438 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6439 * @cfg {String} align Aligns the content inside the element
6440 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6441 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6444 * Create a new TableBody
6445 * @param {Object} config The config object
6448 Roo.bootstrap.TableBody = function(config){
6449 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6452 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6460 getAutoCreate : function(){
6461 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6475 cfg.align = this.align;
6478 cfg.charoff = this.charoff;
6481 cfg.valign = this.valign;
6488 // initEvents : function()
6495 // this.store = Roo.factory(this.store, Roo.data);
6496 // this.store.on('load', this.onLoad, this);
6498 // this.store.load();
6502 // onLoad: function ()
6504 // this.fireEvent('load', this);
6514 * Ext JS Library 1.1.1
6515 * Copyright(c) 2006-2007, Ext JS, LLC.
6517 * Originally Released Under LGPL - original licence link has changed is not relivant.
6520 * <script type="text/javascript">
6523 // as we use this in bootstrap.
6524 Roo.namespace('Roo.form');
6526 * @class Roo.form.Action
6527 * Internal Class used to handle form actions
6529 * @param {Roo.form.BasicForm} el The form element or its id
6530 * @param {Object} config Configuration options
6535 // define the action interface
6536 Roo.form.Action = function(form, options){
6538 this.options = options || {};
6541 * Client Validation Failed
6544 Roo.form.Action.CLIENT_INVALID = 'client';
6546 * Server Validation Failed
6549 Roo.form.Action.SERVER_INVALID = 'server';
6551 * Connect to Server Failed
6554 Roo.form.Action.CONNECT_FAILURE = 'connect';
6556 * Reading Data from Server Failed
6559 Roo.form.Action.LOAD_FAILURE = 'load';
6561 Roo.form.Action.prototype = {
6563 failureType : undefined,
6564 response : undefined,
6568 run : function(options){
6573 success : function(response){
6578 handleResponse : function(response){
6582 // default connection failure
6583 failure : function(response){
6585 this.response = response;
6586 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6587 this.form.afterAction(this, false);
6590 processResponse : function(response){
6591 this.response = response;
6592 if(!response.responseText){
6595 this.result = this.handleResponse(response);
6599 // utility functions used internally
6600 getUrl : function(appendParams){
6601 var url = this.options.url || this.form.url || this.form.el.dom.action;
6603 var p = this.getParams();
6605 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6611 getMethod : function(){
6612 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6615 getParams : function(){
6616 var bp = this.form.baseParams;
6617 var p = this.options.params;
6619 if(typeof p == "object"){
6620 p = Roo.urlEncode(Roo.applyIf(p, bp));
6621 }else if(typeof p == 'string' && bp){
6622 p += '&' + Roo.urlEncode(bp);
6625 p = Roo.urlEncode(bp);
6630 createCallback : function(){
6632 success: this.success,
6633 failure: this.failure,
6635 timeout: (this.form.timeout*1000),
6636 upload: this.form.fileUpload ? this.success : undefined
6641 Roo.form.Action.Submit = function(form, options){
6642 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6645 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6648 haveProgress : false,
6649 uploadComplete : false,
6651 // uploadProgress indicator.
6652 uploadProgress : function()
6654 if (!this.form.progressUrl) {
6658 if (!this.haveProgress) {
6659 Roo.MessageBox.progress("Uploading", "Uploading");
6661 if (this.uploadComplete) {
6662 Roo.MessageBox.hide();
6666 this.haveProgress = true;
6668 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6670 var c = new Roo.data.Connection();
6672 url : this.form.progressUrl,
6677 success : function(req){
6678 //console.log(data);
6682 rdata = Roo.decode(req.responseText)
6684 Roo.log("Invalid data from server..");
6688 if (!rdata || !rdata.success) {
6690 Roo.MessageBox.alert(Roo.encode(rdata));
6693 var data = rdata.data;
6695 if (this.uploadComplete) {
6696 Roo.MessageBox.hide();
6701 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6702 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6705 this.uploadProgress.defer(2000,this);
6708 failure: function(data) {
6709 Roo.log('progress url failed ');
6720 // run get Values on the form, so it syncs any secondary forms.
6721 this.form.getValues();
6723 var o = this.options;
6724 var method = this.getMethod();
6725 var isPost = method == 'POST';
6726 if(o.clientValidation === false || this.form.isValid()){
6728 if (this.form.progressUrl) {
6729 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6730 (new Date() * 1) + '' + Math.random());
6735 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6736 form:this.form.el.dom,
6737 url:this.getUrl(!isPost),
6739 params:isPost ? this.getParams() : null,
6740 isUpload: this.form.fileUpload
6743 this.uploadProgress();
6745 }else if (o.clientValidation !== false){ // client validation failed
6746 this.failureType = Roo.form.Action.CLIENT_INVALID;
6747 this.form.afterAction(this, false);
6751 success : function(response)
6753 this.uploadComplete= true;
6754 if (this.haveProgress) {
6755 Roo.MessageBox.hide();
6759 var result = this.processResponse(response);
6760 if(result === true || result.success){
6761 this.form.afterAction(this, true);
6765 this.form.markInvalid(result.errors);
6766 this.failureType = Roo.form.Action.SERVER_INVALID;
6768 this.form.afterAction(this, false);
6770 failure : function(response)
6772 this.uploadComplete= true;
6773 if (this.haveProgress) {
6774 Roo.MessageBox.hide();
6777 this.response = response;
6778 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6779 this.form.afterAction(this, false);
6782 handleResponse : function(response){
6783 if(this.form.errorReader){
6784 var rs = this.form.errorReader.read(response);
6787 for(var i = 0, len = rs.records.length; i < len; i++) {
6788 var r = rs.records[i];
6792 if(errors.length < 1){
6796 success : rs.success,
6802 ret = Roo.decode(response.responseText);
6806 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6816 Roo.form.Action.Load = function(form, options){
6817 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6818 this.reader = this.form.reader;
6821 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6826 Roo.Ajax.request(Roo.apply(
6827 this.createCallback(), {
6828 method:this.getMethod(),
6829 url:this.getUrl(false),
6830 params:this.getParams()
6834 success : function(response){
6836 var result = this.processResponse(response);
6837 if(result === true || !result.success || !result.data){
6838 this.failureType = Roo.form.Action.LOAD_FAILURE;
6839 this.form.afterAction(this, false);
6842 this.form.clearInvalid();
6843 this.form.setValues(result.data);
6844 this.form.afterAction(this, true);
6847 handleResponse : function(response){
6848 if(this.form.reader){
6849 var rs = this.form.reader.read(response);
6850 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6852 success : rs.success,
6856 return Roo.decode(response.responseText);
6860 Roo.form.Action.ACTION_TYPES = {
6861 'load' : Roo.form.Action.Load,
6862 'submit' : Roo.form.Action.Submit
6871 * @class Roo.bootstrap.Form
6872 * @extends Roo.bootstrap.Component
6873 * Bootstrap Form class
6874 * @cfg {String} method GET | POST (default POST)
6875 * @cfg {String} labelAlign top | left (default top)
6876 * @cfg {String} align left | right - for navbars
6877 * @cfg {Boolean} loadMask load mask when submit (default true)
6882 * @param {Object} config The config object
6886 Roo.bootstrap.Form = function(config){
6887 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6890 * @event clientvalidation
6891 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6892 * @param {Form} this
6893 * @param {Boolean} valid true if the form has passed client-side validation
6895 clientvalidation: true,
6897 * @event beforeaction
6898 * Fires before any action is performed. Return false to cancel the action.
6899 * @param {Form} this
6900 * @param {Action} action The action to be performed
6904 * @event actionfailed
6905 * Fires when an action fails.
6906 * @param {Form} this
6907 * @param {Action} action The action that failed
6909 actionfailed : true,
6911 * @event actioncomplete
6912 * Fires when an action is completed.
6913 * @param {Form} this
6914 * @param {Action} action The action that completed
6916 actioncomplete : true
6921 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6924 * @cfg {String} method
6925 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6930 * The URL to use for form actions if one isn't supplied in the action options.
6933 * @cfg {Boolean} fileUpload
6934 * Set to true if this form is a file upload.
6938 * @cfg {Object} baseParams
6939 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6943 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6947 * @cfg {Sting} align (left|right) for navbar forms
6952 activeAction : null,
6955 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6956 * element by passing it or its id or mask the form itself by passing in true.
6959 waitMsgTarget : false,
6963 getAutoCreate : function(){
6967 method : this.method || 'POST',
6968 id : this.id || Roo.id(),
6971 if (this.parent().xtype.match(/^Nav/)) {
6972 cfg.cls = 'navbar-form navbar-' + this.align;
6976 if (this.labelAlign == 'left' ) {
6977 cfg.cls += ' form-horizontal';
6983 initEvents : function()
6985 this.el.on('submit', this.onSubmit, this);
6986 // this was added as random key presses on the form where triggering form submit.
6987 this.el.on('keypress', function(e) {
6988 if (e.getCharCode() != 13) {
6991 // we might need to allow it for textareas.. and some other items.
6992 // check e.getTarget().
6994 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6998 Roo.log("keypress blocked");
7006 onSubmit : function(e){
7011 * Returns true if client-side validation on the form is successful.
7014 isValid : function(){
7015 var items = this.getItems();
7017 items.each(function(f){
7026 * Returns true if any fields in this form have changed since their original load.
7029 isDirty : function(){
7031 var items = this.getItems();
7032 items.each(function(f){
7042 * Performs a predefined action (submit or load) or custom actions you define on this form.
7043 * @param {String} actionName The name of the action type
7044 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7045 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7046 * accept other config options):
7048 Property Type Description
7049 ---------------- --------------- ----------------------------------------------------------------------------------
7050 url String The url for the action (defaults to the form's url)
7051 method String The form method to use (defaults to the form's method, or POST if not defined)
7052 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7053 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7054 validate the form on the client (defaults to false)
7056 * @return {BasicForm} this
7058 doAction : function(action, options){
7059 if(typeof action == 'string'){
7060 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7062 if(this.fireEvent('beforeaction', this, action) !== false){
7063 this.beforeAction(action);
7064 action.run.defer(100, action);
7070 beforeAction : function(action){
7071 var o = action.options;
7074 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7076 // not really supported yet.. ??
7078 //if(this.waitMsgTarget === true){
7079 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7080 //}else if(this.waitMsgTarget){
7081 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7082 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7084 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7090 afterAction : function(action, success){
7091 this.activeAction = null;
7092 var o = action.options;
7094 //if(this.waitMsgTarget === true){
7096 //}else if(this.waitMsgTarget){
7097 // this.waitMsgTarget.unmask();
7099 // Roo.MessageBox.updateProgress(1);
7100 // Roo.MessageBox.hide();
7107 Roo.callback(o.success, o.scope, [this, action]);
7108 this.fireEvent('actioncomplete', this, action);
7112 // failure condition..
7113 // we have a scenario where updates need confirming.
7114 // eg. if a locking scenario exists..
7115 // we look for { errors : { needs_confirm : true }} in the response.
7117 (typeof(action.result) != 'undefined') &&
7118 (typeof(action.result.errors) != 'undefined') &&
7119 (typeof(action.result.errors.needs_confirm) != 'undefined')
7122 Roo.log("not supported yet");
7125 Roo.MessageBox.confirm(
7126 "Change requires confirmation",
7127 action.result.errorMsg,
7132 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7142 Roo.callback(o.failure, o.scope, [this, action]);
7143 // show an error message if no failed handler is set..
7144 if (!this.hasListener('actionfailed')) {
7145 Roo.log("need to add dialog support");
7147 Roo.MessageBox.alert("Error",
7148 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7149 action.result.errorMsg :
7150 "Saving Failed, please check your entries or try again"
7155 this.fireEvent('actionfailed', this, action);
7160 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7161 * @param {String} id The value to search for
7164 findField : function(id){
7165 var items = this.getItems();
7166 var field = items.get(id);
7168 items.each(function(f){
7169 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7176 return field || null;
7179 * Mark fields in this form invalid in bulk.
7180 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7181 * @return {BasicForm} this
7183 markInvalid : function(errors){
7184 if(errors instanceof Array){
7185 for(var i = 0, len = errors.length; i < len; i++){
7186 var fieldError = errors[i];
7187 var f = this.findField(fieldError.id);
7189 f.markInvalid(fieldError.msg);
7195 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7196 field.markInvalid(errors[id]);
7200 //Roo.each(this.childForms || [], function (f) {
7201 // f.markInvalid(errors);
7208 * Set values for fields in this form in bulk.
7209 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7210 * @return {BasicForm} this
7212 setValues : function(values){
7213 if(values instanceof Array){ // array of objects
7214 for(var i = 0, len = values.length; i < len; i++){
7216 var f = this.findField(v.id);
7218 f.setValue(v.value);
7219 if(this.trackResetOnLoad){
7220 f.originalValue = f.getValue();
7224 }else{ // object hash
7227 if(typeof values[id] != 'function' && (field = this.findField(id))){
7229 if (field.setFromData &&
7231 field.displayField &&
7232 // combos' with local stores can
7233 // be queried via setValue()
7234 // to set their value..
7235 (field.store && !field.store.isLocal)
7239 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7240 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7241 field.setFromData(sd);
7244 field.setValue(values[id]);
7248 if(this.trackResetOnLoad){
7249 field.originalValue = field.getValue();
7255 //Roo.each(this.childForms || [], function (f) {
7256 // f.setValues(values);
7263 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7264 * they are returned as an array.
7265 * @param {Boolean} asString
7268 getValues : function(asString){
7269 //if (this.childForms) {
7270 // copy values from the child forms
7271 // Roo.each(this.childForms, function (f) {
7272 // this.setValues(f.getValues());
7278 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7279 if(asString === true){
7282 return Roo.urlDecode(fs);
7286 * Returns the fields in this form as an object with key/value pairs.
7287 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7290 getFieldValues : function(with_hidden)
7292 var items = this.getItems();
7294 items.each(function(f){
7298 var v = f.getValue();
7299 if (f.inputType =='radio') {
7300 if (typeof(ret[f.getName()]) == 'undefined') {
7301 ret[f.getName()] = ''; // empty..
7304 if (!f.el.dom.checked) {
7312 // not sure if this supported any more..
7313 if ((typeof(v) == 'object') && f.getRawValue) {
7314 v = f.getRawValue() ; // dates..
7316 // combo boxes where name != hiddenName...
7317 if (f.name != f.getName()) {
7318 ret[f.name] = f.getRawValue();
7320 ret[f.getName()] = v;
7327 * Clears all invalid messages in this form.
7328 * @return {BasicForm} this
7330 clearInvalid : function(){
7331 var items = this.getItems();
7333 items.each(function(f){
7344 * @return {BasicForm} this
7347 var items = this.getItems();
7348 items.each(function(f){
7352 Roo.each(this.childForms || [], function (f) {
7359 getItems : function()
7361 var r=new Roo.util.MixedCollection(false, function(o){
7362 return o.id || (o.id = Roo.id());
7364 var iter = function(el) {
7371 Roo.each(el.items,function(e) {
7391 * Ext JS Library 1.1.1
7392 * Copyright(c) 2006-2007, Ext JS, LLC.
7394 * Originally Released Under LGPL - original licence link has changed is not relivant.
7397 * <script type="text/javascript">
7400 * @class Roo.form.VTypes
7401 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7404 Roo.form.VTypes = function(){
7405 // closure these in so they are only created once.
7406 var alpha = /^[a-zA-Z_]+$/;
7407 var alphanum = /^[a-zA-Z0-9_]+$/;
7408 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7409 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7411 // All these messages and functions are configurable
7414 * The function used to validate email addresses
7415 * @param {String} value The email address
7417 'email' : function(v){
7418 return email.test(v);
7421 * The error text to display when the email validation function returns false
7424 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7426 * The keystroke filter mask to be applied on email input
7429 'emailMask' : /[a-z0-9_\.\-@]/i,
7432 * The function used to validate URLs
7433 * @param {String} value The URL
7435 'url' : function(v){
7439 * The error text to display when the url validation function returns false
7442 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7445 * The function used to validate alpha values
7446 * @param {String} value The value
7448 'alpha' : function(v){
7449 return alpha.test(v);
7452 * The error text to display when the alpha validation function returns false
7455 'alphaText' : 'This field should only contain letters and _',
7457 * The keystroke filter mask to be applied on alpha input
7460 'alphaMask' : /[a-z_]/i,
7463 * The function used to validate alphanumeric values
7464 * @param {String} value The value
7466 'alphanum' : function(v){
7467 return alphanum.test(v);
7470 * The error text to display when the alphanumeric validation function returns false
7473 'alphanumText' : 'This field should only contain letters, numbers and _',
7475 * The keystroke filter mask to be applied on alphanumeric input
7478 'alphanumMask' : /[a-z0-9_]/i
7488 * @class Roo.bootstrap.Input
7489 * @extends Roo.bootstrap.Component
7490 * Bootstrap Input class
7491 * @cfg {Boolean} disabled is it disabled
7492 * @cfg {String} fieldLabel - the label associated
7493 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7494 * @cfg {String} name name of the input
7495 * @cfg {string} fieldLabel - the label associated
7496 * @cfg {string} inputType - input / file submit ...
7497 * @cfg {string} placeholder - placeholder to put in text.
7498 * @cfg {string} before - input group add on before
7499 * @cfg {string} after - input group add on after
7500 * @cfg {string} size - (lg|sm) or leave empty..
7501 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7502 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7503 * @cfg {Number} md colspan out of 12 for computer-sized screens
7504 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7505 * @cfg {string} value default value of the input
7506 * @cfg {Number} labelWidth set the width of label (0-12)
7507 * @cfg {String} labelAlign (top|left)
7508 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7509 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7511 * @cfg {String} align (left|center|right) Default left
7512 * @cfg {Boolean} forceFeedback (true|false) Default false
7518 * Create a new Input
7519 * @param {Object} config The config object
7522 Roo.bootstrap.Input = function(config){
7523 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7528 * Fires when this field receives input focus.
7529 * @param {Roo.form.Field} this
7534 * Fires when this field loses input focus.
7535 * @param {Roo.form.Field} this
7540 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7541 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7542 * @param {Roo.form.Field} this
7543 * @param {Roo.EventObject} e The event object
7548 * Fires just before the field blurs if the field value has changed.
7549 * @param {Roo.form.Field} this
7550 * @param {Mixed} newValue The new value
7551 * @param {Mixed} oldValue The original value
7556 * Fires after the field has been marked as invalid.
7557 * @param {Roo.form.Field} this
7558 * @param {String} msg The validation message
7563 * Fires after the field has been validated with no errors.
7564 * @param {Roo.form.Field} this
7569 * Fires after the key up
7570 * @param {Roo.form.Field} this
7571 * @param {Roo.EventObject} e The event Object
7577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
7579 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7580 automatic validation (defaults to "keyup").
7582 validationEvent : "keyup",
7584 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7586 validateOnBlur : true,
7588 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7590 validationDelay : 250,
7592 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7594 focusClass : "x-form-focus", // not needed???
7598 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7600 invalidClass : "has-warning",
7603 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7605 validClass : "has-success",
7608 * @cfg {Boolean} hasFeedback (true|false) default true
7613 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7615 invalidFeedbackClass : "glyphicon-warning-sign",
7618 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7620 validFeedbackClass : "glyphicon-ok",
7623 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7625 selectOnFocus : false,
7628 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7632 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7637 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7639 disableKeyFilter : false,
7642 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7646 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7650 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7652 blankText : "This field is required",
7655 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7659 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7661 maxLength : Number.MAX_VALUE,
7663 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7665 minLengthText : "The minimum length for this field is {0}",
7667 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7669 maxLengthText : "The maximum length for this field is {0}",
7673 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7674 * If available, this function will be called only after the basic validators all return true, and will be passed the
7675 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7679 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7680 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7681 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7685 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7689 autocomplete: false,
7708 formatedValue : false,
7709 forceFeedback : false,
7711 parentLabelAlign : function()
7714 while (parent.parent()) {
7715 parent = parent.parent();
7716 if (typeof(parent.labelAlign) !='undefined') {
7717 return parent.labelAlign;
7724 getAutoCreate : function(){
7726 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7732 if(this.inputType != 'hidden'){
7733 cfg.cls = 'form-group' //input-group
7739 type : this.inputType,
7741 cls : 'form-control',
7742 placeholder : this.placeholder || '',
7743 autocomplete : this.autocomplete || 'new-password'
7748 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7751 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7752 input.maxLength = this.maxLength;
7755 if (this.disabled) {
7756 input.disabled=true;
7759 if (this.readOnly) {
7760 input.readonly=true;
7764 input.name = this.name;
7767 input.cls += ' input-' + this.size;
7770 ['xs','sm','md','lg'].map(function(size){
7771 if (settings[size]) {
7772 cfg.cls += ' col-' + size + '-' + settings[size];
7776 var inputblock = input;
7780 cls: 'glyphicon form-control-feedback'
7783 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7786 cls : 'has-feedback',
7794 if (this.before || this.after) {
7797 cls : 'input-group',
7801 if (this.before && typeof(this.before) == 'string') {
7803 inputblock.cn.push({
7805 cls : 'roo-input-before input-group-addon',
7809 if (this.before && typeof(this.before) == 'object') {
7810 this.before = Roo.factory(this.before);
7811 Roo.log(this.before);
7812 inputblock.cn.push({
7814 cls : 'roo-input-before input-group-' +
7815 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7819 inputblock.cn.push(input);
7821 if (this.after && typeof(this.after) == 'string') {
7822 inputblock.cn.push({
7824 cls : 'roo-input-after input-group-addon',
7828 if (this.after && typeof(this.after) == 'object') {
7829 this.after = Roo.factory(this.after);
7830 Roo.log(this.after);
7831 inputblock.cn.push({
7833 cls : 'roo-input-after input-group-' +
7834 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7838 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7839 inputblock.cls += ' has-feedback';
7840 inputblock.cn.push(feedback);
7844 if (align ==='left' && this.fieldLabel.length) {
7845 Roo.log("left and has label");
7851 cls : 'control-label col-sm-' + this.labelWidth,
7852 html : this.fieldLabel
7856 cls : "col-sm-" + (12 - this.labelWidth),
7863 } else if ( this.fieldLabel.length) {
7869 //cls : 'input-group-addon',
7870 html : this.fieldLabel
7880 Roo.log(" no label && no align");
7889 Roo.log('input-parentType: ' + this.parentType);
7891 if (this.parentType === 'Navbar' && this.parent().bar) {
7892 cfg.cls += ' navbar-form';
7900 * return the real input element.
7902 inputEl: function ()
7904 return this.el.select('input.form-control',true).first();
7907 tooltipEl : function()
7909 return this.inputEl();
7912 setDisabled : function(v)
7914 var i = this.inputEl().dom;
7916 i.removeAttribute('disabled');
7920 i.setAttribute('disabled','true');
7922 initEvents : function()
7925 this.inputEl().on("keydown" , this.fireKey, this);
7926 this.inputEl().on("focus", this.onFocus, this);
7927 this.inputEl().on("blur", this.onBlur, this);
7929 this.inputEl().relayEvent('keyup', this);
7931 // reference to original value for reset
7932 this.originalValue = this.getValue();
7933 //Roo.form.TextField.superclass.initEvents.call(this);
7934 if(this.validationEvent == 'keyup'){
7935 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7936 this.inputEl().on('keyup', this.filterValidation, this);
7938 else if(this.validationEvent !== false){
7939 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7942 if(this.selectOnFocus){
7943 this.on("focus", this.preFocus, this);
7946 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7947 this.inputEl().on("keypress", this.filterKeys, this);
7950 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7951 this.el.on("click", this.autoSize, this);
7954 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7955 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7958 if (typeof(this.before) == 'object') {
7959 this.before.render(this.el.select('.roo-input-before',true).first());
7961 if (typeof(this.after) == 'object') {
7962 this.after.render(this.el.select('.roo-input-after',true).first());
7967 filterValidation : function(e){
7968 if(!e.isNavKeyPress()){
7969 this.validationTask.delay(this.validationDelay);
7973 * Validates the field value
7974 * @return {Boolean} True if the value is valid, else false
7976 validate : function(){
7977 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7978 if(this.disabled || this.validateValue(this.getRawValue())){
7989 * Validates a value according to the field's validation rules and marks the field as invalid
7990 * if the validation fails
7991 * @param {Mixed} value The value to validate
7992 * @return {Boolean} True if the value is valid, else false
7994 validateValue : function(value){
7995 if(value.length < 1) { // if it's blank
7996 if(this.allowBlank){
8002 if(value.length < this.minLength){
8005 if(value.length > this.maxLength){
8009 var vt = Roo.form.VTypes;
8010 if(!vt[this.vtype](value, this)){
8014 if(typeof this.validator == "function"){
8015 var msg = this.validator(value);
8021 if(this.regex && !this.regex.test(value)){
8031 fireKey : function(e){
8032 //Roo.log('field ' + e.getKey());
8033 if(e.isNavKeyPress()){
8034 this.fireEvent("specialkey", this, e);
8037 focus : function (selectText){
8039 this.inputEl().focus();
8040 if(selectText === true){
8041 this.inputEl().dom.select();
8047 onFocus : function(){
8048 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8049 // this.el.addClass(this.focusClass);
8052 this.hasFocus = true;
8053 this.startValue = this.getValue();
8054 this.fireEvent("focus", this);
8058 beforeBlur : Roo.emptyFn,
8062 onBlur : function(){
8064 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8065 //this.el.removeClass(this.focusClass);
8067 this.hasFocus = false;
8068 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8071 var v = this.getValue();
8072 if(String(v) !== String(this.startValue)){
8073 this.fireEvent('change', this, v, this.startValue);
8075 this.fireEvent("blur", this);
8079 * Resets the current field value to the originally loaded value and clears any validation messages
8082 this.setValue(this.originalValue);
8086 * Returns the name of the field
8087 * @return {Mixed} name The name field
8089 getName: function(){
8093 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8094 * @return {Mixed} value The field value
8096 getValue : function(){
8098 var v = this.inputEl().getValue();
8103 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8104 * @return {Mixed} value The field value
8106 getRawValue : function(){
8107 var v = this.inputEl().getValue();
8113 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8114 * @param {Mixed} value The value to set
8116 setRawValue : function(v){
8117 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8120 selectText : function(start, end){
8121 var v = this.getRawValue();
8123 start = start === undefined ? 0 : start;
8124 end = end === undefined ? v.length : end;
8125 var d = this.inputEl().dom;
8126 if(d.setSelectionRange){
8127 d.setSelectionRange(start, end);
8128 }else if(d.createTextRange){
8129 var range = d.createTextRange();
8130 range.moveStart("character", start);
8131 range.moveEnd("character", v.length-end);
8138 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8139 * @param {Mixed} value The value to set
8141 setValue : function(v){
8144 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8150 processValue : function(value){
8151 if(this.stripCharsRe){
8152 var newValue = value.replace(this.stripCharsRe, '');
8153 if(newValue !== value){
8154 this.setRawValue(newValue);
8161 preFocus : function(){
8163 if(this.selectOnFocus){
8164 this.inputEl().dom.select();
8167 filterKeys : function(e){
8169 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8172 var c = e.getCharCode(), cc = String.fromCharCode(c);
8173 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8176 if(!this.maskRe.test(cc)){
8181 * Clear any invalid styles/messages for this field
8183 clearInvalid : function(){
8185 if(!this.el || this.preventMark){ // not rendered
8188 this.el.removeClass(this.invalidClass);
8190 this.fireEvent('valid', this);
8194 * Mark this field as valid
8196 markValid : function(){
8197 if(!this.el || this.preventMark){ // not rendered
8201 this.el.removeClass([this.invalidClass, this.validClass]);
8203 var feedback = this.el.select('.form-control-feedback', true).first();
8206 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8209 if(this.disabled || this.allowBlank){
8213 this.el.addClass(this.validClass);
8215 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8217 var feedback = this.el.select('.form-control-feedback', true).first();
8220 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8221 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8226 this.fireEvent('valid', this);
8230 * Mark this field as invalid
8231 * @param {String} msg The validation message
8233 markInvalid : function(msg){
8234 if(!this.el || this.preventMark){ // not rendered
8238 this.el.removeClass([this.invalidClass, this.validClass]);
8240 var feedback = this.el.select('.form-control-feedback', true).first();
8243 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8246 if(this.disabled || this.allowBlank){
8250 this.el.addClass(this.invalidClass);
8252 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8254 var feedback = this.el.select('.form-control-feedback', true).first();
8257 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8259 if(this.getValue().length || this.forceFeedback){
8260 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8267 this.fireEvent('invalid', this, msg);
8270 SafariOnKeyDown : function(event)
8272 // this is a workaround for a password hang bug on chrome/ webkit.
8274 var isSelectAll = false;
8276 if(this.inputEl().dom.selectionEnd > 0){
8277 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8279 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8280 event.preventDefault();
8285 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8287 event.preventDefault();
8288 // this is very hacky as keydown always get's upper case.
8290 var cc = String.fromCharCode(event.getCharCode());
8291 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8295 adjustWidth : function(tag, w){
8296 tag = tag.toLowerCase();
8297 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8298 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8302 if(tag == 'textarea'){
8305 }else if(Roo.isOpera){
8309 if(tag == 'textarea'){
8328 * @class Roo.bootstrap.TextArea
8329 * @extends Roo.bootstrap.Input
8330 * Bootstrap TextArea class
8331 * @cfg {Number} cols Specifies the visible width of a text area
8332 * @cfg {Number} rows Specifies the visible number of lines in a text area
8333 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8334 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8335 * @cfg {string} html text
8338 * Create a new TextArea
8339 * @param {Object} config The config object
8342 Roo.bootstrap.TextArea = function(config){
8343 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8347 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8357 getAutoCreate : function(){
8359 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8370 value : this.value || '',
8371 html: this.html || '',
8372 cls : 'form-control',
8373 placeholder : this.placeholder || ''
8377 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8378 input.maxLength = this.maxLength;
8382 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8386 input.cols = this.cols;
8389 if (this.readOnly) {
8390 input.readonly = true;
8394 input.name = this.name;
8398 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8402 ['xs','sm','md','lg'].map(function(size){
8403 if (settings[size]) {
8404 cfg.cls += ' col-' + size + '-' + settings[size];
8408 var inputblock = input;
8410 if(this.hasFeedback && !this.allowBlank){
8414 cls: 'glyphicon form-control-feedback'
8418 cls : 'has-feedback',
8427 if (this.before || this.after) {
8430 cls : 'input-group',
8434 inputblock.cn.push({
8436 cls : 'input-group-addon',
8441 inputblock.cn.push(input);
8443 if(this.hasFeedback && !this.allowBlank){
8444 inputblock.cls += ' has-feedback';
8445 inputblock.cn.push(feedback);
8449 inputblock.cn.push({
8451 cls : 'input-group-addon',
8458 if (align ==='left' && this.fieldLabel.length) {
8459 Roo.log("left and has label");
8465 cls : 'control-label col-sm-' + this.labelWidth,
8466 html : this.fieldLabel
8470 cls : "col-sm-" + (12 - this.labelWidth),
8477 } else if ( this.fieldLabel.length) {
8483 //cls : 'input-group-addon',
8484 html : this.fieldLabel
8494 Roo.log(" no label && no align");
8504 if (this.disabled) {
8505 input.disabled=true;
8512 * return the real textarea element.
8514 inputEl: function ()
8516 return this.el.select('textarea.form-control',true).first();
8524 * trigger field - base class for combo..
8529 * @class Roo.bootstrap.TriggerField
8530 * @extends Roo.bootstrap.Input
8531 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8532 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8533 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8534 * for which you can provide a custom implementation. For example:
8536 var trigger = new Roo.bootstrap.TriggerField();
8537 trigger.onTriggerClick = myTriggerFn;
8538 trigger.applyTo('my-field');
8541 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8542 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8543 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
8544 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8545 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8548 * Create a new TriggerField.
8549 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8550 * to the base TextField)
8552 Roo.bootstrap.TriggerField = function(config){
8553 this.mimicing = false;
8554 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8557 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
8559 * @cfg {String} triggerClass A CSS class to apply to the trigger
8562 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8567 * @cfg {Boolean} removable (true|false) special filter default false
8571 /** @cfg {Boolean} grow @hide */
8572 /** @cfg {Number} growMin @hide */
8573 /** @cfg {Number} growMax @hide */
8579 autoSize: Roo.emptyFn,
8586 actionMode : 'wrap',
8591 getAutoCreate : function(){
8593 var align = this.labelAlign || this.parentLabelAlign();
8598 cls: 'form-group' //input-group
8605 type : this.inputType,
8606 cls : 'form-control',
8607 autocomplete: 'new-password',
8608 placeholder : this.placeholder || ''
8612 input.name = this.name;
8615 input.cls += ' input-' + this.size;
8618 if (this.disabled) {
8619 input.disabled=true;
8622 var inputblock = input;
8624 if(this.hasFeedback && !this.allowBlank){
8628 cls: 'glyphicon form-control-feedback'
8631 if(this.removable && !this.editable && !this.tickable){
8633 cls : 'has-feedback',
8639 cls : 'roo-combo-removable-btn close'
8646 cls : 'has-feedback',
8655 if(this.removable && !this.editable && !this.tickable){
8657 cls : 'roo-removable',
8663 cls : 'roo-combo-removable-btn close'
8670 if (this.before || this.after) {
8673 cls : 'input-group',
8677 inputblock.cn.push({
8679 cls : 'input-group-addon',
8684 inputblock.cn.push(input);
8686 if(this.hasFeedback && !this.allowBlank){
8687 inputblock.cls += ' has-feedback';
8688 inputblock.cn.push(feedback);
8692 inputblock.cn.push({
8694 cls : 'input-group-addon',
8707 cls: 'form-hidden-field'
8715 Roo.log('multiple');
8723 cls: 'form-hidden-field'
8727 cls: 'select2-choices',
8731 cls: 'select2-search-field',
8744 cls: 'select2-container input-group',
8749 // cls: 'typeahead typeahead-long dropdown-menu',
8750 // style: 'display:none'
8755 if(!this.multiple && this.showToggleBtn){
8761 if (this.caret != false) {
8764 cls: 'fa fa-' + this.caret
8771 cls : 'input-group-addon btn dropdown-toggle',
8776 cls: 'combobox-clear',
8790 combobox.cls += ' select2-container-multi';
8793 if (align ==='left' && this.fieldLabel.length) {
8795 Roo.log("left and has label");
8801 cls : 'control-label col-sm-' + this.labelWidth,
8802 html : this.fieldLabel
8806 cls : "col-sm-" + (12 - this.labelWidth),
8813 } else if ( this.fieldLabel.length) {
8819 //cls : 'input-group-addon',
8820 html : this.fieldLabel
8830 Roo.log(" no label && no align");
8837 ['xs','sm','md','lg'].map(function(size){
8838 if (settings[size]) {
8839 cfg.cls += ' col-' + size + '-' + settings[size];
8850 onResize : function(w, h){
8851 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8852 // if(typeof w == 'number'){
8853 // var x = w - this.trigger.getWidth();
8854 // this.inputEl().setWidth(this.adjustWidth('input', x));
8855 // this.trigger.setStyle('left', x+'px');
8860 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8863 getResizeEl : function(){
8864 return this.inputEl();
8868 getPositionEl : function(){
8869 return this.inputEl();
8873 alignErrorIcon : function(){
8874 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8878 initEvents : function(){
8882 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8883 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8884 if(!this.multiple && this.showToggleBtn){
8885 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8886 if(this.hideTrigger){
8887 this.trigger.setDisplayed(false);
8889 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8893 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8896 if(this.removable && !this.editable && !this.tickable){
8897 var close = this.closeTriggerEl();
8900 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8901 close.on('click', this.removeBtnClick, this, close);
8905 //this.trigger.addClassOnOver('x-form-trigger-over');
8906 //this.trigger.addClassOnClick('x-form-trigger-click');
8909 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8913 closeTriggerEl : function()
8915 var close = this.el.select('.roo-combo-removable-btn', true).first();
8916 return close ? close : false;
8919 removeBtnClick : function(e, h, el)
8923 if(this.fireEvent("remove", this) !== false){
8928 createList : function()
8930 this.list = Roo.get(document.body).createChild({
8932 cls: 'typeahead typeahead-long dropdown-menu',
8933 style: 'display:none'
8936 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8941 initTrigger : function(){
8946 onDestroy : function(){
8948 this.trigger.removeAllListeners();
8949 // this.trigger.remove();
8952 // this.wrap.remove();
8954 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8958 onFocus : function(){
8959 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8962 this.wrap.addClass('x-trigger-wrap-focus');
8963 this.mimicing = true;
8964 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8965 if(this.monitorTab){
8966 this.el.on("keydown", this.checkTab, this);
8973 checkTab : function(e){
8974 if(e.getKey() == e.TAB){
8980 onBlur : function(){
8985 mimicBlur : function(e, t){
8987 if(!this.wrap.contains(t) && this.validateBlur()){
8994 triggerBlur : function(){
8995 this.mimicing = false;
8996 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8997 if(this.monitorTab){
8998 this.el.un("keydown", this.checkTab, this);
9000 //this.wrap.removeClass('x-trigger-wrap-focus');
9001 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9005 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9006 validateBlur : function(e, t){
9011 onDisable : function(){
9012 this.inputEl().dom.disabled = true;
9013 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9015 // this.wrap.addClass('x-item-disabled');
9020 onEnable : function(){
9021 this.inputEl().dom.disabled = false;
9022 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9024 // this.el.removeClass('x-item-disabled');
9029 onShow : function(){
9030 var ae = this.getActionEl();
9033 ae.dom.style.display = '';
9034 ae.dom.style.visibility = 'visible';
9040 onHide : function(){
9041 var ae = this.getActionEl();
9042 ae.dom.style.display = 'none';
9046 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9047 * by an implementing function.
9049 * @param {EventObject} e
9051 onTriggerClick : Roo.emptyFn
9055 * Ext JS Library 1.1.1
9056 * Copyright(c) 2006-2007, Ext JS, LLC.
9058 * Originally Released Under LGPL - original licence link has changed is not relivant.
9061 * <script type="text/javascript">
9066 * @class Roo.data.SortTypes
9068 * Defines the default sorting (casting?) comparison functions used when sorting data.
9070 Roo.data.SortTypes = {
9072 * Default sort that does nothing
9073 * @param {Mixed} s The value being converted
9074 * @return {Mixed} The comparison value
9081 * The regular expression used to strip tags
9085 stripTagsRE : /<\/?[^>]+>/gi,
9088 * Strips all HTML tags to sort on text only
9089 * @param {Mixed} s The value being converted
9090 * @return {String} The comparison value
9092 asText : function(s){
9093 return String(s).replace(this.stripTagsRE, "");
9097 * Strips all HTML tags to sort on text only - Case insensitive
9098 * @param {Mixed} s The value being converted
9099 * @return {String} The comparison value
9101 asUCText : function(s){
9102 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9106 * Case insensitive string
9107 * @param {Mixed} s The value being converted
9108 * @return {String} The comparison value
9110 asUCString : function(s) {
9111 return String(s).toUpperCase();
9116 * @param {Mixed} s The value being converted
9117 * @return {Number} The comparison value
9119 asDate : function(s) {
9123 if(s instanceof Date){
9126 return Date.parse(String(s));
9131 * @param {Mixed} s The value being converted
9132 * @return {Float} The comparison value
9134 asFloat : function(s) {
9135 var val = parseFloat(String(s).replace(/,/g, ""));
9136 if(isNaN(val)) val = 0;
9142 * @param {Mixed} s The value being converted
9143 * @return {Number} The comparison value
9145 asInt : function(s) {
9146 var val = parseInt(String(s).replace(/,/g, ""));
9147 if(isNaN(val)) val = 0;
9152 * Ext JS Library 1.1.1
9153 * Copyright(c) 2006-2007, Ext JS, LLC.
9155 * Originally Released Under LGPL - original licence link has changed is not relivant.
9158 * <script type="text/javascript">
9162 * @class Roo.data.Record
9163 * Instances of this class encapsulate both record <em>definition</em> information, and record
9164 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9165 * to access Records cached in an {@link Roo.data.Store} object.<br>
9167 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9168 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9171 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9173 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9174 * {@link #create}. The parameters are the same.
9175 * @param {Array} data An associative Array of data values keyed by the field name.
9176 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9177 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9178 * not specified an integer id is generated.
9180 Roo.data.Record = function(data, id){
9181 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9186 * Generate a constructor for a specific record layout.
9187 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9188 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9189 * Each field definition object may contain the following properties: <ul>
9190 * <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,
9191 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9192 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9193 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9194 * is being used, then this is a string containing the javascript expression to reference the data relative to
9195 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9196 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9197 * this may be omitted.</p></li>
9198 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9199 * <ul><li>auto (Default, implies no conversion)</li>
9204 * <li>date</li></ul></p></li>
9205 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9206 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9207 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9208 * by the Reader into an object that will be stored in the Record. It is passed the
9209 * following parameters:<ul>
9210 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9212 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9214 * <br>usage:<br><pre><code>
9215 var TopicRecord = Roo.data.Record.create(
9216 {name: 'title', mapping: 'topic_title'},
9217 {name: 'author', mapping: 'username'},
9218 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9219 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9220 {name: 'lastPoster', mapping: 'user2'},
9221 {name: 'excerpt', mapping: 'post_text'}
9224 var myNewRecord = new TopicRecord({
9225 title: 'Do my job please',
9228 lastPost: new Date(),
9229 lastPoster: 'Animal',
9230 excerpt: 'No way dude!'
9232 myStore.add(myNewRecord);
9237 Roo.data.Record.create = function(o){
9239 f.superclass.constructor.apply(this, arguments);
9241 Roo.extend(f, Roo.data.Record);
9242 var p = f.prototype;
9243 p.fields = new Roo.util.MixedCollection(false, function(field){
9246 for(var i = 0, len = o.length; i < len; i++){
9247 p.fields.add(new Roo.data.Field(o[i]));
9249 f.getField = function(name){
9250 return p.fields.get(name);
9255 Roo.data.Record.AUTO_ID = 1000;
9256 Roo.data.Record.EDIT = 'edit';
9257 Roo.data.Record.REJECT = 'reject';
9258 Roo.data.Record.COMMIT = 'commit';
9260 Roo.data.Record.prototype = {
9262 * Readonly flag - true if this record has been modified.
9271 join : function(store){
9276 * Set the named field to the specified value.
9277 * @param {String} name The name of the field to set.
9278 * @param {Object} value The value to set the field to.
9280 set : function(name, value){
9281 if(this.data[name] == value){
9288 if(typeof this.modified[name] == 'undefined'){
9289 this.modified[name] = this.data[name];
9291 this.data[name] = value;
9292 if(!this.editing && this.store){
9293 this.store.afterEdit(this);
9298 * Get the value of the named field.
9299 * @param {String} name The name of the field to get the value of.
9300 * @return {Object} The value of the field.
9302 get : function(name){
9303 return this.data[name];
9307 beginEdit : function(){
9308 this.editing = true;
9313 cancelEdit : function(){
9314 this.editing = false;
9315 delete this.modified;
9319 endEdit : function(){
9320 this.editing = false;
9321 if(this.dirty && this.store){
9322 this.store.afterEdit(this);
9327 * Usually called by the {@link Roo.data.Store} which owns the Record.
9328 * Rejects all changes made to the Record since either creation, or the last commit operation.
9329 * Modified fields are reverted to their original values.
9331 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9332 * of reject operations.
9334 reject : function(){
9335 var m = this.modified;
9337 if(typeof m[n] != "function"){
9338 this.data[n] = m[n];
9342 delete this.modified;
9343 this.editing = false;
9345 this.store.afterReject(this);
9350 * Usually called by the {@link Roo.data.Store} which owns the Record.
9351 * Commits all changes made to the Record since either creation, or the last commit operation.
9353 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9354 * of commit operations.
9356 commit : function(){
9358 delete this.modified;
9359 this.editing = false;
9361 this.store.afterCommit(this);
9366 hasError : function(){
9367 return this.error != null;
9371 clearError : function(){
9376 * Creates a copy of this record.
9377 * @param {String} id (optional) A new record id if you don't want to use this record's id
9380 copy : function(newId) {
9381 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9385 * Ext JS Library 1.1.1
9386 * Copyright(c) 2006-2007, Ext JS, LLC.
9388 * Originally Released Under LGPL - original licence link has changed is not relivant.
9391 * <script type="text/javascript">
9397 * @class Roo.data.Store
9398 * @extends Roo.util.Observable
9399 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9400 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9402 * 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
9403 * has no knowledge of the format of the data returned by the Proxy.<br>
9405 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9406 * instances from the data object. These records are cached and made available through accessor functions.
9408 * Creates a new Store.
9409 * @param {Object} config A config object containing the objects needed for the Store to access data,
9410 * and read the data into Records.
9412 Roo.data.Store = function(config){
9413 this.data = new Roo.util.MixedCollection(false);
9414 this.data.getKey = function(o){
9417 this.baseParams = {};
9424 "multisort" : "_multisort"
9427 if(config && config.data){
9428 this.inlineData = config.data;
9432 Roo.apply(this, config);
9434 if(this.reader){ // reader passed
9435 this.reader = Roo.factory(this.reader, Roo.data);
9436 this.reader.xmodule = this.xmodule || false;
9437 if(!this.recordType){
9438 this.recordType = this.reader.recordType;
9440 if(this.reader.onMetaChange){
9441 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9445 if(this.recordType){
9446 this.fields = this.recordType.prototype.fields;
9452 * @event datachanged
9453 * Fires when the data cache has changed, and a widget which is using this Store
9454 * as a Record cache should refresh its view.
9455 * @param {Store} this
9460 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9461 * @param {Store} this
9462 * @param {Object} meta The JSON metadata
9467 * Fires when Records have been added to the Store
9468 * @param {Store} this
9469 * @param {Roo.data.Record[]} records The array of Records added
9470 * @param {Number} index The index at which the record(s) were added
9475 * Fires when a Record has been removed from the Store
9476 * @param {Store} this
9477 * @param {Roo.data.Record} record The Record that was removed
9478 * @param {Number} index The index at which the record was removed
9483 * Fires when a Record has been updated
9484 * @param {Store} this
9485 * @param {Roo.data.Record} record The Record that was updated
9486 * @param {String} operation The update operation being performed. Value may be one of:
9488 Roo.data.Record.EDIT
9489 Roo.data.Record.REJECT
9490 Roo.data.Record.COMMIT
9496 * Fires when the data cache has been cleared.
9497 * @param {Store} this
9502 * Fires before a request is made for a new data object. If the beforeload handler returns false
9503 * the load action will be canceled.
9504 * @param {Store} this
9505 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9509 * @event beforeloadadd
9510 * Fires after a new set of Records has been loaded.
9511 * @param {Store} this
9512 * @param {Roo.data.Record[]} records The Records that were loaded
9513 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9515 beforeloadadd : true,
9518 * Fires after a new set of Records has been loaded, before they are added to the store.
9519 * @param {Store} this
9520 * @param {Roo.data.Record[]} records The Records that were loaded
9521 * @param {Object} options The loading options that were specified (see {@link #load} for details)
9522 * @params {Object} return from reader
9526 * @event loadexception
9527 * Fires if an exception occurs in the Proxy during loading.
9528 * Called with the signature of the Proxy's "loadexception" event.
9529 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9532 * @param {Object} return from JsonData.reader() - success, totalRecords, records
9533 * @param {Object} load options
9534 * @param {Object} jsonData from your request (normally this contains the Exception)
9536 loadexception : true
9540 this.proxy = Roo.factory(this.proxy, Roo.data);
9541 this.proxy.xmodule = this.xmodule || false;
9542 this.relayEvents(this.proxy, ["loadexception"]);
9544 this.sortToggle = {};
9545 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9547 Roo.data.Store.superclass.constructor.call(this);
9549 if(this.inlineData){
9550 this.loadData(this.inlineData);
9551 delete this.inlineData;
9555 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9557 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
9558 * without a remote query - used by combo/forms at present.
9562 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9565 * @cfg {Array} data Inline data to be loaded when the store is initialized.
9568 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9569 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9572 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9573 * on any HTTP request
9576 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9579 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9583 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9584 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9589 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9590 * loaded or when a record is removed. (defaults to false).
9592 pruneModifiedRecords : false,
9598 * Add Records to the Store and fires the add event.
9599 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9601 add : function(records){
9602 records = [].concat(records);
9603 for(var i = 0, len = records.length; i < len; i++){
9604 records[i].join(this);
9606 var index = this.data.length;
9607 this.data.addAll(records);
9608 this.fireEvent("add", this, records, index);
9612 * Remove a Record from the Store and fires the remove event.
9613 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9615 remove : function(record){
9616 var index = this.data.indexOf(record);
9617 this.data.removeAt(index);
9618 if(this.pruneModifiedRecords){
9619 this.modified.remove(record);
9621 this.fireEvent("remove", this, record, index);
9625 * Remove all Records from the Store and fires the clear event.
9627 removeAll : function(){
9629 if(this.pruneModifiedRecords){
9632 this.fireEvent("clear", this);
9636 * Inserts Records to the Store at the given index and fires the add event.
9637 * @param {Number} index The start index at which to insert the passed Records.
9638 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9640 insert : function(index, records){
9641 records = [].concat(records);
9642 for(var i = 0, len = records.length; i < len; i++){
9643 this.data.insert(index, records[i]);
9644 records[i].join(this);
9646 this.fireEvent("add", this, records, index);
9650 * Get the index within the cache of the passed Record.
9651 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9652 * @return {Number} The index of the passed Record. Returns -1 if not found.
9654 indexOf : function(record){
9655 return this.data.indexOf(record);
9659 * Get the index within the cache of the Record with the passed id.
9660 * @param {String} id The id of the Record to find.
9661 * @return {Number} The index of the Record. Returns -1 if not found.
9663 indexOfId : function(id){
9664 return this.data.indexOfKey(id);
9668 * Get the Record with the specified id.
9669 * @param {String} id The id of the Record to find.
9670 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9672 getById : function(id){
9673 return this.data.key(id);
9677 * Get the Record at the specified index.
9678 * @param {Number} index The index of the Record to find.
9679 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9681 getAt : function(index){
9682 return this.data.itemAt(index);
9686 * Returns a range of Records between specified indices.
9687 * @param {Number} startIndex (optional) The starting index (defaults to 0)
9688 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9689 * @return {Roo.data.Record[]} An array of Records
9691 getRange : function(start, end){
9692 return this.data.getRange(start, end);
9696 storeOptions : function(o){
9697 o = Roo.apply({}, o);
9700 this.lastOptions = o;
9704 * Loads the Record cache from the configured Proxy using the configured Reader.
9706 * If using remote paging, then the first load call must specify the <em>start</em>
9707 * and <em>limit</em> properties in the options.params property to establish the initial
9708 * position within the dataset, and the number of Records to cache on each read from the Proxy.
9710 * <strong>It is important to note that for remote data sources, loading is asynchronous,
9711 * and this call will return before the new data has been loaded. Perform any post-processing
9712 * in a callback function, or in a "load" event handler.</strong>
9714 * @param {Object} options An object containing properties which control loading options:<ul>
9715 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9716 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9717 * passed the following arguments:<ul>
9718 * <li>r : Roo.data.Record[]</li>
9719 * <li>options: Options object from the load call</li>
9720 * <li>success: Boolean success indicator</li></ul></li>
9721 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9722 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9725 load : function(options){
9726 options = options || {};
9727 if(this.fireEvent("beforeload", this, options) !== false){
9728 this.storeOptions(options);
9729 var p = Roo.apply(options.params || {}, this.baseParams);
9730 // if meta was not loaded from remote source.. try requesting it.
9731 if (!this.reader.metaFromRemote) {
9734 if(this.sortInfo && this.remoteSort){
9735 var pn = this.paramNames;
9736 p[pn["sort"]] = this.sortInfo.field;
9737 p[pn["dir"]] = this.sortInfo.direction;
9739 if (this.multiSort) {
9740 var pn = this.paramNames;
9741 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9744 this.proxy.load(p, this.reader, this.loadRecords, this, options);
9749 * Reloads the Record cache from the configured Proxy using the configured Reader and
9750 * the options from the last load operation performed.
9751 * @param {Object} options (optional) An object containing properties which may override the options
9752 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9753 * the most recently used options are reused).
9755 reload : function(options){
9756 this.load(Roo.applyIf(options||{}, this.lastOptions));
9760 // Called as a callback by the Reader during a load operation.
9761 loadRecords : function(o, options, success){
9762 if(!o || success === false){
9763 if(success !== false){
9764 this.fireEvent("load", this, [], options, o);
9766 if(options.callback){
9767 options.callback.call(options.scope || this, [], options, false);
9771 // if data returned failure - throw an exception.
9772 if (o.success === false) {
9773 // show a message if no listener is registered.
9774 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9775 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9777 // loadmask wil be hooked into this..
9778 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9781 var r = o.records, t = o.totalRecords || r.length;
9783 this.fireEvent("beforeloadadd", this, r, options, o);
9785 if(!options || options.add !== true){
9786 if(this.pruneModifiedRecords){
9789 for(var i = 0, len = r.length; i < len; i++){
9793 this.data = this.snapshot;
9794 delete this.snapshot;
9797 this.data.addAll(r);
9798 this.totalLength = t;
9800 this.fireEvent("datachanged", this);
9802 this.totalLength = Math.max(t, this.data.length+r.length);
9805 this.fireEvent("load", this, r, options, o);
9806 if(options.callback){
9807 options.callback.call(options.scope || this, r, options, true);
9813 * Loads data from a passed data block. A Reader which understands the format of the data
9814 * must have been configured in the constructor.
9815 * @param {Object} data The data block from which to read the Records. The format of the data expected
9816 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9817 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9819 loadData : function(o, append){
9820 var r = this.reader.readRecords(o);
9821 this.loadRecords(r, {add: append}, true);
9825 * Gets the number of cached records.
9827 * <em>If using paging, this may not be the total size of the dataset. If the data object
9828 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9829 * the data set size</em>
9831 getCount : function(){
9832 return this.data.length || 0;
9836 * Gets the total number of records in the dataset as returned by the server.
9838 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9839 * the dataset size</em>
9841 getTotalCount : function(){
9842 return this.totalLength || 0;
9846 * Returns the sort state of the Store as an object with two properties:
9848 field {String} The name of the field by which the Records are sorted
9849 direction {String} The sort order, "ASC" or "DESC"
9852 getSortState : function(){
9853 return this.sortInfo;
9857 applySort : function(){
9858 if(this.sortInfo && !this.remoteSort){
9859 var s = this.sortInfo, f = s.field;
9860 var st = this.fields.get(f).sortType;
9861 var fn = function(r1, r2){
9862 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9863 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9865 this.data.sort(s.direction, fn);
9866 if(this.snapshot && this.snapshot != this.data){
9867 this.snapshot.sort(s.direction, fn);
9873 * Sets the default sort column and order to be used by the next load operation.
9874 * @param {String} fieldName The name of the field to sort by.
9875 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9877 setDefaultSort : function(field, dir){
9878 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9883 * If remote sorting is used, the sort is performed on the server, and the cache is
9884 * reloaded. If local sorting is used, the cache is sorted internally.
9885 * @param {String} fieldName The name of the field to sort by.
9886 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9888 sort : function(fieldName, dir){
9889 var f = this.fields.get(fieldName);
9891 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9893 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9894 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9899 this.sortToggle[f.name] = dir;
9900 this.sortInfo = {field: f.name, direction: dir};
9901 if(!this.remoteSort){
9903 this.fireEvent("datachanged", this);
9905 this.load(this.lastOptions);
9910 * Calls the specified function for each of the Records in the cache.
9911 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9912 * Returning <em>false</em> aborts and exits the iteration.
9913 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9915 each : function(fn, scope){
9916 this.data.each(fn, scope);
9920 * Gets all records modified since the last commit. Modified records are persisted across load operations
9921 * (e.g., during paging).
9922 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9924 getModifiedRecords : function(){
9925 return this.modified;
9929 createFilterFn : function(property, value, anyMatch){
9930 if(!value.exec){ // not a regex
9931 value = String(value);
9932 if(value.length == 0){
9935 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9938 return value.test(r.data[property]);
9943 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9944 * @param {String} property A field on your records
9945 * @param {Number} start The record index to start at (defaults to 0)
9946 * @param {Number} end The last record index to include (defaults to length - 1)
9947 * @return {Number} The sum
9949 sum : function(property, start, end){
9950 var rs = this.data.items, v = 0;
9952 end = (end || end === 0) ? end : rs.length-1;
9954 for(var i = start; i <= end; i++){
9955 v += (rs[i].data[property] || 0);
9961 * Filter the records by a specified property.
9962 * @param {String} field A field on your records
9963 * @param {String/RegExp} value Either a string that the field
9964 * should start with or a RegExp to test against the field
9965 * @param {Boolean} anyMatch True to match any part not just the beginning
9967 filter : function(property, value, anyMatch){
9968 var fn = this.createFilterFn(property, value, anyMatch);
9969 return fn ? this.filterBy(fn) : this.clearFilter();
9973 * Filter by a function. The specified function will be called with each
9974 * record in this data source. If the function returns true the record is included,
9975 * otherwise it is filtered.
9976 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9977 * @param {Object} scope (optional) The scope of the function (defaults to this)
9979 filterBy : function(fn, scope){
9980 this.snapshot = this.snapshot || this.data;
9981 this.data = this.queryBy(fn, scope||this);
9982 this.fireEvent("datachanged", this);
9986 * Query the records by a specified property.
9987 * @param {String} field A field on your records
9988 * @param {String/RegExp} value Either a string that the field
9989 * should start with or a RegExp to test against the field
9990 * @param {Boolean} anyMatch True to match any part not just the beginning
9991 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9993 query : function(property, value, anyMatch){
9994 var fn = this.createFilterFn(property, value, anyMatch);
9995 return fn ? this.queryBy(fn) : this.data.clone();
9999 * Query by a function. The specified function will be called with each
10000 * record in this data source. If the function returns true the record is included
10002 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10003 * @param {Object} scope (optional) The scope of the function (defaults to this)
10004 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10006 queryBy : function(fn, scope){
10007 var data = this.snapshot || this.data;
10008 return data.filterBy(fn, scope||this);
10012 * Collects unique values for a particular dataIndex from this store.
10013 * @param {String} dataIndex The property to collect
10014 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10015 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10016 * @return {Array} An array of the unique values
10018 collect : function(dataIndex, allowNull, bypassFilter){
10019 var d = (bypassFilter === true && this.snapshot) ?
10020 this.snapshot.items : this.data.items;
10021 var v, sv, r = [], l = {};
10022 for(var i = 0, len = d.length; i < len; i++){
10023 v = d[i].data[dataIndex];
10025 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10034 * Revert to a view of the Record cache with no filtering applied.
10035 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10037 clearFilter : function(suppressEvent){
10038 if(this.snapshot && this.snapshot != this.data){
10039 this.data = this.snapshot;
10040 delete this.snapshot;
10041 if(suppressEvent !== true){
10042 this.fireEvent("datachanged", this);
10048 afterEdit : function(record){
10049 if(this.modified.indexOf(record) == -1){
10050 this.modified.push(record);
10052 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10056 afterReject : function(record){
10057 this.modified.remove(record);
10058 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10062 afterCommit : function(record){
10063 this.modified.remove(record);
10064 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10068 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10069 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10071 commitChanges : function(){
10072 var m = this.modified.slice(0);
10073 this.modified = [];
10074 for(var i = 0, len = m.length; i < len; i++){
10080 * Cancel outstanding changes on all changed records.
10082 rejectChanges : function(){
10083 var m = this.modified.slice(0);
10084 this.modified = [];
10085 for(var i = 0, len = m.length; i < len; i++){
10090 onMetaChange : function(meta, rtype, o){
10091 this.recordType = rtype;
10092 this.fields = rtype.prototype.fields;
10093 delete this.snapshot;
10094 this.sortInfo = meta.sortInfo || this.sortInfo;
10095 this.modified = [];
10096 this.fireEvent('metachange', this, this.reader.meta);
10099 moveIndex : function(data, type)
10101 var index = this.indexOf(data);
10103 var newIndex = index + type;
10107 this.insert(newIndex, data);
10112 * Ext JS Library 1.1.1
10113 * Copyright(c) 2006-2007, Ext JS, LLC.
10115 * Originally Released Under LGPL - original licence link has changed is not relivant.
10118 * <script type="text/javascript">
10122 * @class Roo.data.SimpleStore
10123 * @extends Roo.data.Store
10124 * Small helper class to make creating Stores from Array data easier.
10125 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10126 * @cfg {Array} fields An array of field definition objects, or field name strings.
10127 * @cfg {Array} data The multi-dimensional array of data
10129 * @param {Object} config
10131 Roo.data.SimpleStore = function(config){
10132 Roo.data.SimpleStore.superclass.constructor.call(this, {
10134 reader: new Roo.data.ArrayReader({
10137 Roo.data.Record.create(config.fields)
10139 proxy : new Roo.data.MemoryProxy(config.data)
10143 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10145 * Ext JS Library 1.1.1
10146 * Copyright(c) 2006-2007, Ext JS, LLC.
10148 * Originally Released Under LGPL - original licence link has changed is not relivant.
10151 * <script type="text/javascript">
10156 * @extends Roo.data.Store
10157 * @class Roo.data.JsonStore
10158 * Small helper class to make creating Stores for JSON data easier. <br/>
10160 var store = new Roo.data.JsonStore({
10161 url: 'get-images.php',
10163 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10166 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10167 * JsonReader and HttpProxy (unless inline data is provided).</b>
10168 * @cfg {Array} fields An array of field definition objects, or field name strings.
10170 * @param {Object} config
10172 Roo.data.JsonStore = function(c){
10173 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10174 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10175 reader: new Roo.data.JsonReader(c, c.fields)
10178 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10180 * Ext JS Library 1.1.1
10181 * Copyright(c) 2006-2007, Ext JS, LLC.
10183 * Originally Released Under LGPL - original licence link has changed is not relivant.
10186 * <script type="text/javascript">
10190 Roo.data.Field = function(config){
10191 if(typeof config == "string"){
10192 config = {name: config};
10194 Roo.apply(this, config);
10197 this.type = "auto";
10200 var st = Roo.data.SortTypes;
10201 // named sortTypes are supported, here we look them up
10202 if(typeof this.sortType == "string"){
10203 this.sortType = st[this.sortType];
10206 // set default sortType for strings and dates
10207 if(!this.sortType){
10210 this.sortType = st.asUCString;
10213 this.sortType = st.asDate;
10216 this.sortType = st.none;
10221 var stripRe = /[\$,%]/g;
10223 // prebuilt conversion function for this field, instead of
10224 // switching every time we're reading a value
10226 var cv, dateFormat = this.dateFormat;
10231 cv = function(v){ return v; };
10234 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10238 return v !== undefined && v !== null && v !== '' ?
10239 parseInt(String(v).replace(stripRe, ""), 10) : '';
10244 return v !== undefined && v !== null && v !== '' ?
10245 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10250 cv = function(v){ return v === true || v === "true" || v == 1; };
10257 if(v instanceof Date){
10261 if(dateFormat == "timestamp"){
10262 return new Date(v*1000);
10264 return Date.parseDate(v, dateFormat);
10266 var parsed = Date.parse(v);
10267 return parsed ? new Date(parsed) : null;
10276 Roo.data.Field.prototype = {
10284 * Ext JS Library 1.1.1
10285 * Copyright(c) 2006-2007, Ext JS, LLC.
10287 * Originally Released Under LGPL - original licence link has changed is not relivant.
10290 * <script type="text/javascript">
10293 // Base class for reading structured data from a data source. This class is intended to be
10294 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10297 * @class Roo.data.DataReader
10298 * Base class for reading structured data from a data source. This class is intended to be
10299 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10302 Roo.data.DataReader = function(meta, recordType){
10306 this.recordType = recordType instanceof Array ?
10307 Roo.data.Record.create(recordType) : recordType;
10310 Roo.data.DataReader.prototype = {
10312 * Create an empty record
10313 * @param {Object} data (optional) - overlay some values
10314 * @return {Roo.data.Record} record created.
10316 newRow : function(d) {
10318 this.recordType.prototype.fields.each(function(c) {
10320 case 'int' : da[c.name] = 0; break;
10321 case 'date' : da[c.name] = new Date(); break;
10322 case 'float' : da[c.name] = 0.0; break;
10323 case 'boolean' : da[c.name] = false; break;
10324 default : da[c.name] = ""; break;
10328 return new this.recordType(Roo.apply(da, d));
10333 * Ext JS Library 1.1.1
10334 * Copyright(c) 2006-2007, Ext JS, LLC.
10336 * Originally Released Under LGPL - original licence link has changed is not relivant.
10339 * <script type="text/javascript">
10343 * @class Roo.data.DataProxy
10344 * @extends Roo.data.Observable
10345 * This class is an abstract base class for implementations which provide retrieval of
10346 * unformatted data objects.<br>
10348 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10349 * (of the appropriate type which knows how to parse the data object) to provide a block of
10350 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10352 * Custom implementations must implement the load method as described in
10353 * {@link Roo.data.HttpProxy#load}.
10355 Roo.data.DataProxy = function(){
10358 * @event beforeload
10359 * Fires before a network request is made to retrieve a data object.
10360 * @param {Object} This DataProxy object.
10361 * @param {Object} params The params parameter to the load function.
10366 * Fires before the load method's callback is called.
10367 * @param {Object} This DataProxy object.
10368 * @param {Object} o The data object.
10369 * @param {Object} arg The callback argument object passed to the load function.
10373 * @event loadexception
10374 * Fires if an Exception occurs during data retrieval.
10375 * @param {Object} This DataProxy object.
10376 * @param {Object} o The data object.
10377 * @param {Object} arg The callback argument object passed to the load function.
10378 * @param {Object} e The Exception.
10380 loadexception : true
10382 Roo.data.DataProxy.superclass.constructor.call(this);
10385 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10388 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10392 * Ext JS Library 1.1.1
10393 * Copyright(c) 2006-2007, Ext JS, LLC.
10395 * Originally Released Under LGPL - original licence link has changed is not relivant.
10398 * <script type="text/javascript">
10401 * @class Roo.data.MemoryProxy
10402 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10403 * to the Reader when its load method is called.
10405 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10407 Roo.data.MemoryProxy = function(data){
10411 Roo.data.MemoryProxy.superclass.constructor.call(this);
10415 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10417 * Load data from the requested source (in this case an in-memory
10418 * data object passed to the constructor), read the data object into
10419 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10420 * process that block using the passed callback.
10421 * @param {Object} params This parameter is not used by the MemoryProxy class.
10422 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10423 * object into a block of Roo.data.Records.
10424 * @param {Function} callback The function into which to pass the block of Roo.data.records.
10425 * The function must be passed <ul>
10426 * <li>The Record block object</li>
10427 * <li>The "arg" argument from the load function</li>
10428 * <li>A boolean success indicator</li>
10430 * @param {Object} scope The scope in which to call the callback
10431 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10433 load : function(params, reader, callback, scope, arg){
10434 params = params || {};
10437 result = reader.readRecords(this.data);
10439 this.fireEvent("loadexception", this, arg, null, e);
10440 callback.call(scope, null, arg, false);
10443 callback.call(scope, result, arg, true);
10447 update : function(params, records){
10452 * Ext JS Library 1.1.1
10453 * Copyright(c) 2006-2007, Ext JS, LLC.
10455 * Originally Released Under LGPL - original licence link has changed is not relivant.
10458 * <script type="text/javascript">
10461 * @class Roo.data.HttpProxy
10462 * @extends Roo.data.DataProxy
10463 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10464 * configured to reference a certain URL.<br><br>
10466 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10467 * from which the running page was served.<br><br>
10469 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10471 * Be aware that to enable the browser to parse an XML document, the server must set
10472 * the Content-Type header in the HTTP response to "text/xml".
10474 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10475 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
10476 * will be used to make the request.
10478 Roo.data.HttpProxy = function(conn){
10479 Roo.data.HttpProxy.superclass.constructor.call(this);
10480 // is conn a conn config or a real conn?
10482 this.useAjax = !conn || !conn.events;
10486 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10487 // thse are take from connection...
10490 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10493 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10494 * extra parameters to each request made by this object. (defaults to undefined)
10497 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10498 * to each request made by this object. (defaults to undefined)
10501 * @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)
10504 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10507 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10513 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10517 * Return the {@link Roo.data.Connection} object being used by this Proxy.
10518 * @return {Connection} The Connection object. This object may be used to subscribe to events on
10519 * a finer-grained basis than the DataProxy events.
10521 getConnection : function(){
10522 return this.useAjax ? Roo.Ajax : this.conn;
10526 * Load data from the configured {@link Roo.data.Connection}, read the data object into
10527 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10528 * process that block using the passed callback.
10529 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10530 * for the request to the remote server.
10531 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10532 * object into a block of Roo.data.Records.
10533 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10534 * The function must be passed <ul>
10535 * <li>The Record block object</li>
10536 * <li>The "arg" argument from the load function</li>
10537 * <li>A boolean success indicator</li>
10539 * @param {Object} scope The scope in which to call the callback
10540 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10542 load : function(params, reader, callback, scope, arg){
10543 if(this.fireEvent("beforeload", this, params) !== false){
10545 params : params || {},
10547 callback : callback,
10552 callback : this.loadResponse,
10556 Roo.applyIf(o, this.conn);
10557 if(this.activeRequest){
10558 Roo.Ajax.abort(this.activeRequest);
10560 this.activeRequest = Roo.Ajax.request(o);
10562 this.conn.request(o);
10565 callback.call(scope||this, null, arg, false);
10570 loadResponse : function(o, success, response){
10571 delete this.activeRequest;
10573 this.fireEvent("loadexception", this, o, response);
10574 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10579 result = o.reader.read(response);
10581 this.fireEvent("loadexception", this, o, response, e);
10582 o.request.callback.call(o.request.scope, null, o.request.arg, false);
10586 this.fireEvent("load", this, o, o.request.arg);
10587 o.request.callback.call(o.request.scope, result, o.request.arg, true);
10591 update : function(dataSet){
10596 updateResponse : function(dataSet){
10601 * Ext JS Library 1.1.1
10602 * Copyright(c) 2006-2007, Ext JS, LLC.
10604 * Originally Released Under LGPL - original licence link has changed is not relivant.
10607 * <script type="text/javascript">
10611 * @class Roo.data.ScriptTagProxy
10612 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10613 * other than the originating domain of the running page.<br><br>
10615 * <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
10616 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10618 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10619 * source code that is used as the source inside a <script> tag.<br><br>
10621 * In order for the browser to process the returned data, the server must wrap the data object
10622 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10623 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10624 * depending on whether the callback name was passed:
10627 boolean scriptTag = false;
10628 String cb = request.getParameter("callback");
10631 response.setContentType("text/javascript");
10633 response.setContentType("application/x-json");
10635 Writer out = response.getWriter();
10637 out.write(cb + "(");
10639 out.print(dataBlock.toJsonString());
10646 * @param {Object} config A configuration object.
10648 Roo.data.ScriptTagProxy = function(config){
10649 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10650 Roo.apply(this, config);
10651 this.head = document.getElementsByTagName("head")[0];
10654 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10656 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10658 * @cfg {String} url The URL from which to request the data object.
10661 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10665 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10666 * the server the name of the callback function set up by the load call to process the returned data object.
10667 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10668 * javascript output which calls this named function passing the data object as its only parameter.
10670 callbackParam : "callback",
10672 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10673 * name to the request.
10678 * Load data from the configured URL, read the data object into
10679 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10680 * process that block using the passed callback.
10681 * @param {Object} params An object containing properties which are to be used as HTTP parameters
10682 * for the request to the remote server.
10683 * @param {Roo.data.DataReader} reader The Reader object which converts the data
10684 * object into a block of Roo.data.Records.
10685 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10686 * The function must be passed <ul>
10687 * <li>The Record block object</li>
10688 * <li>The "arg" argument from the load function</li>
10689 * <li>A boolean success indicator</li>
10691 * @param {Object} scope The scope in which to call the callback
10692 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10694 load : function(params, reader, callback, scope, arg){
10695 if(this.fireEvent("beforeload", this, params) !== false){
10697 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10699 var url = this.url;
10700 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10702 url += "&_dc=" + (new Date().getTime());
10704 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10707 cb : "stcCallback"+transId,
10708 scriptId : "stcScript"+transId,
10712 callback : callback,
10718 window[trans.cb] = function(o){
10719 conn.handleResponse(o, trans);
10722 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10724 if(this.autoAbort !== false){
10728 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10730 var script = document.createElement("script");
10731 script.setAttribute("src", url);
10732 script.setAttribute("type", "text/javascript");
10733 script.setAttribute("id", trans.scriptId);
10734 this.head.appendChild(script);
10736 this.trans = trans;
10738 callback.call(scope||this, null, arg, false);
10743 isLoading : function(){
10744 return this.trans ? true : false;
10748 * Abort the current server request.
10750 abort : function(){
10751 if(this.isLoading()){
10752 this.destroyTrans(this.trans);
10757 destroyTrans : function(trans, isLoaded){
10758 this.head.removeChild(document.getElementById(trans.scriptId));
10759 clearTimeout(trans.timeoutId);
10761 window[trans.cb] = undefined;
10763 delete window[trans.cb];
10766 // if hasn't been loaded, wait for load to remove it to prevent script error
10767 window[trans.cb] = function(){
10768 window[trans.cb] = undefined;
10770 delete window[trans.cb];
10777 handleResponse : function(o, trans){
10778 this.trans = false;
10779 this.destroyTrans(trans, true);
10782 result = trans.reader.readRecords(o);
10784 this.fireEvent("loadexception", this, o, trans.arg, e);
10785 trans.callback.call(trans.scope||window, null, trans.arg, false);
10788 this.fireEvent("load", this, o, trans.arg);
10789 trans.callback.call(trans.scope||window, result, trans.arg, true);
10793 handleFailure : function(trans){
10794 this.trans = false;
10795 this.destroyTrans(trans, false);
10796 this.fireEvent("loadexception", this, null, trans.arg);
10797 trans.callback.call(trans.scope||window, null, trans.arg, false);
10801 * Ext JS Library 1.1.1
10802 * Copyright(c) 2006-2007, Ext JS, LLC.
10804 * Originally Released Under LGPL - original licence link has changed is not relivant.
10807 * <script type="text/javascript">
10811 * @class Roo.data.JsonReader
10812 * @extends Roo.data.DataReader
10813 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10814 * based on mappings in a provided Roo.data.Record constructor.
10816 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10817 * in the reply previously.
10822 var RecordDef = Roo.data.Record.create([
10823 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10824 {name: 'occupation'} // This field will use "occupation" as the mapping.
10826 var myReader = new Roo.data.JsonReader({
10827 totalProperty: "results", // The property which contains the total dataset size (optional)
10828 root: "rows", // The property which contains an Array of row objects
10829 id: "id" // The property within each row object that provides an ID for the record (optional)
10833 * This would consume a JSON file like this:
10835 { 'results': 2, 'rows': [
10836 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10837 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10840 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10841 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10842 * paged from the remote server.
10843 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10844 * @cfg {String} root name of the property which contains the Array of row objects.
10845 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10846 * @cfg {Array} fields Array of field definition objects
10848 * Create a new JsonReader
10849 * @param {Object} meta Metadata configuration options
10850 * @param {Object} recordType Either an Array of field definition objects,
10851 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10853 Roo.data.JsonReader = function(meta, recordType){
10856 // set some defaults:
10857 Roo.applyIf(meta, {
10858 totalProperty: 'total',
10859 successProperty : 'success',
10864 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10866 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10869 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10870 * Used by Store query builder to append _requestMeta to params.
10873 metaFromRemote : false,
10875 * This method is only used by a DataProxy which has retrieved data from a remote server.
10876 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10877 * @return {Object} data A data block which is used by an Roo.data.Store object as
10878 * a cache of Roo.data.Records.
10880 read : function(response){
10881 var json = response.responseText;
10883 var o = /* eval:var:o */ eval("("+json+")");
10885 throw {message: "JsonReader.read: Json object not found"};
10891 this.metaFromRemote = true;
10892 this.meta = o.metaData;
10893 this.recordType = Roo.data.Record.create(o.metaData.fields);
10894 this.onMetaChange(this.meta, this.recordType, o);
10896 return this.readRecords(o);
10899 // private function a store will implement
10900 onMetaChange : function(meta, recordType, o){
10907 simpleAccess: function(obj, subsc) {
10914 getJsonAccessor: function(){
10916 return function(expr) {
10918 return(re.test(expr))
10919 ? new Function("obj", "return obj." + expr)
10924 return Roo.emptyFn;
10929 * Create a data block containing Roo.data.Records from an XML document.
10930 * @param {Object} o An object which contains an Array of row objects in the property specified
10931 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10932 * which contains the total size of the dataset.
10933 * @return {Object} data A data block which is used by an Roo.data.Store object as
10934 * a cache of Roo.data.Records.
10936 readRecords : function(o){
10938 * After any data loads, the raw JSON data is available for further custom processing.
10942 var s = this.meta, Record = this.recordType,
10943 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10945 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10947 if(s.totalProperty) {
10948 this.getTotal = this.getJsonAccessor(s.totalProperty);
10950 if(s.successProperty) {
10951 this.getSuccess = this.getJsonAccessor(s.successProperty);
10953 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10955 var g = this.getJsonAccessor(s.id);
10956 this.getId = function(rec) {
10958 return (r === undefined || r === "") ? null : r;
10961 this.getId = function(){return null;};
10964 for(var jj = 0; jj < fl; jj++){
10966 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10967 this.ef[jj] = this.getJsonAccessor(map);
10971 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10972 if(s.totalProperty){
10973 var vt = parseInt(this.getTotal(o), 10);
10978 if(s.successProperty){
10979 var vs = this.getSuccess(o);
10980 if(vs === false || vs === 'false'){
10985 for(var i = 0; i < c; i++){
10988 var id = this.getId(n);
10989 for(var j = 0; j < fl; j++){
10991 var v = this.ef[j](n);
10993 Roo.log('missing convert for ' + f.name);
10997 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10999 var record = new Record(values, id);
11001 records[i] = record;
11007 totalRecords : totalRecords
11012 * Ext JS Library 1.1.1
11013 * Copyright(c) 2006-2007, Ext JS, LLC.
11015 * Originally Released Under LGPL - original licence link has changed is not relivant.
11018 * <script type="text/javascript">
11022 * @class Roo.data.ArrayReader
11023 * @extends Roo.data.DataReader
11024 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11025 * Each element of that Array represents a row of data fields. The
11026 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11027 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11031 var RecordDef = Roo.data.Record.create([
11032 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11033 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11035 var myReader = new Roo.data.ArrayReader({
11036 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11040 * This would consume an Array like this:
11042 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11044 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11046 * Create a new JsonReader
11047 * @param {Object} meta Metadata configuration options.
11048 * @param {Object} recordType Either an Array of field definition objects
11049 * as specified to {@link Roo.data.Record#create},
11050 * or an {@link Roo.data.Record} object
11051 * created using {@link Roo.data.Record#create}.
11053 Roo.data.ArrayReader = function(meta, recordType){
11054 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11057 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11059 * Create a data block containing Roo.data.Records from an XML document.
11060 * @param {Object} o An Array of row objects which represents the dataset.
11061 * @return {Object} data A data block which is used by an Roo.data.Store object as
11062 * a cache of Roo.data.Records.
11064 readRecords : function(o){
11065 var sid = this.meta ? this.meta.id : null;
11066 var recordType = this.recordType, fields = recordType.prototype.fields;
11069 for(var i = 0; i < root.length; i++){
11072 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11073 for(var j = 0, jlen = fields.length; j < jlen; j++){
11074 var f = fields.items[j];
11075 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11076 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11078 values[f.name] = v;
11080 var record = new recordType(values, id);
11082 records[records.length] = record;
11086 totalRecords : records.length
11095 * @class Roo.bootstrap.ComboBox
11096 * @extends Roo.bootstrap.TriggerField
11097 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11098 * @cfg {Boolean} append (true|false) default false
11099 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11100 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11101 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11102 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11103 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11104 * @cfg {Boolean} animate default true
11105 * @cfg {Boolean} emptyResultText only for touch device
11107 * Create a new ComboBox.
11108 * @param {Object} config Configuration options
11110 Roo.bootstrap.ComboBox = function(config){
11111 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11115 * Fires when the dropdown list is expanded
11116 * @param {Roo.bootstrap.ComboBox} combo This combo box
11121 * Fires when the dropdown list is collapsed
11122 * @param {Roo.bootstrap.ComboBox} combo This combo box
11126 * @event beforeselect
11127 * Fires before a list item is selected. Return false to cancel the selection.
11128 * @param {Roo.bootstrap.ComboBox} combo This combo box
11129 * @param {Roo.data.Record} record The data record returned from the underlying store
11130 * @param {Number} index The index of the selected item in the dropdown list
11132 'beforeselect' : true,
11135 * Fires when a list item is selected
11136 * @param {Roo.bootstrap.ComboBox} combo This combo box
11137 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11138 * @param {Number} index The index of the selected item in the dropdown list
11142 * @event beforequery
11143 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11144 * The event object passed has these properties:
11145 * @param {Roo.bootstrap.ComboBox} combo This combo box
11146 * @param {String} query The query
11147 * @param {Boolean} forceAll true to force "all" query
11148 * @param {Boolean} cancel true to cancel the query
11149 * @param {Object} e The query event object
11151 'beforequery': true,
11154 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11155 * @param {Roo.bootstrap.ComboBox} combo This combo box
11160 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11161 * @param {Roo.bootstrap.ComboBox} combo This combo box
11162 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11167 * Fires when the remove value from the combobox array
11168 * @param {Roo.bootstrap.ComboBox} combo This combo box
11172 * @event specialfilter
11173 * Fires when specialfilter
11174 * @param {Roo.bootstrap.ComboBox} combo This combo box
11176 'specialfilter' : true
11181 this.tickItems = [];
11183 this.selectedIndex = -1;
11184 if(this.mode == 'local'){
11185 if(config.queryDelay === undefined){
11186 this.queryDelay = 10;
11188 if(config.minChars === undefined){
11194 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11197 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11198 * rendering into an Roo.Editor, defaults to false)
11201 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11202 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11205 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11208 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11209 * the dropdown list (defaults to undefined, with no header element)
11213 * @cfg {String/Roo.Template} tpl The template to use to render the output
11217 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11219 listWidth: undefined,
11221 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11222 * mode = 'remote' or 'text' if mode = 'local')
11224 displayField: undefined,
11227 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11228 * mode = 'remote' or 'value' if mode = 'local').
11229 * Note: use of a valueField requires the user make a selection
11230 * in order for a value to be mapped.
11232 valueField: undefined,
11236 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11237 * field's data value (defaults to the underlying DOM element's name)
11239 hiddenName: undefined,
11241 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11245 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11247 selectedClass: 'active',
11250 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11254 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11255 * anchor positions (defaults to 'tl-bl')
11257 listAlign: 'tl-bl?',
11259 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11263 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11264 * query specified by the allQuery config option (defaults to 'query')
11266 triggerAction: 'query',
11268 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11269 * (defaults to 4, does not apply if editable = false)
11273 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11274 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11278 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11279 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11283 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11284 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11288 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11289 * when editable = true (defaults to false)
11291 selectOnFocus:false,
11293 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11295 queryParam: 'query',
11297 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11298 * when mode = 'remote' (defaults to 'Loading...')
11300 loadingText: 'Loading...',
11302 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11306 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11310 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11311 * traditional select (defaults to true)
11315 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11319 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11323 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11324 * listWidth has a higher value)
11328 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11329 * allow the user to set arbitrary text into the field (defaults to false)
11331 forceSelection:false,
11333 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11334 * if typeAhead = true (defaults to 250)
11336 typeAheadDelay : 250,
11338 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11339 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11341 valueNotFoundText : undefined,
11343 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11345 blockFocus : false,
11348 * @cfg {Boolean} disableClear Disable showing of clear button.
11350 disableClear : false,
11352 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11354 alwaysQuery : false,
11357 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
11362 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11364 invalidClass : "has-warning",
11367 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11369 validClass : "has-success",
11372 * @cfg {Boolean} specialFilter (true|false) special filter default false
11374 specialFilter : false,
11377 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11379 mobileTouchView : true,
11391 btnPosition : 'right',
11392 triggerList : true,
11393 showToggleBtn : true,
11395 emptyResultText: 'Empty',
11396 // element that contains real text value.. (when hidden is used..)
11398 getAutoCreate : function()
11406 if(Roo.isTouch && this.mobileTouchView){
11407 cfg = this.getAutoCreateTouchView();
11414 if(!this.tickable){
11415 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11420 * ComboBox with tickable selections
11423 var align = this.labelAlign || this.parentLabelAlign();
11426 cls : 'form-group roo-combobox-tickable' //input-group
11431 cls : 'tickable-buttons',
11436 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11443 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11450 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11457 buttons.cn.unshift({
11459 cls: 'select2-search-field-input'
11465 Roo.each(buttons.cn, function(c){
11467 c.cls += ' btn-' + _this.size;
11470 if (_this.disabled) {
11481 cls: 'form-hidden-field'
11485 cls: 'select2-choices',
11489 cls: 'select2-search-field',
11501 cls: 'select2-container input-group select2-container-multi',
11506 // cls: 'typeahead typeahead-long dropdown-menu',
11507 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
11512 if(this.hasFeedback && !this.allowBlank){
11516 cls: 'glyphicon form-control-feedback'
11519 combobox.cn.push(feedback);
11522 if (align ==='left' && this.fieldLabel.length) {
11524 Roo.log("left and has label");
11530 cls : 'control-label col-sm-' + this.labelWidth,
11531 html : this.fieldLabel
11535 cls : "col-sm-" + (12 - this.labelWidth),
11542 } else if ( this.fieldLabel.length) {
11548 //cls : 'input-group-addon',
11549 html : this.fieldLabel
11559 Roo.log(" no label && no align");
11566 ['xs','sm','md','lg'].map(function(size){
11567 if (settings[size]) {
11568 cfg.cls += ' col-' + size + '-' + settings[size];
11577 initEvents: function()
11581 throw "can not find store for combo";
11584 this.store = Roo.factory(this.store, Roo.data);
11590 if(Roo.isTouch && this.mobileTouchView){
11591 this.initTouchView();
11596 this.initTickableEvents();
11600 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11602 if(this.hiddenName){
11604 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11606 this.hiddenField.dom.value =
11607 this.hiddenValue !== undefined ? this.hiddenValue :
11608 this.value !== undefined ? this.value : '';
11610 // prevent input submission
11611 this.el.dom.removeAttribute('name');
11612 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11617 // this.el.dom.setAttribute('autocomplete', 'off');
11620 var cls = 'x-combo-list';
11622 //this.list = new Roo.Layer({
11623 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11629 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11630 _this.list.setWidth(lw);
11633 this.list.on('mouseover', this.onViewOver, this);
11634 this.list.on('mousemove', this.onViewMove, this);
11636 this.list.on('scroll', this.onViewScroll, this);
11639 this.list.swallowEvent('mousewheel');
11640 this.assetHeight = 0;
11643 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11644 this.assetHeight += this.header.getHeight();
11647 this.innerList = this.list.createChild({cls:cls+'-inner'});
11648 this.innerList.on('mouseover', this.onViewOver, this);
11649 this.innerList.on('mousemove', this.onViewMove, this);
11650 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11652 if(this.allowBlank && !this.pageSize && !this.disableClear){
11653 this.footer = this.list.createChild({cls:cls+'-ft'});
11654 this.pageTb = new Roo.Toolbar(this.footer);
11658 this.footer = this.list.createChild({cls:cls+'-ft'});
11659 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11660 {pageSize: this.pageSize});
11664 if (this.pageTb && this.allowBlank && !this.disableClear) {
11666 this.pageTb.add(new Roo.Toolbar.Fill(), {
11667 cls: 'x-btn-icon x-btn-clear',
11669 handler: function()
11672 _this.clearValue();
11673 _this.onSelect(false, -1);
11678 this.assetHeight += this.footer.getHeight();
11683 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11686 this.view = new Roo.View(this.list, this.tpl, {
11687 singleSelect:true, store: this.store, selectedClass: this.selectedClass
11689 //this.view.wrapEl.setDisplayed(false);
11690 this.view.on('click', this.onViewClick, this);
11694 this.store.on('beforeload', this.onBeforeLoad, this);
11695 this.store.on('load', this.onLoad, this);
11696 this.store.on('loadexception', this.onLoadException, this);
11698 if(this.resizable){
11699 this.resizer = new Roo.Resizable(this.list, {
11700 pinned:true, handles:'se'
11702 this.resizer.on('resize', function(r, w, h){
11703 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11704 this.listWidth = w;
11705 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11706 this.restrictHeight();
11708 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11711 if(!this.editable){
11712 this.editable = true;
11713 this.setEditable(false);
11718 if (typeof(this.events.add.listeners) != 'undefined') {
11720 this.addicon = this.wrap.createChild(
11721 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
11723 this.addicon.on('click', function(e) {
11724 this.fireEvent('add', this);
11727 if (typeof(this.events.edit.listeners) != 'undefined') {
11729 this.editicon = this.wrap.createChild(
11730 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
11731 if (this.addicon) {
11732 this.editicon.setStyle('margin-left', '40px');
11734 this.editicon.on('click', function(e) {
11736 // we fire even if inothing is selected..
11737 this.fireEvent('edit', this, this.lastData );
11743 this.keyNav = new Roo.KeyNav(this.inputEl(), {
11744 "up" : function(e){
11745 this.inKeyMode = true;
11749 "down" : function(e){
11750 if(!this.isExpanded()){
11751 this.onTriggerClick();
11753 this.inKeyMode = true;
11758 "enter" : function(e){
11759 // this.onViewClick();
11763 if(this.fireEvent("specialkey", this, e)){
11764 this.onViewClick(false);
11770 "esc" : function(e){
11774 "tab" : function(e){
11777 if(this.fireEvent("specialkey", this, e)){
11778 this.onViewClick(false);
11786 doRelay : function(foo, bar, hname){
11787 if(hname == 'down' || this.scope.isExpanded()){
11788 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11797 this.queryDelay = Math.max(this.queryDelay || 10,
11798 this.mode == 'local' ? 10 : 250);
11801 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11803 if(this.typeAhead){
11804 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11806 if(this.editable !== false){
11807 this.inputEl().on("keyup", this.onKeyUp, this);
11809 if(this.forceSelection){
11810 this.inputEl().on('blur', this.doForce, this);
11814 this.choices = this.el.select('ul.select2-choices', true).first();
11815 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11819 initTickableEvents: function()
11823 if(this.hiddenName){
11825 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11827 this.hiddenField.dom.value =
11828 this.hiddenValue !== undefined ? this.hiddenValue :
11829 this.value !== undefined ? this.value : '';
11831 // prevent input submission
11832 this.el.dom.removeAttribute('name');
11833 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11838 // this.list = this.el.select('ul.dropdown-menu',true).first();
11840 this.choices = this.el.select('ul.select2-choices', true).first();
11841 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11842 if(this.triggerList){
11843 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11846 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11847 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11849 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11850 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11852 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11853 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11855 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11856 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11857 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11860 this.cancelBtn.hide();
11865 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11866 _this.list.setWidth(lw);
11869 this.list.on('mouseover', this.onViewOver, this);
11870 this.list.on('mousemove', this.onViewMove, this);
11872 this.list.on('scroll', this.onViewScroll, this);
11875 this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11878 this.view = new Roo.View(this.list, this.tpl, {
11879 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11882 //this.view.wrapEl.setDisplayed(false);
11883 this.view.on('click', this.onViewClick, this);
11887 this.store.on('beforeload', this.onBeforeLoad, this);
11888 this.store.on('load', this.onLoad, this);
11889 this.store.on('loadexception', this.onLoadException, this);
11892 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11893 "up" : function(e){
11894 this.inKeyMode = true;
11898 "down" : function(e){
11899 this.inKeyMode = true;
11903 "enter" : function(e){
11904 if(this.fireEvent("specialkey", this, e)){
11905 this.onViewClick(false);
11911 "esc" : function(e){
11912 this.onTickableFooterButtonClick(e, false, false);
11915 "tab" : function(e){
11916 this.fireEvent("specialkey", this, e);
11918 this.onTickableFooterButtonClick(e, false, false);
11925 doRelay : function(e, fn, key){
11926 if(this.scope.isExpanded()){
11927 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11936 this.queryDelay = Math.max(this.queryDelay || 10,
11937 this.mode == 'local' ? 10 : 250);
11940 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11942 if(this.typeAhead){
11943 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11946 if(this.editable !== false){
11947 this.tickableInputEl().on("keyup", this.onKeyUp, this);
11952 onDestroy : function(){
11954 this.view.setStore(null);
11955 this.view.el.removeAllListeners();
11956 this.view.el.remove();
11957 this.view.purgeListeners();
11960 this.list.dom.innerHTML = '';
11964 this.store.un('beforeload', this.onBeforeLoad, this);
11965 this.store.un('load', this.onLoad, this);
11966 this.store.un('loadexception', this.onLoadException, this);
11968 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11972 fireKey : function(e){
11973 if(e.isNavKeyPress() && !this.list.isVisible()){
11974 this.fireEvent("specialkey", this, e);
11979 onResize: function(w, h){
11980 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11982 // if(typeof w != 'number'){
11983 // // we do not handle it!?!?
11986 // var tw = this.trigger.getWidth();
11987 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11988 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11990 // this.inputEl().setWidth( this.adjustWidth('input', x));
11992 // //this.trigger.setStyle('left', x+'px');
11994 // if(this.list && this.listWidth === undefined){
11995 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11996 // this.list.setWidth(lw);
11997 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12005 * Allow or prevent the user from directly editing the field text. If false is passed,
12006 * the user will only be able to select from the items defined in the dropdown list. This method
12007 * is the runtime equivalent of setting the 'editable' config option at config time.
12008 * @param {Boolean} value True to allow the user to directly edit the field text
12010 setEditable : function(value){
12011 if(value == this.editable){
12014 this.editable = value;
12016 this.inputEl().dom.setAttribute('readOnly', true);
12017 this.inputEl().on('mousedown', this.onTriggerClick, this);
12018 this.inputEl().addClass('x-combo-noedit');
12020 this.inputEl().dom.setAttribute('readOnly', false);
12021 this.inputEl().un('mousedown', this.onTriggerClick, this);
12022 this.inputEl().removeClass('x-combo-noedit');
12028 onBeforeLoad : function(combo,opts){
12029 if(!this.hasFocus){
12033 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12035 this.restrictHeight();
12036 this.selectedIndex = -1;
12040 onLoad : function(){
12042 this.hasQuery = false;
12044 if(!this.hasFocus){
12048 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12049 this.loading.hide();
12052 if(this.store.getCount() > 0){
12054 this.restrictHeight();
12055 if(this.lastQuery == this.allQuery){
12056 if(this.editable && !this.tickable){
12057 this.inputEl().dom.select();
12061 !this.selectByValue(this.value, true) &&
12064 !this.store.lastOptions ||
12065 typeof(this.store.lastOptions.add) == 'undefined' ||
12066 this.store.lastOptions.add != true
12069 this.select(0, true);
12072 if(this.autoFocus){
12075 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12076 this.taTask.delay(this.typeAheadDelay);
12080 this.onEmptyResults();
12086 onLoadException : function()
12088 this.hasQuery = false;
12090 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12091 this.loading.hide();
12094 if(this.tickable && this.editable){
12100 Roo.log(this.store.reader.jsonData);
12101 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12103 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12109 onTypeAhead : function(){
12110 if(this.store.getCount() > 0){
12111 var r = this.store.getAt(0);
12112 var newValue = r.data[this.displayField];
12113 var len = newValue.length;
12114 var selStart = this.getRawValue().length;
12116 if(selStart != len){
12117 this.setRawValue(newValue);
12118 this.selectText(selStart, newValue.length);
12124 onSelect : function(record, index){
12126 if(this.fireEvent('beforeselect', this, record, index) !== false){
12128 this.setFromData(index > -1 ? record.data : false);
12131 this.fireEvent('select', this, record, index);
12136 * Returns the currently selected field value or empty string if no value is set.
12137 * @return {String} value The selected value
12139 getValue : function(){
12142 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12145 if(this.valueField){
12146 return typeof this.value != 'undefined' ? this.value : '';
12148 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12153 * Clears any text/value currently set in the field
12155 clearValue : function(){
12156 if(this.hiddenField){
12157 this.hiddenField.dom.value = '';
12160 this.setRawValue('');
12161 this.lastSelectionText = '';
12162 this.lastData = false;
12164 var close = this.closeTriggerEl();
12173 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12174 * will be displayed in the field. If the value does not match the data value of an existing item,
12175 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12176 * Otherwise the field will be blank (although the value will still be set).
12177 * @param {String} value The value to match
12179 setValue : function(v){
12186 if(this.valueField){
12187 var r = this.findRecord(this.valueField, v);
12189 text = r.data[this.displayField];
12190 }else if(this.valueNotFoundText !== undefined){
12191 text = this.valueNotFoundText;
12194 this.lastSelectionText = text;
12195 if(this.hiddenField){
12196 this.hiddenField.dom.value = v;
12198 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12201 var close = this.closeTriggerEl();
12204 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12208 * @property {Object} the last set data for the element
12213 * Sets the value of the field based on a object which is related to the record format for the store.
12214 * @param {Object} value the value to set as. or false on reset?
12216 setFromData : function(o){
12223 var dv = ''; // display value
12224 var vv = ''; // value value..
12226 if (this.displayField) {
12227 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12229 // this is an error condition!!!
12230 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12233 if(this.valueField){
12234 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12237 var close = this.closeTriggerEl();
12240 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12243 if(this.hiddenField){
12244 this.hiddenField.dom.value = vv;
12246 this.lastSelectionText = dv;
12247 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12251 // no hidden field.. - we store the value in 'value', but still display
12252 // display field!!!!
12253 this.lastSelectionText = dv;
12254 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12261 reset : function(){
12262 // overridden so that last data is reset..
12269 this.setValue(this.originalValue);
12270 this.clearInvalid();
12271 this.lastData = false;
12273 this.view.clearSelections();
12277 findRecord : function(prop, value){
12279 if(this.store.getCount() > 0){
12280 this.store.each(function(r){
12281 if(r.data[prop] == value){
12291 getName: function()
12293 // returns hidden if it's set..
12294 if (!this.rendered) {return ''};
12295 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12299 onViewMove : function(e, t){
12300 this.inKeyMode = false;
12304 onViewOver : function(e, t){
12305 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12308 var item = this.view.findItemFromChild(t);
12311 var index = this.view.indexOf(item);
12312 this.select(index, false);
12317 onViewClick : function(view, doFocus, el, e)
12319 var index = this.view.getSelectedIndexes()[0];
12321 var r = this.store.getAt(index);
12325 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12332 Roo.each(this.tickItems, function(v,k){
12334 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12335 _this.tickItems.splice(k, 1);
12337 if(typeof(e) == 'undefined' && view == false){
12338 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12350 this.tickItems.push(r.data);
12352 if(typeof(e) == 'undefined' && view == false){
12353 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12360 this.onSelect(r, index);
12362 if(doFocus !== false && !this.blockFocus){
12363 this.inputEl().focus();
12368 restrictHeight : function(){
12369 //this.innerList.dom.style.height = '';
12370 //var inner = this.innerList.dom;
12371 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12372 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12373 //this.list.beginUpdate();
12374 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12375 this.list.alignTo(this.inputEl(), this.listAlign);
12376 this.list.alignTo(this.inputEl(), this.listAlign);
12377 //this.list.endUpdate();
12381 onEmptyResults : function(){
12383 if(this.tickable && this.editable){
12384 this.restrictHeight();
12392 * Returns true if the dropdown list is expanded, else false.
12394 isExpanded : function(){
12395 return this.list.isVisible();
12399 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12400 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12401 * @param {String} value The data value of the item to select
12402 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12403 * selected item if it is not currently in view (defaults to true)
12404 * @return {Boolean} True if the value matched an item in the list, else false
12406 selectByValue : function(v, scrollIntoView){
12407 if(v !== undefined && v !== null){
12408 var r = this.findRecord(this.valueField || this.displayField, v);
12410 this.select(this.store.indexOf(r), scrollIntoView);
12418 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12419 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12420 * @param {Number} index The zero-based index of the list item to select
12421 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12422 * selected item if it is not currently in view (defaults to true)
12424 select : function(index, scrollIntoView){
12425 this.selectedIndex = index;
12426 this.view.select(index);
12427 if(scrollIntoView !== false){
12428 var el = this.view.getNode(index);
12430 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12433 this.list.scrollChildIntoView(el, false);
12439 selectNext : function(){
12440 var ct = this.store.getCount();
12442 if(this.selectedIndex == -1){
12444 }else if(this.selectedIndex < ct-1){
12445 this.select(this.selectedIndex+1);
12451 selectPrev : function(){
12452 var ct = this.store.getCount();
12454 if(this.selectedIndex == -1){
12456 }else if(this.selectedIndex != 0){
12457 this.select(this.selectedIndex-1);
12463 onKeyUp : function(e){
12464 if(this.editable !== false && !e.isSpecialKey()){
12465 this.lastKey = e.getKey();
12466 this.dqTask.delay(this.queryDelay);
12471 validateBlur : function(){
12472 return !this.list || !this.list.isVisible();
12476 initQuery : function(){
12478 var v = this.getRawValue();
12480 if(this.tickable && this.editable){
12481 v = this.tickableInputEl().getValue();
12488 doForce : function(){
12489 if(this.inputEl().dom.value.length > 0){
12490 this.inputEl().dom.value =
12491 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12497 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
12498 * query allowing the query action to be canceled if needed.
12499 * @param {String} query The SQL query to execute
12500 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12501 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
12502 * saved in the current store (defaults to false)
12504 doQuery : function(q, forceAll){
12506 if(q === undefined || q === null){
12511 forceAll: forceAll,
12515 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12520 forceAll = qe.forceAll;
12521 if(forceAll === true || (q.length >= this.minChars)){
12523 this.hasQuery = true;
12525 if(this.lastQuery != q || this.alwaysQuery){
12526 this.lastQuery = q;
12527 if(this.mode == 'local'){
12528 this.selectedIndex = -1;
12530 this.store.clearFilter();
12533 if(this.specialFilter){
12534 this.fireEvent('specialfilter', this);
12539 this.store.filter(this.displayField, q);
12542 this.store.fireEvent("datachanged", this.store);
12549 this.store.baseParams[this.queryParam] = q;
12551 var options = {params : this.getParams(q)};
12554 options.add = true;
12555 options.params.start = this.page * this.pageSize;
12558 this.store.load(options);
12561 * this code will make the page width larger, at the beginning, the list not align correctly,
12562 * we should expand the list on onLoad
12563 * so command out it
12568 this.selectedIndex = -1;
12573 this.loadNext = false;
12577 getParams : function(q){
12579 //p[this.queryParam] = q;
12583 p.limit = this.pageSize;
12589 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12591 collapse : function(){
12592 if(!this.isExpanded()){
12599 this.hasFocus = false;
12601 this.cancelBtn.hide();
12602 this.trigger.show();
12605 this.tickableInputEl().dom.value = '';
12606 this.tickableInputEl().blur();
12611 Roo.get(document).un('mousedown', this.collapseIf, this);
12612 Roo.get(document).un('mousewheel', this.collapseIf, this);
12613 if (!this.editable) {
12614 Roo.get(document).un('keydown', this.listKeyPress, this);
12616 this.fireEvent('collapse', this);
12620 collapseIf : function(e){
12621 var in_combo = e.within(this.el);
12622 var in_list = e.within(this.list);
12623 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12625 if (in_combo || in_list || is_list) {
12626 //e.stopPropagation();
12631 this.onTickableFooterButtonClick(e, false, false);
12639 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12641 expand : function(){
12643 if(this.isExpanded() || !this.hasFocus){
12647 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12648 this.list.setWidth(lw);
12655 this.restrictHeight();
12659 this.tickItems = Roo.apply([], this.item);
12662 this.cancelBtn.show();
12663 this.trigger.hide();
12666 this.tickableInputEl().focus();
12671 Roo.get(document).on('mousedown', this.collapseIf, this);
12672 Roo.get(document).on('mousewheel', this.collapseIf, this);
12673 if (!this.editable) {
12674 Roo.get(document).on('keydown', this.listKeyPress, this);
12677 this.fireEvent('expand', this);
12681 // Implements the default empty TriggerField.onTriggerClick function
12682 onTriggerClick : function(e)
12684 Roo.log('trigger click');
12686 if(this.disabled || !this.triggerList){
12691 this.loadNext = false;
12693 if(this.isExpanded()){
12695 if (!this.blockFocus) {
12696 this.inputEl().focus();
12700 this.hasFocus = true;
12701 if(this.triggerAction == 'all') {
12702 this.doQuery(this.allQuery, true);
12704 this.doQuery(this.getRawValue());
12706 if (!this.blockFocus) {
12707 this.inputEl().focus();
12712 onTickableTriggerClick : function(e)
12719 this.loadNext = false;
12720 this.hasFocus = true;
12722 if(this.triggerAction == 'all') {
12723 this.doQuery(this.allQuery, true);
12725 this.doQuery(this.getRawValue());
12729 onSearchFieldClick : function(e)
12731 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12732 this.onTickableFooterButtonClick(e, false, false);
12736 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12741 this.loadNext = false;
12742 this.hasFocus = true;
12744 if(this.triggerAction == 'all') {
12745 this.doQuery(this.allQuery, true);
12747 this.doQuery(this.getRawValue());
12751 listKeyPress : function(e)
12753 //Roo.log('listkeypress');
12754 // scroll to first matching element based on key pres..
12755 if (e.isSpecialKey()) {
12758 var k = String.fromCharCode(e.getKey()).toUpperCase();
12761 var csel = this.view.getSelectedNodes();
12762 var cselitem = false;
12764 var ix = this.view.indexOf(csel[0]);
12765 cselitem = this.store.getAt(ix);
12766 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12772 this.store.each(function(v) {
12774 // start at existing selection.
12775 if (cselitem.id == v.id) {
12781 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12782 match = this.store.indexOf(v);
12788 if (match === false) {
12789 return true; // no more action?
12792 this.view.select(match);
12793 var sn = Roo.get(this.view.getSelectedNodes()[0])
12794 sn.scrollIntoView(sn.dom.parentNode, false);
12797 onViewScroll : function(e, t){
12799 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){
12803 this.hasQuery = true;
12805 this.loading = this.list.select('.loading', true).first();
12807 if(this.loading === null){
12808 this.list.createChild({
12810 cls: 'loading select2-more-results select2-active',
12811 html: 'Loading more results...'
12814 this.loading = this.list.select('.loading', true).first();
12816 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12818 this.loading.hide();
12821 this.loading.show();
12826 this.loadNext = true;
12828 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12833 addItem : function(o)
12835 var dv = ''; // display value
12837 if (this.displayField) {
12838 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12840 // this is an error condition!!!
12841 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12848 var choice = this.choices.createChild({
12850 cls: 'select2-search-choice',
12859 cls: 'select2-search-choice-close',
12864 }, this.searchField);
12866 var close = choice.select('a.select2-search-choice-close', true).first()
12868 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12876 this.inputEl().dom.value = '';
12881 onRemoveItem : function(e, _self, o)
12883 e.preventDefault();
12885 this.lastItem = Roo.apply([], this.item);
12887 var index = this.item.indexOf(o.data) * 1;
12890 Roo.log('not this item?!');
12894 this.item.splice(index, 1);
12899 this.fireEvent('remove', this, e);
12905 syncValue : function()
12907 if(!this.item.length){
12914 Roo.each(this.item, function(i){
12915 if(_this.valueField){
12916 value.push(i[_this.valueField]);
12923 this.value = value.join(',');
12925 if(this.hiddenField){
12926 this.hiddenField.dom.value = this.value;
12929 this.store.fireEvent("datachanged", this.store);
12932 clearItem : function()
12934 if(!this.multiple){
12940 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12948 if(this.tickable && !Roo.isTouch){
12949 this.view.refresh();
12953 inputEl: function ()
12955 if(Roo.isTouch && this.mobileTouchView){
12956 return this.el.select('input.form-control',true).first();
12960 return this.searchField;
12963 return this.el.select('input.form-control',true).first();
12967 onTickableFooterButtonClick : function(e, btn, el)
12969 e.preventDefault();
12971 this.lastItem = Roo.apply([], this.item);
12973 if(btn && btn.name == 'cancel'){
12974 this.tickItems = Roo.apply([], this.item);
12983 Roo.each(this.tickItems, function(o){
12991 validate : function()
12993 var v = this.getRawValue();
12996 v = this.getValue();
12999 if(this.disabled || this.allowBlank || v.length){
13004 this.markInvalid();
13008 tickableInputEl : function()
13010 if(!this.tickable || !this.editable){
13011 return this.inputEl();
13014 return this.inputEl().select('.select2-search-field-input', true).first();
13018 getAutoCreateTouchView : function()
13023 cls: 'form-group' //input-group
13029 type : this.inputType,
13030 cls : 'form-control x-combo-noedit',
13031 autocomplete: 'new-password',
13032 placeholder : this.placeholder || '',
13037 input.name = this.name;
13041 input.cls += ' input-' + this.size;
13044 if (this.disabled) {
13045 input.disabled = true;
13056 inputblock.cls += ' input-group';
13058 inputblock.cn.unshift({
13060 cls : 'input-group-addon',
13065 if(this.removable && !this.multiple){
13066 inputblock.cls += ' roo-removable';
13068 inputblock.cn.push({
13071 cls : 'roo-combo-removable-btn close'
13075 if(this.hasFeedback && !this.allowBlank){
13077 inputblock.cls += ' has-feedback';
13079 inputblock.cn.push({
13081 cls: 'glyphicon form-control-feedback'
13088 inputblock.cls += (this.before) ? '' : ' input-group';
13090 inputblock.cn.push({
13092 cls : 'input-group-addon',
13103 cls: 'form-hidden-field'
13117 cls: 'form-hidden-field'
13121 cls: 'select2-choices',
13125 cls: 'select2-search-field',
13138 cls: 'select2-container input-group',
13145 combobox.cls += ' select2-container-multi';
13148 var align = this.labelAlign || this.parentLabelAlign();
13152 if(this.fieldLabel.length){
13154 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13155 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13160 cls : 'control-label ' + lw,
13161 html : this.fieldLabel
13173 var settings = this;
13175 ['xs','sm','md','lg'].map(function(size){
13176 if (settings[size]) {
13177 cfg.cls += ' col-' + size + '-' + settings[size];
13184 initTouchView : function()
13186 this.renderTouchView();
13188 this.touchViewEl.on('scroll', function(){
13189 this.el.dom.scrollTop = 0;
13192 this.inputEl().on("click", this.showTouchView, this);
13193 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13194 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13196 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13198 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13199 this.store.on('load', this.onTouchViewLoad, this);
13200 this.store.on('loadexception', this.onTouchViewLoadException, this);
13202 if(this.hiddenName){
13204 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13206 this.hiddenField.dom.value =
13207 this.hiddenValue !== undefined ? this.hiddenValue :
13208 this.value !== undefined ? this.value : '';
13210 this.el.dom.removeAttribute('name');
13211 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13215 this.choices = this.el.select('ul.select2-choices', true).first();
13216 this.searchField = this.el.select('ul li.select2-search-field', true).first();
13219 if(this.removable && !this.multiple){
13220 var close = this.closeTriggerEl();
13222 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13223 close.on('click', this.removeBtnClick, this, close);
13232 renderTouchView : function()
13234 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13235 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13237 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13238 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13240 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13241 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13242 this.touchViewBodyEl.setStyle('overflow', 'auto');
13244 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13245 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13247 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13248 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13252 showTouchView : function()
13254 this.touchViewHeaderEl.hide();
13256 if(this.fieldLabel.length){
13257 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13258 this.touchViewHeaderEl.show();
13261 this.touchViewEl.show();
13263 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13264 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13266 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13268 if(this.fieldLabel.length){
13269 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13272 this.touchViewBodyEl.setHeight(bodyHeight);
13276 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13278 this.touchViewEl.addClass('in');
13281 this.doTouchViewQuery();
13285 hideTouchView : function()
13287 this.touchViewEl.removeClass('in');
13291 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13293 this.touchViewEl.setStyle('display', 'none');
13298 setTouchViewValue : function()
13305 Roo.each(this.tickItems, function(o){
13310 this.hideTouchView();
13313 doTouchViewQuery : function()
13322 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13326 if(!this.alwaysQuery || this.mode == 'local'){
13327 this.onTouchViewLoad();
13334 onTouchViewBeforeLoad : function(combo,opts)
13340 onTouchViewLoad : function()
13342 if(this.store.getCount() < 1){
13343 this.onTouchViewEmptyResults();
13347 this.clearTouchView();
13349 var rawValue = this.getRawValue();
13351 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13353 this.tickItems = [];
13355 this.store.data.each(function(d, rowIndex){
13356 var row = this.touchViewListGroup.createChild(template);
13358 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13359 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13362 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13363 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13366 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13367 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13368 this.tickItems.push(d.data);
13371 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13375 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13377 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13379 if(this.fieldLabel.length){
13380 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13383 var listHeight = this.touchViewListGroup.getHeight();
13387 if(firstChecked && listHeight > bodyHeight){
13388 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13393 onTouchViewLoadException : function()
13395 this.hideTouchView();
13398 onTouchViewEmptyResults : function()
13400 this.clearTouchView();
13402 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13404 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13408 clearTouchView : function()
13410 this.touchViewListGroup.dom.innerHTML = '';
13413 onTouchViewClick : function(e, el, o)
13415 e.preventDefault();
13418 var rowIndex = o.rowIndex;
13420 var r = this.store.getAt(rowIndex);
13422 if(!this.multiple){
13423 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13424 c.dom.removeAttribute('checked');
13427 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13429 this.setFromData(r.data);
13431 var close = this.closeTriggerEl();
13437 this.hideTouchView();
13439 this.fireEvent('select', this, r, rowIndex);
13444 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13445 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13446 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13450 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13451 this.addItem(r.data);
13452 this.tickItems.push(r.data);
13458 * @cfg {Boolean} grow
13462 * @cfg {Number} growMin
13466 * @cfg {Number} growMax
13475 Roo.apply(Roo.bootstrap.ComboBox, {
13479 cls: 'modal-header',
13501 cls: 'list-group-item',
13505 cls: 'roo-combobox-list-group-item-value'
13509 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13523 listItemCheckbox : {
13525 cls: 'list-group-item',
13529 cls: 'roo-combobox-list-group-item-value'
13533 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13549 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13554 cls: 'modal-footer',
13562 cls: 'col-xs-6 text-left',
13565 cls: 'btn btn-danger roo-touch-view-cancel',
13571 cls: 'col-xs-6 text-right',
13574 cls: 'btn btn-success roo-touch-view-ok',
13585 Roo.apply(Roo.bootstrap.ComboBox, {
13587 touchViewTemplate : {
13589 cls: 'modal fade roo-combobox-touch-view',
13593 cls: 'modal-dialog',
13597 cls: 'modal-content',
13599 Roo.bootstrap.ComboBox.header,
13600 Roo.bootstrap.ComboBox.body,
13601 Roo.bootstrap.ComboBox.footer
13610 * Ext JS Library 1.1.1
13611 * Copyright(c) 2006-2007, Ext JS, LLC.
13613 * Originally Released Under LGPL - original licence link has changed is not relivant.
13616 * <script type="text/javascript">
13621 * @extends Roo.util.Observable
13622 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
13623 * This class also supports single and multi selection modes. <br>
13624 * Create a data model bound view:
13626 var store = new Roo.data.Store(...);
13628 var view = new Roo.View({
13630 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
13632 singleSelect: true,
13633 selectedClass: "ydataview-selected",
13637 // listen for node click?
13638 view.on("click", function(vw, index, node, e){
13639 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13643 dataModel.load("foobar.xml");
13645 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13647 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13648 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13650 * Note: old style constructor is still suported (container, template, config)
13653 * Create a new View
13654 * @param {Object} config The config object
13657 Roo.View = function(config, depreciated_tpl, depreciated_config){
13659 this.parent = false;
13661 if (typeof(depreciated_tpl) == 'undefined') {
13662 // new way.. - universal constructor.
13663 Roo.apply(this, config);
13664 this.el = Roo.get(this.el);
13667 this.el = Roo.get(config);
13668 this.tpl = depreciated_tpl;
13669 Roo.apply(this, depreciated_config);
13671 this.wrapEl = this.el.wrap().wrap();
13672 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13675 if(typeof(this.tpl) == "string"){
13676 this.tpl = new Roo.Template(this.tpl);
13678 // support xtype ctors..
13679 this.tpl = new Roo.factory(this.tpl, Roo);
13683 this.tpl.compile();
13688 * @event beforeclick
13689 * Fires before a click is processed. Returns false to cancel the default action.
13690 * @param {Roo.View} this
13691 * @param {Number} index The index of the target node
13692 * @param {HTMLElement} node The target node
13693 * @param {Roo.EventObject} e The raw event object
13695 "beforeclick" : true,
13698 * Fires when a template node is clicked.
13699 * @param {Roo.View} this
13700 * @param {Number} index The index of the target node
13701 * @param {HTMLElement} node The target node
13702 * @param {Roo.EventObject} e The raw event object
13707 * Fires when a template node is double clicked.
13708 * @param {Roo.View} this
13709 * @param {Number} index The index of the target node
13710 * @param {HTMLElement} node The target node
13711 * @param {Roo.EventObject} e The raw event object
13715 * @event contextmenu
13716 * Fires when a template node is right clicked.
13717 * @param {Roo.View} this
13718 * @param {Number} index The index of the target node
13719 * @param {HTMLElement} node The target node
13720 * @param {Roo.EventObject} e The raw event object
13722 "contextmenu" : true,
13724 * @event selectionchange
13725 * Fires when the selected nodes change.
13726 * @param {Roo.View} this
13727 * @param {Array} selections Array of the selected nodes
13729 "selectionchange" : true,
13732 * @event beforeselect
13733 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13734 * @param {Roo.View} this
13735 * @param {HTMLElement} node The node to be selected
13736 * @param {Array} selections Array of currently selected nodes
13738 "beforeselect" : true,
13740 * @event preparedata
13741 * Fires on every row to render, to allow you to change the data.
13742 * @param {Roo.View} this
13743 * @param {Object} data to be rendered (change this)
13745 "preparedata" : true
13753 "click": this.onClick,
13754 "dblclick": this.onDblClick,
13755 "contextmenu": this.onContextMenu,
13759 this.selections = [];
13761 this.cmp = new Roo.CompositeElementLite([]);
13763 this.store = Roo.factory(this.store, Roo.data);
13764 this.setStore(this.store, true);
13767 if ( this.footer && this.footer.xtype) {
13769 var fctr = this.wrapEl.appendChild(document.createElement("div"));
13771 this.footer.dataSource = this.store
13772 this.footer.container = fctr;
13773 this.footer = Roo.factory(this.footer, Roo);
13774 fctr.insertFirst(this.el);
13776 // this is a bit insane - as the paging toolbar seems to detach the el..
13777 // dom.parentNode.parentNode.parentNode
13778 // they get detached?
13782 Roo.View.superclass.constructor.call(this);
13787 Roo.extend(Roo.View, Roo.util.Observable, {
13790 * @cfg {Roo.data.Store} store Data store to load data from.
13795 * @cfg {String|Roo.Element} el The container element.
13800 * @cfg {String|Roo.Template} tpl The template used by this View
13804 * @cfg {String} dataName the named area of the template to use as the data area
13805 * Works with domtemplates roo-name="name"
13809 * @cfg {String} selectedClass The css class to add to selected nodes
13811 selectedClass : "x-view-selected",
13813 * @cfg {String} emptyText The empty text to show when nothing is loaded.
13818 * @cfg {String} text to display on mask (default Loading)
13822 * @cfg {Boolean} multiSelect Allow multiple selection
13824 multiSelect : false,
13826 * @cfg {Boolean} singleSelect Allow single selection
13828 singleSelect: false,
13831 * @cfg {Boolean} toggleSelect - selecting
13833 toggleSelect : false,
13836 * @cfg {Boolean} tickable - selecting
13841 * Returns the element this view is bound to.
13842 * @return {Roo.Element}
13844 getEl : function(){
13845 return this.wrapEl;
13851 * Refreshes the view. - called by datachanged on the store. - do not call directly.
13853 refresh : function(){
13854 //Roo.log('refresh');
13857 // if we are using something like 'domtemplate', then
13858 // the what gets used is:
13859 // t.applySubtemplate(NAME, data, wrapping data..)
13860 // the outer template then get' applied with
13861 // the store 'extra data'
13862 // and the body get's added to the
13863 // roo-name="data" node?
13864 // <span class='roo-tpl-{name}'></span> ?????
13868 this.clearSelections();
13869 this.el.update("");
13871 var records = this.store.getRange();
13872 if(records.length < 1) {
13874 // is this valid?? = should it render a template??
13876 this.el.update(this.emptyText);
13880 if (this.dataName) {
13881 this.el.update(t.apply(this.store.meta)); //????
13882 el = this.el.child('.roo-tpl-' + this.dataName);
13885 for(var i = 0, len = records.length; i < len; i++){
13886 var data = this.prepareData(records[i].data, i, records[i]);
13887 this.fireEvent("preparedata", this, data, i, records[i]);
13889 var d = Roo.apply({}, data);
13892 Roo.apply(d, {'roo-id' : Roo.id()});
13896 Roo.each(this.parent.item, function(item){
13897 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13900 Roo.apply(d, {'roo-data-checked' : 'checked'});
13904 html[html.length] = Roo.util.Format.trim(
13906 t.applySubtemplate(this.dataName, d, this.store.meta) :
13913 el.update(html.join(""));
13914 this.nodes = el.dom.childNodes;
13915 this.updateIndexes(0);
13920 * Function to override to reformat the data that is sent to
13921 * the template for each node.
13922 * DEPRICATED - use the preparedata event handler.
13923 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13924 * a JSON object for an UpdateManager bound view).
13926 prepareData : function(data, index, record)
13928 this.fireEvent("preparedata", this, data, index, record);
13932 onUpdate : function(ds, record){
13933 // Roo.log('on update');
13934 this.clearSelections();
13935 var index = this.store.indexOf(record);
13936 var n = this.nodes[index];
13937 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13938 n.parentNode.removeChild(n);
13939 this.updateIndexes(index, index);
13945 onAdd : function(ds, records, index)
13947 //Roo.log(['on Add', ds, records, index] );
13948 this.clearSelections();
13949 if(this.nodes.length == 0){
13953 var n = this.nodes[index];
13954 for(var i = 0, len = records.length; i < len; i++){
13955 var d = this.prepareData(records[i].data, i, records[i]);
13957 this.tpl.insertBefore(n, d);
13960 this.tpl.append(this.el, d);
13963 this.updateIndexes(index);
13966 onRemove : function(ds, record, index){
13967 // Roo.log('onRemove');
13968 this.clearSelections();
13969 var el = this.dataName ?
13970 this.el.child('.roo-tpl-' + this.dataName) :
13973 el.dom.removeChild(this.nodes[index]);
13974 this.updateIndexes(index);
13978 * Refresh an individual node.
13979 * @param {Number} index
13981 refreshNode : function(index){
13982 this.onUpdate(this.store, this.store.getAt(index));
13985 updateIndexes : function(startIndex, endIndex){
13986 var ns = this.nodes;
13987 startIndex = startIndex || 0;
13988 endIndex = endIndex || ns.length - 1;
13989 for(var i = startIndex; i <= endIndex; i++){
13990 ns[i].nodeIndex = i;
13995 * Changes the data store this view uses and refresh the view.
13996 * @param {Store} store
13998 setStore : function(store, initial){
13999 if(!initial && this.store){
14000 this.store.un("datachanged", this.refresh);
14001 this.store.un("add", this.onAdd);
14002 this.store.un("remove", this.onRemove);
14003 this.store.un("update", this.onUpdate);
14004 this.store.un("clear", this.refresh);
14005 this.store.un("beforeload", this.onBeforeLoad);
14006 this.store.un("load", this.onLoad);
14007 this.store.un("loadexception", this.onLoad);
14011 store.on("datachanged", this.refresh, this);
14012 store.on("add", this.onAdd, this);
14013 store.on("remove", this.onRemove, this);
14014 store.on("update", this.onUpdate, this);
14015 store.on("clear", this.refresh, this);
14016 store.on("beforeload", this.onBeforeLoad, this);
14017 store.on("load", this.onLoad, this);
14018 store.on("loadexception", this.onLoad, this);
14026 * onbeforeLoad - masks the loading area.
14029 onBeforeLoad : function(store,opts)
14031 //Roo.log('onBeforeLoad');
14033 this.el.update("");
14035 this.el.mask(this.mask ? this.mask : "Loading" );
14037 onLoad : function ()
14044 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14045 * @param {HTMLElement} node
14046 * @return {HTMLElement} The template node
14048 findItemFromChild : function(node){
14049 var el = this.dataName ?
14050 this.el.child('.roo-tpl-' + this.dataName,true) :
14053 if(!node || node.parentNode == el){
14056 var p = node.parentNode;
14057 while(p && p != el){
14058 if(p.parentNode == el){
14067 onClick : function(e){
14068 var item = this.findItemFromChild(e.getTarget());
14070 var index = this.indexOf(item);
14071 if(this.onItemClick(item, index, e) !== false){
14072 this.fireEvent("click", this, index, item, e);
14075 this.clearSelections();
14080 onContextMenu : function(e){
14081 var item = this.findItemFromChild(e.getTarget());
14083 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14088 onDblClick : function(e){
14089 var item = this.findItemFromChild(e.getTarget());
14091 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14095 onItemClick : function(item, index, e)
14097 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14100 if (this.toggleSelect) {
14101 var m = this.isSelected(item) ? 'unselect' : 'select';
14104 _t[m](item, true, false);
14107 if(this.multiSelect || this.singleSelect){
14108 if(this.multiSelect && e.shiftKey && this.lastSelection){
14109 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14111 this.select(item, this.multiSelect && e.ctrlKey);
14112 this.lastSelection = item;
14115 if(!this.tickable){
14116 e.preventDefault();
14124 * Get the number of selected nodes.
14127 getSelectionCount : function(){
14128 return this.selections.length;
14132 * Get the currently selected nodes.
14133 * @return {Array} An array of HTMLElements
14135 getSelectedNodes : function(){
14136 return this.selections;
14140 * Get the indexes of the selected nodes.
14143 getSelectedIndexes : function(){
14144 var indexes = [], s = this.selections;
14145 for(var i = 0, len = s.length; i < len; i++){
14146 indexes.push(s[i].nodeIndex);
14152 * Clear all selections
14153 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14155 clearSelections : function(suppressEvent){
14156 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14157 this.cmp.elements = this.selections;
14158 this.cmp.removeClass(this.selectedClass);
14159 this.selections = [];
14160 if(!suppressEvent){
14161 this.fireEvent("selectionchange", this, this.selections);
14167 * Returns true if the passed node is selected
14168 * @param {HTMLElement/Number} node The node or node index
14169 * @return {Boolean}
14171 isSelected : function(node){
14172 var s = this.selections;
14176 node = this.getNode(node);
14177 return s.indexOf(node) !== -1;
14182 * @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
14183 * @param {Boolean} keepExisting (optional) true to keep existing selections
14184 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14186 select : function(nodeInfo, keepExisting, suppressEvent){
14187 if(nodeInfo instanceof Array){
14189 this.clearSelections(true);
14191 for(var i = 0, len = nodeInfo.length; i < len; i++){
14192 this.select(nodeInfo[i], true, true);
14196 var node = this.getNode(nodeInfo);
14197 if(!node || this.isSelected(node)){
14198 return; // already selected.
14201 this.clearSelections(true);
14204 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14205 Roo.fly(node).addClass(this.selectedClass);
14206 this.selections.push(node);
14207 if(!suppressEvent){
14208 this.fireEvent("selectionchange", this, this.selections);
14216 * @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
14217 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14218 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14220 unselect : function(nodeInfo, keepExisting, suppressEvent)
14222 if(nodeInfo instanceof Array){
14223 Roo.each(this.selections, function(s) {
14224 this.unselect(s, nodeInfo);
14228 var node = this.getNode(nodeInfo);
14229 if(!node || !this.isSelected(node)){
14230 //Roo.log("not selected");
14231 return; // not selected.
14235 Roo.each(this.selections, function(s) {
14237 Roo.fly(node).removeClass(this.selectedClass);
14244 this.selections= ns;
14245 this.fireEvent("selectionchange", this, this.selections);
14249 * Gets a template node.
14250 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14251 * @return {HTMLElement} The node or null if it wasn't found
14253 getNode : function(nodeInfo){
14254 if(typeof nodeInfo == "string"){
14255 return document.getElementById(nodeInfo);
14256 }else if(typeof nodeInfo == "number"){
14257 return this.nodes[nodeInfo];
14263 * Gets a range template nodes.
14264 * @param {Number} startIndex
14265 * @param {Number} endIndex
14266 * @return {Array} An array of nodes
14268 getNodes : function(start, end){
14269 var ns = this.nodes;
14270 start = start || 0;
14271 end = typeof end == "undefined" ? ns.length - 1 : end;
14274 for(var i = start; i <= end; i++){
14278 for(var i = start; i >= end; i--){
14286 * Finds the index of the passed node
14287 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14288 * @return {Number} The index of the node or -1
14290 indexOf : function(node){
14291 node = this.getNode(node);
14292 if(typeof node.nodeIndex == "number"){
14293 return node.nodeIndex;
14295 var ns = this.nodes;
14296 for(var i = 0, len = ns.length; i < len; i++){
14307 * based on jquery fullcalendar
14311 Roo.bootstrap = Roo.bootstrap || {};
14313 * @class Roo.bootstrap.Calendar
14314 * @extends Roo.bootstrap.Component
14315 * Bootstrap Calendar class
14316 * @cfg {Boolean} loadMask (true|false) default false
14317 * @cfg {Object} header generate the user specific header of the calendar, default false
14320 * Create a new Container
14321 * @param {Object} config The config object
14326 Roo.bootstrap.Calendar = function(config){
14327 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14331 * Fires when a date is selected
14332 * @param {DatePicker} this
14333 * @param {Date} date The selected date
14337 * @event monthchange
14338 * Fires when the displayed month changes
14339 * @param {DatePicker} this
14340 * @param {Date} date The selected month
14342 'monthchange': true,
14344 * @event evententer
14345 * Fires when mouse over an event
14346 * @param {Calendar} this
14347 * @param {event} Event
14349 'evententer': true,
14351 * @event eventleave
14352 * Fires when the mouse leaves an
14353 * @param {Calendar} this
14356 'eventleave': true,
14358 * @event eventclick
14359 * Fires when the mouse click an
14360 * @param {Calendar} this
14369 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
14372 * @cfg {Number} startDay
14373 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14381 getAutoCreate : function(){
14384 var fc_button = function(name, corner, style, content ) {
14385 return Roo.apply({},{
14387 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
14389 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14392 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14403 style : 'width:100%',
14410 cls : 'fc-header-left',
14412 fc_button('prev', 'left', 'arrow', '‹' ),
14413 fc_button('next', 'right', 'arrow', '›' ),
14414 { tag: 'span', cls: 'fc-header-space' },
14415 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
14423 cls : 'fc-header-center',
14427 cls: 'fc-header-title',
14430 html : 'month / year'
14438 cls : 'fc-header-right',
14440 /* fc_button('month', 'left', '', 'month' ),
14441 fc_button('week', '', '', 'week' ),
14442 fc_button('day', 'right', '', 'day' )
14454 header = this.header;
14457 var cal_heads = function() {
14459 // fixme - handle this.
14461 for (var i =0; i < Date.dayNames.length; i++) {
14462 var d = Date.dayNames[i];
14465 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14466 html : d.substring(0,3)
14470 ret[0].cls += ' fc-first';
14471 ret[6].cls += ' fc-last';
14474 var cal_cell = function(n) {
14477 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14482 cls: 'fc-day-number',
14486 cls: 'fc-day-content',
14490 style: 'position: relative;' // height: 17px;
14502 var cal_rows = function() {
14505 for (var r = 0; r < 6; r++) {
14512 for (var i =0; i < Date.dayNames.length; i++) {
14513 var d = Date.dayNames[i];
14514 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14517 row.cn[0].cls+=' fc-first';
14518 row.cn[0].cn[0].style = 'min-height:90px';
14519 row.cn[6].cls+=' fc-last';
14523 ret[0].cls += ' fc-first';
14524 ret[4].cls += ' fc-prev-last';
14525 ret[5].cls += ' fc-last';
14532 cls: 'fc-border-separate',
14533 style : 'width:100%',
14541 cls : 'fc-first fc-last',
14559 cls : 'fc-content',
14560 style : "position: relative;",
14563 cls : 'fc-view fc-view-month fc-grid',
14564 style : 'position: relative',
14565 unselectable : 'on',
14568 cls : 'fc-event-container',
14569 style : 'position:absolute;z-index:8;top:0;left:0;'
14587 initEvents : function()
14590 throw "can not find store for calendar";
14596 style: "text-align:center",
14600 style: "background-color:white;width:50%;margin:250 auto",
14604 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
14615 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14617 var size = this.el.select('.fc-content', true).first().getSize();
14618 this.maskEl.setSize(size.width, size.height);
14619 this.maskEl.enableDisplayMode("block");
14620 if(!this.loadMask){
14621 this.maskEl.hide();
14624 this.store = Roo.factory(this.store, Roo.data);
14625 this.store.on('load', this.onLoad, this);
14626 this.store.on('beforeload', this.onBeforeLoad, this);
14630 this.cells = this.el.select('.fc-day',true);
14631 //Roo.log(this.cells);
14632 this.textNodes = this.el.query('.fc-day-number');
14633 this.cells.addClassOnOver('fc-state-hover');
14635 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14636 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14637 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14638 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14640 this.on('monthchange', this.onMonthChange, this);
14642 this.update(new Date().clearTime());
14645 resize : function() {
14646 var sz = this.el.getSize();
14648 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14649 this.el.select('.fc-day-content div',true).setHeight(34);
14654 showPrevMonth : function(e){
14655 this.update(this.activeDate.add("mo", -1));
14657 showToday : function(e){
14658 this.update(new Date().clearTime());
14661 showNextMonth : function(e){
14662 this.update(this.activeDate.add("mo", 1));
14666 showPrevYear : function(){
14667 this.update(this.activeDate.add("y", -1));
14671 showNextYear : function(){
14672 this.update(this.activeDate.add("y", 1));
14677 update : function(date)
14679 var vd = this.activeDate;
14680 this.activeDate = date;
14681 // if(vd && this.el){
14682 // var t = date.getTime();
14683 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14684 // Roo.log('using add remove');
14686 // this.fireEvent('monthchange', this, date);
14688 // this.cells.removeClass("fc-state-highlight");
14689 // this.cells.each(function(c){
14690 // if(c.dateValue == t){
14691 // c.addClass("fc-state-highlight");
14692 // setTimeout(function(){
14693 // try{c.dom.firstChild.focus();}catch(e){}
14703 var days = date.getDaysInMonth();
14705 var firstOfMonth = date.getFirstDateOfMonth();
14706 var startingPos = firstOfMonth.getDay()-this.startDay;
14708 if(startingPos < this.startDay){
14712 var pm = date.add(Date.MONTH, -1);
14713 var prevStart = pm.getDaysInMonth()-startingPos;
14715 this.cells = this.el.select('.fc-day',true);
14716 this.textNodes = this.el.query('.fc-day-number');
14717 this.cells.addClassOnOver('fc-state-hover');
14719 var cells = this.cells.elements;
14720 var textEls = this.textNodes;
14722 Roo.each(cells, function(cell){
14723 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14726 days += startingPos;
14728 // convert everything to numbers so it's fast
14729 var day = 86400000;
14730 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14733 //Roo.log(prevStart);
14735 var today = new Date().clearTime().getTime();
14736 var sel = date.clearTime().getTime();
14737 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14738 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14739 var ddMatch = this.disabledDatesRE;
14740 var ddText = this.disabledDatesText;
14741 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14742 var ddaysText = this.disabledDaysText;
14743 var format = this.format;
14745 var setCellClass = function(cal, cell){
14749 //Roo.log('set Cell Class');
14751 var t = d.getTime();
14755 cell.dateValue = t;
14757 cell.className += " fc-today";
14758 cell.className += " fc-state-highlight";
14759 cell.title = cal.todayText;
14762 // disable highlight in other month..
14763 //cell.className += " fc-state-highlight";
14768 cell.className = " fc-state-disabled";
14769 cell.title = cal.minText;
14773 cell.className = " fc-state-disabled";
14774 cell.title = cal.maxText;
14778 if(ddays.indexOf(d.getDay()) != -1){
14779 cell.title = ddaysText;
14780 cell.className = " fc-state-disabled";
14783 if(ddMatch && format){
14784 var fvalue = d.dateFormat(format);
14785 if(ddMatch.test(fvalue)){
14786 cell.title = ddText.replace("%0", fvalue);
14787 cell.className = " fc-state-disabled";
14791 if (!cell.initialClassName) {
14792 cell.initialClassName = cell.dom.className;
14795 cell.dom.className = cell.initialClassName + ' ' + cell.className;
14800 for(; i < startingPos; i++) {
14801 textEls[i].innerHTML = (++prevStart);
14802 d.setDate(d.getDate()+1);
14804 cells[i].className = "fc-past fc-other-month";
14805 setCellClass(this, cells[i]);
14810 for(; i < days; i++){
14811 intDay = i - startingPos + 1;
14812 textEls[i].innerHTML = (intDay);
14813 d.setDate(d.getDate()+1);
14815 cells[i].className = ''; // "x-date-active";
14816 setCellClass(this, cells[i]);
14820 for(; i < 42; i++) {
14821 textEls[i].innerHTML = (++extraDays);
14822 d.setDate(d.getDate()+1);
14824 cells[i].className = "fc-future fc-other-month";
14825 setCellClass(this, cells[i]);
14828 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14830 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14832 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14833 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14835 if(totalRows != 6){
14836 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14837 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14840 this.fireEvent('monthchange', this, date);
14844 if(!this.internalRender){
14845 var main = this.el.dom.firstChild;
14846 var w = main.offsetWidth;
14847 this.el.setWidth(w + this.el.getBorderWidth("lr"));
14848 Roo.fly(main).setWidth(w);
14849 this.internalRender = true;
14850 // opera does not respect the auto grow header center column
14851 // then, after it gets a width opera refuses to recalculate
14852 // without a second pass
14853 if(Roo.isOpera && !this.secondPass){
14854 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14855 this.secondPass = true;
14856 this.update.defer(10, this, [date]);
14863 findCell : function(dt) {
14864 dt = dt.clearTime().getTime();
14866 this.cells.each(function(c){
14867 //Roo.log("check " +c.dateValue + '?=' + dt);
14868 if(c.dateValue == dt){
14878 findCells : function(ev) {
14879 var s = ev.start.clone().clearTime().getTime();
14881 var e= ev.end.clone().clearTime().getTime();
14884 this.cells.each(function(c){
14885 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14887 if(c.dateValue > e){
14890 if(c.dateValue < s){
14899 // findBestRow: function(cells)
14903 // for (var i =0 ; i < cells.length;i++) {
14904 // ret = Math.max(cells[i].rows || 0,ret);
14911 addItem : function(ev)
14913 // look for vertical location slot in
14914 var cells = this.findCells(ev);
14916 // ev.row = this.findBestRow(cells);
14918 // work out the location.
14922 for(var i =0; i < cells.length; i++) {
14924 cells[i].row = cells[0].row;
14927 cells[i].row = cells[i].row + 1;
14937 if (crow.start.getY() == cells[i].getY()) {
14939 crow.end = cells[i];
14956 cells[0].events.push(ev);
14958 this.calevents.push(ev);
14961 clearEvents: function() {
14963 if(!this.calevents){
14967 Roo.each(this.cells.elements, function(c){
14973 Roo.each(this.calevents, function(e) {
14974 Roo.each(e.els, function(el) {
14975 el.un('mouseenter' ,this.onEventEnter, this);
14976 el.un('mouseleave' ,this.onEventLeave, this);
14981 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14987 renderEvents: function()
14991 this.cells.each(function(c) {
15000 if(c.row != c.events.length){
15001 r = 4 - (4 - (c.row - c.events.length));
15004 c.events = ev.slice(0, r);
15005 c.more = ev.slice(r);
15007 if(c.more.length && c.more.length == 1){
15008 c.events.push(c.more.pop());
15011 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15015 this.cells.each(function(c) {
15017 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15020 for (var e = 0; e < c.events.length; e++){
15021 var ev = c.events[e];
15022 var rows = ev.rows;
15024 for(var i = 0; i < rows.length; i++) {
15026 // how many rows should it span..
15029 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15030 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15032 unselectable : "on",
15035 cls: 'fc-event-inner',
15039 // cls: 'fc-event-time',
15040 // html : cells.length > 1 ? '' : ev.time
15044 cls: 'fc-event-title',
15045 html : String.format('{0}', ev.title)
15052 cls: 'ui-resizable-handle ui-resizable-e',
15053 html : '  '
15060 cfg.cls += ' fc-event-start';
15062 if ((i+1) == rows.length) {
15063 cfg.cls += ' fc-event-end';
15066 var ctr = _this.el.select('.fc-event-container',true).first();
15067 var cg = ctr.createChild(cfg);
15069 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15070 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15072 var r = (c.more.length) ? 1 : 0;
15073 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15074 cg.setWidth(ebox.right - sbox.x -2);
15076 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15077 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15078 cg.on('click', _this.onEventClick, _this, ev);
15089 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15090 style : 'position: absolute',
15091 unselectable : "on",
15094 cls: 'fc-event-inner',
15098 cls: 'fc-event-title',
15106 cls: 'ui-resizable-handle ui-resizable-e',
15107 html : '  '
15113 var ctr = _this.el.select('.fc-event-container',true).first();
15114 var cg = ctr.createChild(cfg);
15116 var sbox = c.select('.fc-day-content',true).first().getBox();
15117 var ebox = c.select('.fc-day-content',true).first().getBox();
15119 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15120 cg.setWidth(ebox.right - sbox.x -2);
15122 cg.on('click', _this.onMoreEventClick, _this, c.more);
15132 onEventEnter: function (e, el,event,d) {
15133 this.fireEvent('evententer', this, el, event);
15136 onEventLeave: function (e, el,event,d) {
15137 this.fireEvent('eventleave', this, el, event);
15140 onEventClick: function (e, el,event,d) {
15141 this.fireEvent('eventclick', this, el, event);
15144 onMonthChange: function () {
15148 onMoreEventClick: function(e, el, more)
15152 this.calpopover.placement = 'right';
15153 this.calpopover.setTitle('More');
15155 this.calpopover.setContent('');
15157 var ctr = this.calpopover.el.select('.popover-content', true).first();
15159 Roo.each(more, function(m){
15161 cls : 'fc-event-hori fc-event-draggable',
15164 var cg = ctr.createChild(cfg);
15166 cg.on('click', _this.onEventClick, _this, m);
15169 this.calpopover.show(el);
15174 onLoad: function ()
15176 this.calevents = [];
15179 if(this.store.getCount() > 0){
15180 this.store.data.each(function(d){
15183 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15184 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15185 time : d.data.start_time,
15186 title : d.data.title,
15187 description : d.data.description,
15188 venue : d.data.venue
15193 this.renderEvents();
15195 if(this.calevents.length && this.loadMask){
15196 this.maskEl.hide();
15200 onBeforeLoad: function()
15202 this.clearEvents();
15204 this.maskEl.show();
15218 * @class Roo.bootstrap.Popover
15219 * @extends Roo.bootstrap.Component
15220 * Bootstrap Popover class
15221 * @cfg {String} html contents of the popover (or false to use children..)
15222 * @cfg {String} title of popover (or false to hide)
15223 * @cfg {String} placement how it is placed
15224 * @cfg {String} trigger click || hover (or false to trigger manually)
15225 * @cfg {String} over what (parent or false to trigger manually.)
15226 * @cfg {Number} delay - delay before showing
15229 * Create a new Popover
15230 * @param {Object} config The config object
15233 Roo.bootstrap.Popover = function(config){
15234 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15237 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15239 title: 'Fill in a title',
15242 placement : 'right',
15243 trigger : 'hover', // hover
15249 can_build_overlaid : false,
15251 getChildContainer : function()
15253 return this.el.select('.popover-content',true).first();
15256 getAutoCreate : function(){
15257 Roo.log('make popover?');
15259 cls : 'popover roo-dynamic',
15260 style: 'display:block',
15266 cls : 'popover-inner',
15270 cls: 'popover-title',
15274 cls : 'popover-content',
15285 setTitle: function(str)
15288 this.el.select('.popover-title',true).first().dom.innerHTML = str;
15290 setContent: function(str)
15293 this.el.select('.popover-content',true).first().dom.innerHTML = str;
15295 // as it get's added to the bottom of the page.
15296 onRender : function(ct, position)
15298 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15300 var cfg = Roo.apply({}, this.getAutoCreate());
15304 cfg.cls += ' ' + this.cls;
15307 cfg.style = this.style;
15309 Roo.log("adding to ")
15310 this.el = Roo.get(document.body).createChild(cfg, position);
15316 initEvents : function()
15318 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15319 this.el.enableDisplayMode('block');
15321 if (this.over === false) {
15324 if (this.triggers === false) {
15327 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15328 var triggers = this.trigger ? this.trigger.split(' ') : [];
15329 Roo.each(triggers, function(trigger) {
15331 if (trigger == 'click') {
15332 on_el.on('click', this.toggle, this);
15333 } else if (trigger != 'manual') {
15334 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
15335 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15337 on_el.on(eventIn ,this.enter, this);
15338 on_el.on(eventOut, this.leave, this);
15349 toggle : function () {
15350 this.hoverState == 'in' ? this.leave() : this.enter();
15353 enter : function () {
15356 clearTimeout(this.timeout);
15358 this.hoverState = 'in';
15360 if (!this.delay || !this.delay.show) {
15365 this.timeout = setTimeout(function () {
15366 if (_t.hoverState == 'in') {
15369 }, this.delay.show)
15371 leave : function() {
15372 clearTimeout(this.timeout);
15374 this.hoverState = 'out';
15376 if (!this.delay || !this.delay.hide) {
15381 this.timeout = setTimeout(function () {
15382 if (_t.hoverState == 'out') {
15385 }, this.delay.hide)
15388 show : function (on_el)
15391 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15394 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15395 if (this.html !== false) {
15396 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15398 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15399 if (!this.title.length) {
15400 this.el.select('.popover-title',true).hide();
15403 var placement = typeof this.placement == 'function' ?
15404 this.placement.call(this, this.el, on_el) :
15407 var autoToken = /\s?auto?\s?/i;
15408 var autoPlace = autoToken.test(placement);
15410 placement = placement.replace(autoToken, '') || 'top';
15414 //this.el.setXY([0,0]);
15416 this.el.dom.style.display='block';
15417 this.el.addClass(placement);
15419 //this.el.appendTo(on_el);
15421 var p = this.getPosition();
15422 var box = this.el.getBox();
15427 var align = Roo.bootstrap.Popover.alignment[placement];
15428 this.el.alignTo(on_el, align[0],align[1]);
15429 //var arrow = this.el.select('.arrow',true).first();
15430 //arrow.set(align[2],
15432 this.el.addClass('in');
15435 if (this.el.hasClass('fade')) {
15442 this.el.setXY([0,0]);
15443 this.el.removeClass('in');
15445 this.hoverState = null;
15451 Roo.bootstrap.Popover.alignment = {
15452 'left' : ['r-l', [-10,0], 'right'],
15453 'right' : ['l-r', [10,0], 'left'],
15454 'bottom' : ['t-b', [0,10], 'top'],
15455 'top' : [ 'b-t', [0,-10], 'bottom']
15466 * @class Roo.bootstrap.Progress
15467 * @extends Roo.bootstrap.Component
15468 * Bootstrap Progress class
15469 * @cfg {Boolean} striped striped of the progress bar
15470 * @cfg {Boolean} active animated of the progress bar
15474 * Create a new Progress
15475 * @param {Object} config The config object
15478 Roo.bootstrap.Progress = function(config){
15479 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15482 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
15487 getAutoCreate : function(){
15495 cfg.cls += ' progress-striped';
15499 cfg.cls += ' active';
15518 * @class Roo.bootstrap.ProgressBar
15519 * @extends Roo.bootstrap.Component
15520 * Bootstrap ProgressBar class
15521 * @cfg {Number} aria_valuenow aria-value now
15522 * @cfg {Number} aria_valuemin aria-value min
15523 * @cfg {Number} aria_valuemax aria-value max
15524 * @cfg {String} label label for the progress bar
15525 * @cfg {String} panel (success | info | warning | danger )
15526 * @cfg {String} role role of the progress bar
15527 * @cfg {String} sr_only text
15531 * Create a new ProgressBar
15532 * @param {Object} config The config object
15535 Roo.bootstrap.ProgressBar = function(config){
15536 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15539 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
15543 aria_valuemax : 100,
15549 getAutoCreate : function()
15554 cls: 'progress-bar',
15555 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15567 cfg.role = this.role;
15570 if(this.aria_valuenow){
15571 cfg['aria-valuenow'] = this.aria_valuenow;
15574 if(this.aria_valuemin){
15575 cfg['aria-valuemin'] = this.aria_valuemin;
15578 if(this.aria_valuemax){
15579 cfg['aria-valuemax'] = this.aria_valuemax;
15582 if(this.label && !this.sr_only){
15583 cfg.html = this.label;
15587 cfg.cls += ' progress-bar-' + this.panel;
15593 update : function(aria_valuenow)
15595 this.aria_valuenow = aria_valuenow;
15597 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15612 * @class Roo.bootstrap.TabGroup
15613 * @extends Roo.bootstrap.Column
15614 * Bootstrap Column class
15615 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15616 * @cfg {Boolean} carousel true to make the group behave like a carousel
15617 * @cfg {Number} bullets show the panel pointer.. default 0
15618 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15619 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15620 * @cfg {Number} timer auto slide timer .. default 0 millisecond
15623 * Create a new TabGroup
15624 * @param {Object} config The config object
15627 Roo.bootstrap.TabGroup = function(config){
15628 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15630 this.navId = Roo.id();
15633 Roo.bootstrap.TabGroup.register(this);
15637 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
15640 transition : false,
15645 slideOnTouch : false,
15647 getAutoCreate : function()
15649 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15651 cfg.cls += ' tab-content';
15653 Roo.log('get auto create...............');
15655 if (this.carousel) {
15656 cfg.cls += ' carousel slide';
15659 cls : 'carousel-inner'
15662 if(this.bullets > 0 && !Roo.isTouch){
15665 cls : 'carousel-bullets',
15669 if(this.bullets_cls){
15670 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15673 for (var i = 0; i < this.bullets; i++){
15675 cls : 'bullet bullet-' + i
15683 cfg.cn[0].cn = bullets;
15690 initEvents: function()
15692 Roo.log('-------- init events on tab group ---------');
15694 if(this.bullets > 0 && !Roo.isTouch){
15700 if(Roo.isTouch && this.slideOnTouch){
15701 this.el.on("touchstart", this.onTouchStart, this);
15704 if(this.autoslide){
15707 this.slideFn = window.setInterval(function() {
15708 _this.showPanelNext();
15714 onTouchStart : function(e, el, o)
15716 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15720 this.showPanelNext();
15723 getChildContainer : function()
15725 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15729 * register a Navigation item
15730 * @param {Roo.bootstrap.NavItem} the navitem to add
15732 register : function(item)
15734 this.tabs.push( item);
15735 item.navId = this.navId; // not really needed..
15739 getActivePanel : function()
15742 Roo.each(this.tabs, function(t) {
15752 getPanelByName : function(n)
15755 Roo.each(this.tabs, function(t) {
15756 if (t.tabId == n) {
15764 indexOfPanel : function(p)
15767 Roo.each(this.tabs, function(t,i) {
15768 if (t.tabId == p.tabId) {
15777 * show a specific panel
15778 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15779 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15781 showPanel : function (pan)
15783 if(this.transition){
15784 Roo.log("waiting for the transitionend");
15788 if (typeof(pan) == 'number') {
15789 pan = this.tabs[pan];
15791 if (typeof(pan) == 'string') {
15792 pan = this.getPanelByName(pan);
15794 if (pan.tabId == this.getActivePanel().tabId) {
15797 var cur = this.getActivePanel();
15799 if (false === cur.fireEvent('beforedeactivate')) {
15803 if(this.bullets > 0 && !Roo.isTouch){
15804 this.setActiveBullet(this.indexOfPanel(pan));
15807 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15809 this.transition = true;
15810 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
15811 var lr = dir == 'next' ? 'left' : 'right';
15812 pan.el.addClass(dir); // or prev
15813 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15814 cur.el.addClass(lr); // or right
15815 pan.el.addClass(lr);
15818 cur.el.on('transitionend', function() {
15819 Roo.log("trans end?");
15821 pan.el.removeClass([lr,dir]);
15822 pan.setActive(true);
15824 cur.el.removeClass([lr]);
15825 cur.setActive(false);
15827 _this.transition = false;
15829 }, this, { single: true } );
15834 cur.setActive(false);
15835 pan.setActive(true);
15840 showPanelNext : function()
15842 var i = this.indexOfPanel(this.getActivePanel());
15844 if (i >= this.tabs.length - 1 && !this.autoslide) {
15848 if (i >= this.tabs.length - 1 && this.autoslide) {
15852 this.showPanel(this.tabs[i+1]);
15855 showPanelPrev : function()
15857 var i = this.indexOfPanel(this.getActivePanel());
15859 if (i < 1 && !this.autoslide) {
15863 if (i < 1 && this.autoslide) {
15864 i = this.tabs.length;
15867 this.showPanel(this.tabs[i-1]);
15870 initBullet : function()
15878 for (var i = 0; i < this.bullets; i++){
15879 var bullet = this.el.select('.bullet-' + i, true).first();
15885 bullet.on('click', (function(e, el, o, ii, t){
15887 e.preventDefault();
15889 _this.showPanel(ii);
15891 if(_this.autoslide && _this.slideFn){
15892 clearInterval(_this.slideFn);
15893 _this.slideFn = window.setInterval(function() {
15894 _this.showPanelNext();
15898 }).createDelegate(this, [i, bullet], true));
15902 setActiveBullet : function(i)
15908 Roo.each(this.el.select('.bullet', true).elements, function(el){
15909 el.removeClass('selected');
15912 var bullet = this.el.select('.bullet-' + i, true).first();
15918 bullet.addClass('selected');
15929 Roo.apply(Roo.bootstrap.TabGroup, {
15933 * register a Navigation Group
15934 * @param {Roo.bootstrap.NavGroup} the navgroup to add
15936 register : function(navgrp)
15938 this.groups[navgrp.navId] = navgrp;
15942 * fetch a Navigation Group based on the navigation ID
15943 * if one does not exist , it will get created.
15944 * @param {string} the navgroup to add
15945 * @returns {Roo.bootstrap.NavGroup} the navgroup
15947 get: function(navId) {
15948 if (typeof(this.groups[navId]) == 'undefined') {
15949 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15951 return this.groups[navId] ;
15966 * @class Roo.bootstrap.TabPanel
15967 * @extends Roo.bootstrap.Component
15968 * Bootstrap TabPanel class
15969 * @cfg {Boolean} active panel active
15970 * @cfg {String} html panel content
15971 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15972 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15976 * Create a new TabPanel
15977 * @param {Object} config The config object
15980 Roo.bootstrap.TabPanel = function(config){
15981 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15985 * Fires when the active status changes
15986 * @param {Roo.bootstrap.TabPanel} this
15987 * @param {Boolean} state the new state
15992 * @event beforedeactivate
15993 * Fires before a tab is de-activated - can be used to do validation on a form.
15994 * @param {Roo.bootstrap.TabPanel} this
15995 * @return {Boolean} false if there is an error
15998 'beforedeactivate': true
16001 this.tabId = this.tabId || Roo.id();
16005 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16012 getAutoCreate : function(){
16015 // item is needed for carousel - not sure if it has any effect otherwise
16016 cls: 'tab-pane item',
16017 html: this.html || ''
16021 cfg.cls += ' active';
16025 cfg.tabId = this.tabId;
16032 initEvents: function()
16034 Roo.log('-------- init events on tab panel ---------');
16036 var p = this.parent();
16037 this.navId = this.navId || p.navId;
16039 if (typeof(this.navId) != 'undefined') {
16040 // not really needed.. but just in case.. parent should be a NavGroup.
16041 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16042 Roo.log(['register', tg, this]);
16045 var i = tg.tabs.length - 1;
16047 if(this.active && tg.bullets > 0 && i < tg.bullets){
16048 tg.setActiveBullet(i);
16055 onRender : function(ct, position)
16057 // Roo.log("Call onRender: " + this.xtype);
16059 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16067 setActive: function(state)
16069 Roo.log("panel - set active " + this.tabId + "=" + state);
16071 this.active = state;
16073 this.el.removeClass('active');
16075 } else if (!this.el.hasClass('active')) {
16076 this.el.addClass('active');
16079 this.fireEvent('changed', this, state);
16096 * @class Roo.bootstrap.DateField
16097 * @extends Roo.bootstrap.Input
16098 * Bootstrap DateField class
16099 * @cfg {Number} weekStart default 0
16100 * @cfg {String} viewMode default empty, (months|years)
16101 * @cfg {String} minViewMode default empty, (months|years)
16102 * @cfg {Number} startDate default -Infinity
16103 * @cfg {Number} endDate default Infinity
16104 * @cfg {Boolean} todayHighlight default false
16105 * @cfg {Boolean} todayBtn default false
16106 * @cfg {Boolean} calendarWeeks default false
16107 * @cfg {Object} daysOfWeekDisabled default empty
16108 * @cfg {Boolean} singleMode default false (true | false)
16110 * @cfg {Boolean} keyboardNavigation default true
16111 * @cfg {String} language default en
16114 * Create a new DateField
16115 * @param {Object} config The config object
16118 Roo.bootstrap.DateField = function(config){
16119 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16123 * Fires when this field show.
16124 * @param {Roo.bootstrap.DateField} this
16125 * @param {Mixed} date The date value
16130 * Fires when this field hide.
16131 * @param {Roo.bootstrap.DateField} this
16132 * @param {Mixed} date The date value
16137 * Fires when select a date.
16138 * @param {Roo.bootstrap.DateField} this
16139 * @param {Mixed} date The date value
16145 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16148 * @cfg {String} format
16149 * The default date format string which can be overriden for localization support. The format must be
16150 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16154 * @cfg {String} altFormats
16155 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16156 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16158 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16166 todayHighlight : false,
16172 keyboardNavigation: true,
16174 calendarWeeks: false,
16176 startDate: -Infinity,
16180 daysOfWeekDisabled: [],
16184 singleMode : false,
16186 UTCDate: function()
16188 return new Date(Date.UTC.apply(Date, arguments));
16191 UTCToday: function()
16193 var today = new Date();
16194 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16197 getDate: function() {
16198 var d = this.getUTCDate();
16199 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16202 getUTCDate: function() {
16206 setDate: function(d) {
16207 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16210 setUTCDate: function(d) {
16212 this.setValue(this.formatDate(this.date));
16215 onRender: function(ct, position)
16218 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16220 this.language = this.language || 'en';
16221 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16222 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16224 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16225 this.format = this.format || 'm/d/y';
16226 this.isInline = false;
16227 this.isInput = true;
16228 this.component = this.el.select('.add-on', true).first() || false;
16229 this.component = (this.component && this.component.length === 0) ? false : this.component;
16230 this.hasInput = this.component && this.inputEL().length;
16232 if (typeof(this.minViewMode === 'string')) {
16233 switch (this.minViewMode) {
16235 this.minViewMode = 1;
16238 this.minViewMode = 2;
16241 this.minViewMode = 0;
16246 if (typeof(this.viewMode === 'string')) {
16247 switch (this.viewMode) {
16260 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16262 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16264 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16266 this.picker().on('mousedown', this.onMousedown, this);
16267 this.picker().on('click', this.onClick, this);
16269 this.picker().addClass('datepicker-dropdown');
16271 this.startViewMode = this.viewMode;
16273 if(this.singleMode){
16274 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16275 v.setVisibilityMode(Roo.Element.DISPLAY)
16279 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16280 v.setStyle('width', '189px');
16284 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16285 if(!this.calendarWeeks){
16290 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16291 v.attr('colspan', function(i, val){
16292 return parseInt(val) + 1;
16297 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16299 this.setStartDate(this.startDate);
16300 this.setEndDate(this.endDate);
16302 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16309 if(this.isInline) {
16314 picker : function()
16316 return this.pickerEl;
16317 // return this.el.select('.datepicker', true).first();
16320 fillDow: function()
16322 var dowCnt = this.weekStart;
16331 if(this.calendarWeeks){
16339 while (dowCnt < this.weekStart + 7) {
16343 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16347 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16350 fillMonths: function()
16353 var months = this.picker().select('>.datepicker-months td', true).first();
16355 months.dom.innerHTML = '';
16361 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16364 months.createChild(month);
16371 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;
16373 if (this.date < this.startDate) {
16374 this.viewDate = new Date(this.startDate);
16375 } else if (this.date > this.endDate) {
16376 this.viewDate = new Date(this.endDate);
16378 this.viewDate = new Date(this.date);
16386 var d = new Date(this.viewDate),
16387 year = d.getUTCFullYear(),
16388 month = d.getUTCMonth(),
16389 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16390 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16391 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16392 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16393 currentDate = this.date && this.date.valueOf(),
16394 today = this.UTCToday();
16396 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16398 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16400 // this.picker.select('>tfoot th.today').
16401 // .text(dates[this.language].today)
16402 // .toggle(this.todayBtn !== false);
16404 this.updateNavArrows();
16407 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16409 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16411 prevMonth.setUTCDate(day);
16413 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16415 var nextMonth = new Date(prevMonth);
16417 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16419 nextMonth = nextMonth.valueOf();
16421 var fillMonths = false;
16423 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16425 while(prevMonth.valueOf() < nextMonth) {
16428 if (prevMonth.getUTCDay() === this.weekStart) {
16430 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16438 if(this.calendarWeeks){
16439 // ISO 8601: First week contains first thursday.
16440 // ISO also states week starts on Monday, but we can be more abstract here.
16442 // Start of current week: based on weekstart/current date
16443 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16444 // Thursday of this week
16445 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16446 // First Thursday of year, year from thursday
16447 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16448 // Calendar week: ms between thursdays, div ms per day, div 7 days
16449 calWeek = (th - yth) / 864e5 / 7 + 1;
16451 fillMonths.cn.push({
16459 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16461 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16464 if (this.todayHighlight &&
16465 prevMonth.getUTCFullYear() == today.getFullYear() &&
16466 prevMonth.getUTCMonth() == today.getMonth() &&
16467 prevMonth.getUTCDate() == today.getDate()) {
16468 clsName += ' today';
16471 if (currentDate && prevMonth.valueOf() === currentDate) {
16472 clsName += ' active';
16475 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16476 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16477 clsName += ' disabled';
16480 fillMonths.cn.push({
16482 cls: 'day ' + clsName,
16483 html: prevMonth.getDate()
16486 prevMonth.setDate(prevMonth.getDate()+1);
16489 var currentYear = this.date && this.date.getUTCFullYear();
16490 var currentMonth = this.date && this.date.getUTCMonth();
16492 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16494 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16495 v.removeClass('active');
16497 if(currentYear === year && k === currentMonth){
16498 v.addClass('active');
16501 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16502 v.addClass('disabled');
16508 year = parseInt(year/10, 10) * 10;
16510 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16512 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16515 for (var i = -1; i < 11; i++) {
16516 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16518 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16526 showMode: function(dir)
16529 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16532 Roo.each(this.picker().select('>div',true).elements, function(v){
16533 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16536 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16541 if(this.isInline) return;
16543 this.picker().removeClass(['bottom', 'top']);
16545 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16547 * place to the top of element!
16551 this.picker().addClass('top');
16552 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16557 this.picker().addClass('bottom');
16559 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16562 parseDate : function(value)
16564 if(!value || value instanceof Date){
16567 var v = Date.parseDate(value, this.format);
16568 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16569 v = Date.parseDate(value, 'Y-m-d');
16571 if(!v && this.altFormats){
16572 if(!this.altFormatsArray){
16573 this.altFormatsArray = this.altFormats.split("|");
16575 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16576 v = Date.parseDate(value, this.altFormatsArray[i]);
16582 formatDate : function(date, fmt)
16584 return (!date || !(date instanceof Date)) ?
16585 date : date.dateFormat(fmt || this.format);
16588 onFocus : function()
16590 Roo.bootstrap.DateField.superclass.onFocus.call(this);
16594 onBlur : function()
16596 Roo.bootstrap.DateField.superclass.onBlur.call(this);
16598 var d = this.inputEl().getValue();
16607 this.picker().show();
16611 this.fireEvent('show', this, this.date);
16616 if(this.isInline) return;
16617 this.picker().hide();
16618 this.viewMode = this.startViewMode;
16621 this.fireEvent('hide', this, this.date);
16625 onMousedown: function(e)
16627 e.stopPropagation();
16628 e.preventDefault();
16633 Roo.bootstrap.DateField.superclass.keyup.call(this);
16637 setValue: function(v)
16640 // v can be a string or a date..
16643 var d = new Date(this.parseDate(v) ).clearTime();
16645 if(isNaN(d.getTime())){
16646 this.date = this.viewDate = '';
16647 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16651 v = this.formatDate(d);
16653 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16655 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16659 this.fireEvent('select', this, this.date);
16663 getValue: function()
16665 return this.formatDate(this.date);
16668 fireKey: function(e)
16670 if (!this.picker().isVisible()){
16671 if (e.keyCode == 27) // allow escape to hide and re-show picker
16676 var dateChanged = false,
16678 newDate, newViewDate;
16683 e.preventDefault();
16687 if (!this.keyboardNavigation) break;
16688 dir = e.keyCode == 37 ? -1 : 1;
16691 newDate = this.moveYear(this.date, dir);
16692 newViewDate = this.moveYear(this.viewDate, dir);
16693 } else if (e.shiftKey){
16694 newDate = this.moveMonth(this.date, dir);
16695 newViewDate = this.moveMonth(this.viewDate, dir);
16697 newDate = new Date(this.date);
16698 newDate.setUTCDate(this.date.getUTCDate() + dir);
16699 newViewDate = new Date(this.viewDate);
16700 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16702 if (this.dateWithinRange(newDate)){
16703 this.date = newDate;
16704 this.viewDate = newViewDate;
16705 this.setValue(this.formatDate(this.date));
16707 e.preventDefault();
16708 dateChanged = true;
16713 if (!this.keyboardNavigation) break;
16714 dir = e.keyCode == 38 ? -1 : 1;
16716 newDate = this.moveYear(this.date, dir);
16717 newViewDate = this.moveYear(this.viewDate, dir);
16718 } else if (e.shiftKey){
16719 newDate = this.moveMonth(this.date, dir);
16720 newViewDate = this.moveMonth(this.viewDate, dir);
16722 newDate = new Date(this.date);
16723 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16724 newViewDate = new Date(this.viewDate);
16725 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16727 if (this.dateWithinRange(newDate)){
16728 this.date = newDate;
16729 this.viewDate = newViewDate;
16730 this.setValue(this.formatDate(this.date));
16732 e.preventDefault();
16733 dateChanged = true;
16737 this.setValue(this.formatDate(this.date));
16739 e.preventDefault();
16742 this.setValue(this.formatDate(this.date));
16756 onClick: function(e)
16758 e.stopPropagation();
16759 e.preventDefault();
16761 var target = e.getTarget();
16763 if(target.nodeName.toLowerCase() === 'i'){
16764 target = Roo.get(target).dom.parentNode;
16767 var nodeName = target.nodeName;
16768 var className = target.className;
16769 var html = target.innerHTML;
16770 //Roo.log(nodeName);
16772 switch(nodeName.toLowerCase()) {
16774 switch(className) {
16780 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16781 switch(this.viewMode){
16783 this.viewDate = this.moveMonth(this.viewDate, dir);
16787 this.viewDate = this.moveYear(this.viewDate, dir);
16793 var date = new Date();
16794 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16796 this.setValue(this.formatDate(this.date));
16803 if (className.indexOf('disabled') < 0) {
16804 this.viewDate.setUTCDate(1);
16805 if (className.indexOf('month') > -1) {
16806 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16808 var year = parseInt(html, 10) || 0;
16809 this.viewDate.setUTCFullYear(year);
16813 if(this.singleMode){
16814 this.setValue(this.formatDate(this.viewDate));
16825 //Roo.log(className);
16826 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16827 var day = parseInt(html, 10) || 1;
16828 var year = this.viewDate.getUTCFullYear(),
16829 month = this.viewDate.getUTCMonth();
16831 if (className.indexOf('old') > -1) {
16838 } else if (className.indexOf('new') > -1) {
16846 //Roo.log([year,month,day]);
16847 this.date = this.UTCDate(year, month, day,0,0,0,0);
16848 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16850 //Roo.log(this.formatDate(this.date));
16851 this.setValue(this.formatDate(this.date));
16858 setStartDate: function(startDate)
16860 this.startDate = startDate || -Infinity;
16861 if (this.startDate !== -Infinity) {
16862 this.startDate = this.parseDate(this.startDate);
16865 this.updateNavArrows();
16868 setEndDate: function(endDate)
16870 this.endDate = endDate || Infinity;
16871 if (this.endDate !== Infinity) {
16872 this.endDate = this.parseDate(this.endDate);
16875 this.updateNavArrows();
16878 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16880 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16881 if (typeof(this.daysOfWeekDisabled) !== 'object') {
16882 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16884 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16885 return parseInt(d, 10);
16888 this.updateNavArrows();
16891 updateNavArrows: function()
16893 if(this.singleMode){
16897 var d = new Date(this.viewDate),
16898 year = d.getUTCFullYear(),
16899 month = d.getUTCMonth();
16901 Roo.each(this.picker().select('.prev', true).elements, function(v){
16903 switch (this.viewMode) {
16906 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16912 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16919 Roo.each(this.picker().select('.next', true).elements, function(v){
16921 switch (this.viewMode) {
16924 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16930 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16938 moveMonth: function(date, dir)
16940 if (!dir) return date;
16941 var new_date = new Date(date.valueOf()),
16942 day = new_date.getUTCDate(),
16943 month = new_date.getUTCMonth(),
16944 mag = Math.abs(dir),
16946 dir = dir > 0 ? 1 : -1;
16949 // If going back one month, make sure month is not current month
16950 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16952 return new_date.getUTCMonth() == month;
16954 // If going forward one month, make sure month is as expected
16955 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16957 return new_date.getUTCMonth() != new_month;
16959 new_month = month + dir;
16960 new_date.setUTCMonth(new_month);
16961 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16962 if (new_month < 0 || new_month > 11)
16963 new_month = (new_month + 12) % 12;
16965 // For magnitudes >1, move one month at a time...
16966 for (var i=0; i<mag; i++)
16967 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16968 new_date = this.moveMonth(new_date, dir);
16969 // ...then reset the day, keeping it in the new month
16970 new_month = new_date.getUTCMonth();
16971 new_date.setUTCDate(day);
16973 return new_month != new_date.getUTCMonth();
16976 // Common date-resetting loop -- if date is beyond end of month, make it
16979 new_date.setUTCDate(--day);
16980 new_date.setUTCMonth(new_month);
16985 moveYear: function(date, dir)
16987 return this.moveMonth(date, dir*12);
16990 dateWithinRange: function(date)
16992 return date >= this.startDate && date <= this.endDate;
16998 this.picker().remove();
17003 Roo.apply(Roo.bootstrap.DateField, {
17014 html: '<i class="fa fa-arrow-left"/>'
17024 html: '<i class="fa fa-arrow-right"/>'
17066 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17067 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17068 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17069 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17070 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17083 navFnc: 'FullYear',
17088 navFnc: 'FullYear',
17093 Roo.apply(Roo.bootstrap.DateField, {
17097 cls: 'datepicker dropdown-menu roo-dynamic',
17101 cls: 'datepicker-days',
17105 cls: 'table-condensed',
17107 Roo.bootstrap.DateField.head,
17111 Roo.bootstrap.DateField.footer
17118 cls: 'datepicker-months',
17122 cls: 'table-condensed',
17124 Roo.bootstrap.DateField.head,
17125 Roo.bootstrap.DateField.content,
17126 Roo.bootstrap.DateField.footer
17133 cls: 'datepicker-years',
17137 cls: 'table-condensed',
17139 Roo.bootstrap.DateField.head,
17140 Roo.bootstrap.DateField.content,
17141 Roo.bootstrap.DateField.footer
17160 * @class Roo.bootstrap.TimeField
17161 * @extends Roo.bootstrap.Input
17162 * Bootstrap DateField class
17166 * Create a new TimeField
17167 * @param {Object} config The config object
17170 Roo.bootstrap.TimeField = function(config){
17171 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17175 * Fires when this field show.
17176 * @param {Roo.bootstrap.DateField} thisthis
17177 * @param {Mixed} date The date value
17182 * Fires when this field hide.
17183 * @param {Roo.bootstrap.DateField} this
17184 * @param {Mixed} date The date value
17189 * Fires when select a date.
17190 * @param {Roo.bootstrap.DateField} this
17191 * @param {Mixed} date The date value
17197 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17200 * @cfg {String} format
17201 * The default time format string which can be overriden for localization support. The format must be
17202 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17206 onRender: function(ct, position)
17209 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17211 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17213 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17215 this.pop = this.picker().select('>.datepicker-time',true).first();
17216 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17218 this.picker().on('mousedown', this.onMousedown, this);
17219 this.picker().on('click', this.onClick, this);
17221 this.picker().addClass('datepicker-dropdown');
17226 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17227 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17228 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17229 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17230 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17231 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17235 fireKey: function(e){
17236 if (!this.picker().isVisible()){
17237 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17243 e.preventDefault();
17251 this.onTogglePeriod();
17254 this.onIncrementMinutes();
17257 this.onDecrementMinutes();
17266 onClick: function(e) {
17267 e.stopPropagation();
17268 e.preventDefault();
17271 picker : function()
17273 return this.el.select('.datepicker', true).first();
17276 fillTime: function()
17278 var time = this.pop.select('tbody', true).first();
17280 time.dom.innerHTML = '';
17295 cls: 'hours-up glyphicon glyphicon-chevron-up'
17315 cls: 'minutes-up glyphicon glyphicon-chevron-up'
17336 cls: 'timepicker-hour',
17351 cls: 'timepicker-minute',
17366 cls: 'btn btn-primary period',
17388 cls: 'hours-down glyphicon glyphicon-chevron-down'
17408 cls: 'minutes-down glyphicon glyphicon-chevron-down'
17426 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17433 var hours = this.time.getHours();
17434 var minutes = this.time.getMinutes();
17447 hours = hours - 12;
17451 hours = '0' + hours;
17455 minutes = '0' + minutes;
17458 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17459 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17460 this.pop.select('button', true).first().dom.innerHTML = period;
17466 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17468 var cls = ['bottom'];
17470 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17477 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17482 this.picker().addClass(cls.join('-'));
17486 Roo.each(cls, function(c){
17488 _this.picker().setTop(_this.inputEl().getHeight());
17492 _this.picker().setTop(0 - _this.picker().getHeight());
17497 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17501 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17508 onFocus : function()
17510 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17514 onBlur : function()
17516 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17522 this.picker().show();
17527 this.fireEvent('show', this, this.date);
17532 this.picker().hide();
17535 this.fireEvent('hide', this, this.date);
17538 setTime : function()
17541 this.setValue(this.time.format(this.format));
17543 this.fireEvent('select', this, this.date);
17548 onMousedown: function(e){
17549 e.stopPropagation();
17550 e.preventDefault();
17553 onIncrementHours: function()
17555 Roo.log('onIncrementHours');
17556 this.time = this.time.add(Date.HOUR, 1);
17561 onDecrementHours: function()
17563 Roo.log('onDecrementHours');
17564 this.time = this.time.add(Date.HOUR, -1);
17568 onIncrementMinutes: function()
17570 Roo.log('onIncrementMinutes');
17571 this.time = this.time.add(Date.MINUTE, 1);
17575 onDecrementMinutes: function()
17577 Roo.log('onDecrementMinutes');
17578 this.time = this.time.add(Date.MINUTE, -1);
17582 onTogglePeriod: function()
17584 Roo.log('onTogglePeriod');
17585 this.time = this.time.add(Date.HOUR, 12);
17592 Roo.apply(Roo.bootstrap.TimeField, {
17622 cls: 'btn btn-info ok',
17634 Roo.apply(Roo.bootstrap.TimeField, {
17638 cls: 'datepicker dropdown-menu',
17642 cls: 'datepicker-time',
17646 cls: 'table-condensed',
17648 Roo.bootstrap.TimeField.content,
17649 Roo.bootstrap.TimeField.footer
17668 * @class Roo.bootstrap.MonthField
17669 * @extends Roo.bootstrap.Input
17670 * Bootstrap MonthField class
17672 * @cfg {String} language default en
17675 * Create a new MonthField
17676 * @param {Object} config The config object
17679 Roo.bootstrap.MonthField = function(config){
17680 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17685 * Fires when this field show.
17686 * @param {Roo.bootstrap.MonthField} this
17687 * @param {Mixed} date The date value
17692 * Fires when this field hide.
17693 * @param {Roo.bootstrap.MonthField} this
17694 * @param {Mixed} date The date value
17699 * Fires when select a date.
17700 * @param {Roo.bootstrap.MonthField} this
17701 * @param {String} oldvalue The old value
17702 * @param {String} newvalue The new value
17708 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
17710 onRender: function(ct, position)
17713 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17715 this.language = this.language || 'en';
17716 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17717 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17719 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17720 this.isInline = false;
17721 this.isInput = true;
17722 this.component = this.el.select('.add-on', true).first() || false;
17723 this.component = (this.component && this.component.length === 0) ? false : this.component;
17724 this.hasInput = this.component && this.inputEL().length;
17726 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17728 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17730 this.picker().on('mousedown', this.onMousedown, this);
17731 this.picker().on('click', this.onClick, this);
17733 this.picker().addClass('datepicker-dropdown');
17735 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17736 v.setStyle('width', '189px');
17743 if(this.isInline) {
17749 setValue: function(v, suppressEvent)
17751 var o = this.getValue();
17753 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17757 if(suppressEvent !== true){
17758 this.fireEvent('select', this, o, v);
17763 getValue: function()
17768 onClick: function(e)
17770 e.stopPropagation();
17771 e.preventDefault();
17773 var target = e.getTarget();
17775 if(target.nodeName.toLowerCase() === 'i'){
17776 target = Roo.get(target).dom.parentNode;
17779 var nodeName = target.nodeName;
17780 var className = target.className;
17781 var html = target.innerHTML;
17783 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17787 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17789 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17795 picker : function()
17797 return this.pickerEl;
17800 fillMonths: function()
17803 var months = this.picker().select('>.datepicker-months td', true).first();
17805 months.dom.innerHTML = '';
17811 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17814 months.createChild(month);
17823 if(typeof(this.vIndex) == 'undefined' && this.value.length){
17824 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17827 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17828 e.removeClass('active');
17830 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17831 e.addClass('active');
17838 if(this.isInline) return;
17840 this.picker().removeClass(['bottom', 'top']);
17842 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17844 * place to the top of element!
17848 this.picker().addClass('top');
17849 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17854 this.picker().addClass('bottom');
17856 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17859 onFocus : function()
17861 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17865 onBlur : function()
17867 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17869 var d = this.inputEl().getValue();
17878 this.picker().show();
17879 this.picker().select('>.datepicker-months', true).first().show();
17883 this.fireEvent('show', this, this.date);
17888 if(this.isInline) return;
17889 this.picker().hide();
17890 this.fireEvent('hide', this, this.date);
17894 onMousedown: function(e)
17896 e.stopPropagation();
17897 e.preventDefault();
17902 Roo.bootstrap.MonthField.superclass.keyup.call(this);
17906 fireKey: function(e)
17908 if (!this.picker().isVisible()){
17909 if (e.keyCode == 27) // allow escape to hide and re-show picker
17919 e.preventDefault();
17923 dir = e.keyCode == 37 ? -1 : 1;
17925 this.vIndex = this.vIndex + dir;
17927 if(this.vIndex < 0){
17931 if(this.vIndex > 11){
17935 if(isNaN(this.vIndex)){
17939 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17945 dir = e.keyCode == 38 ? -1 : 1;
17947 this.vIndex = this.vIndex + dir * 4;
17949 if(this.vIndex < 0){
17953 if(this.vIndex > 11){
17957 if(isNaN(this.vIndex)){
17961 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17966 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17967 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17971 e.preventDefault();
17974 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17975 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17991 this.picker().remove();
17996 Roo.apply(Roo.bootstrap.MonthField, {
18015 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18016 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18021 Roo.apply(Roo.bootstrap.MonthField, {
18025 cls: 'datepicker dropdown-menu roo-dynamic',
18029 cls: 'datepicker-months',
18033 cls: 'table-condensed',
18035 Roo.bootstrap.DateField.content
18055 * @class Roo.bootstrap.CheckBox
18056 * @extends Roo.bootstrap.Input
18057 * Bootstrap CheckBox class
18059 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18060 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18061 * @cfg {String} boxLabel The text that appears beside the checkbox
18062 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18063 * @cfg {Boolean} checked initnal the element
18064 * @cfg {Boolean} inline inline the element (default false)
18065 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18068 * Create a new CheckBox
18069 * @param {Object} config The config object
18072 Roo.bootstrap.CheckBox = function(config){
18073 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18078 * Fires when the element is checked or unchecked.
18079 * @param {Roo.bootstrap.CheckBox} this This input
18080 * @param {Boolean} checked The new checked value
18087 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18089 inputType: 'checkbox',
18097 getAutoCreate : function()
18099 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18105 cfg.cls = 'form-group ' + this.inputType; //input-group
18108 cfg.cls += ' ' + this.inputType + '-inline';
18114 type : this.inputType,
18115 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18116 cls : 'roo-' + this.inputType, //'form-box',
18117 placeholder : this.placeholder || ''
18121 if (this.weight) { // Validity check?
18122 cfg.cls += " " + this.inputType + "-" + this.weight;
18125 if (this.disabled) {
18126 input.disabled=true;
18130 input.checked = this.checked;
18134 input.name = this.name;
18138 input.cls += ' input-' + this.size;
18143 ['xs','sm','md','lg'].map(function(size){
18144 if (settings[size]) {
18145 cfg.cls += ' col-' + size + '-' + settings[size];
18149 var inputblock = input;
18151 if (this.before || this.after) {
18154 cls : 'input-group',
18159 inputblock.cn.push({
18161 cls : 'input-group-addon',
18166 inputblock.cn.push(input);
18169 inputblock.cn.push({
18171 cls : 'input-group-addon',
18178 if (align ==='left' && this.fieldLabel.length) {
18179 Roo.log("left and has label");
18185 cls : 'control-label col-md-' + this.labelWidth,
18186 html : this.fieldLabel
18190 cls : "col-md-" + (12 - this.labelWidth),
18197 } else if ( this.fieldLabel.length) {
18202 tag: this.boxLabel ? 'span' : 'label',
18204 cls: 'control-label box-input-label',
18205 //cls : 'input-group-addon',
18206 html : this.fieldLabel
18216 Roo.log(" no label && no align");
18217 cfg.cn = [ inputblock ] ;
18222 var boxLabelCfg = {
18224 //'for': id, // box label is handled by onclick - so no for...
18226 html: this.boxLabel
18230 boxLabelCfg.tooltip = this.tooltip;
18233 cfg.cn.push(boxLabelCfg);
18243 * return the real input element.
18245 inputEl: function ()
18247 return this.el.select('input.roo-' + this.inputType,true).first();
18250 labelEl: function()
18252 return this.el.select('label.control-label',true).first();
18254 /* depricated... */
18258 return this.labelEl();
18261 initEvents : function()
18263 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18265 this.inputEl().on('click', this.onClick, this);
18267 if (this.boxLabel) {
18268 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
18271 this.startValue = this.getValue();
18274 Roo.bootstrap.CheckBox.register(this);
18278 onClick : function()
18280 this.setChecked(!this.checked);
18283 setChecked : function(state,suppressEvent)
18285 this.startValue = this.getValue();
18287 if(this.inputType == 'radio'){
18289 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18290 e.dom.checked = false;
18293 this.inputEl().dom.checked = true;
18295 this.inputEl().dom.value = this.inputValue;
18297 if(suppressEvent !== true){
18298 this.fireEvent('check', this, true);
18306 this.checked = state;
18308 this.inputEl().dom.checked = state;
18310 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18312 if(suppressEvent !== true){
18313 this.fireEvent('check', this, state);
18319 getValue : function()
18321 if(this.inputType == 'radio'){
18322 return this.getGroupValue();
18325 return this.inputEl().getValue();
18329 getGroupValue : function()
18331 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18335 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18338 setValue : function(v,suppressEvent)
18340 if(this.inputType == 'radio'){
18341 this.setGroupValue(v, suppressEvent);
18345 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18350 setGroupValue : function(v, suppressEvent)
18352 this.startValue = this.getValue();
18354 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18355 e.dom.checked = false;
18357 if(e.dom.value == v){
18358 e.dom.checked = true;
18362 if(suppressEvent !== true){
18363 this.fireEvent('check', this, true);
18371 validate : function()
18375 (this.inputType == 'radio' && this.validateRadio()) ||
18376 (this.inputType == 'checkbox' && this.validateCheckbox())
18382 this.markInvalid();
18386 validateRadio : function()
18390 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18391 if(!e.dom.checked){
18403 validateCheckbox : function()
18406 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18409 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18417 for(var i in group){
18422 r = (group[i].getValue() == group[i].inputValue) ? true : false;
18429 * Mark this field as valid
18431 markValid : function()
18433 if(this.allowBlank){
18439 this.fireEvent('valid', this);
18441 if(this.inputType == 'radio'){
18442 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18443 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18444 e.findParent('.form-group', false, true).addClass(_this.validClass);
18451 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18452 this.el.findParent('.form-group', false, true).addClass(this.validClass);
18456 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18462 for(var i in group){
18463 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18464 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18469 * Mark this field as invalid
18470 * @param {String} msg The validation message
18472 markInvalid : function(msg)
18474 if(this.allowBlank){
18480 this.fireEvent('invalid', this, msg);
18482 if(this.inputType == 'radio'){
18483 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18484 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18485 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18492 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18493 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18497 var group = Roo.bootstrap.CheckBox.get(this.groupId);
18503 for(var i in group){
18504 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18505 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18512 Roo.apply(Roo.bootstrap.CheckBox, {
18517 * register a CheckBox Group
18518 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18520 register : function(checkbox)
18522 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18523 this.groups[checkbox.groupId] = {};
18526 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18530 this.groups[checkbox.groupId][checkbox.name] = checkbox;
18534 * fetch a CheckBox Group based on the group ID
18535 * @param {string} the group ID
18536 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18538 get: function(groupId) {
18539 if (typeof(this.groups[groupId]) == 'undefined') {
18543 return this.groups[groupId] ;
18555 *<div class="radio">
18557 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18558 Option one is this and that—be sure to include why it's great
18565 *<label class="radio-inline">fieldLabel</label>
18566 *<label class="radio-inline">
18567 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18575 * @class Roo.bootstrap.Radio
18576 * @extends Roo.bootstrap.CheckBox
18577 * Bootstrap Radio class
18580 * Create a new Radio
18581 * @param {Object} config The config object
18584 Roo.bootstrap.Radio = function(config){
18585 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18589 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
18591 inputType: 'radio',
18595 getAutoCreate : function()
18597 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18598 align = align || 'left'; // default...
18605 tag : this.inline ? 'span' : 'div',
18610 var inline = this.inline ? ' radio-inline' : '';
18614 // does not need for, as we wrap the input with it..
18616 cls : 'control-label box-label' + inline,
18619 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18623 //cls : 'control-label' + inline,
18624 html : this.fieldLabel,
18625 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18634 type : this.inputType,
18635 //value : (!this.checked) ? this.valueOff : this.inputValue,
18636 value : this.inputValue,
18638 placeholder : this.placeholder || '' // ?? needed????
18641 if (this.weight) { // Validity check?
18642 input.cls += " radio-" + this.weight;
18644 if (this.disabled) {
18645 input.disabled=true;
18649 input.checked = this.checked;
18653 input.name = this.name;
18657 input.cls += ' input-' + this.size;
18660 //?? can span's inline have a width??
18663 ['xs','sm','md','lg'].map(function(size){
18664 if (settings[size]) {
18665 cfg.cls += ' col-' + size + '-' + settings[size];
18669 var inputblock = input;
18671 if (this.before || this.after) {
18674 cls : 'input-group',
18679 inputblock.cn.push({
18681 cls : 'input-group-addon',
18685 inputblock.cn.push(input);
18687 inputblock.cn.push({
18689 cls : 'input-group-addon',
18697 if (this.fieldLabel && this.fieldLabel.length) {
18698 cfg.cn.push(fieldLabel);
18701 // normal bootstrap puts the input inside the label.
18702 // however with our styled version - it has to go after the input.
18704 //lbl.cn.push(inputblock);
18708 cls: 'radio' + inline,
18715 cfg.cn.push( lblwrap);
18720 html: this.boxLabel
18729 initEvents : function()
18731 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18733 this.inputEl().on('click', this.onClick, this);
18734 if (this.boxLabel) {
18735 Roo.log('find label')
18736 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
18741 inputEl: function ()
18743 return this.el.select('input.roo-radio',true).first();
18745 onClick : function()
18748 this.setChecked(true);
18751 setChecked : function(state,suppressEvent)
18754 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18755 v.dom.checked = false;
18758 Roo.log(this.inputEl().dom);
18759 this.checked = state;
18760 this.inputEl().dom.checked = state;
18762 if(suppressEvent !== true){
18763 this.fireEvent('check', this, state);
18766 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18770 getGroupValue : function()
18773 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18774 if(v.dom.checked == true){
18775 value = v.dom.value;
18783 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18784 * @return {Mixed} value The field value
18786 getValue : function(){
18787 return this.getGroupValue();
18793 //<script type="text/javascript">
18796 * Based Ext JS Library 1.1.1
18797 * Copyright(c) 2006-2007, Ext JS, LLC.
18803 * @class Roo.HtmlEditorCore
18804 * @extends Roo.Component
18805 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18807 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18810 Roo.HtmlEditorCore = function(config){
18813 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18818 * @event initialize
18819 * Fires when the editor is fully initialized (including the iframe)
18820 * @param {Roo.HtmlEditorCore} this
18825 * Fires when the editor is first receives the focus. Any insertion must wait
18826 * until after this event.
18827 * @param {Roo.HtmlEditorCore} this
18831 * @event beforesync
18832 * Fires before the textarea is updated with content from the editor iframe. Return false
18833 * to cancel the sync.
18834 * @param {Roo.HtmlEditorCore} this
18835 * @param {String} html
18839 * @event beforepush
18840 * Fires before the iframe editor is updated with content from the textarea. Return false
18841 * to cancel the push.
18842 * @param {Roo.HtmlEditorCore} this
18843 * @param {String} html
18848 * Fires when the textarea is updated with content from the editor iframe.
18849 * @param {Roo.HtmlEditorCore} this
18850 * @param {String} html
18855 * Fires when the iframe editor is updated with content from the textarea.
18856 * @param {Roo.HtmlEditorCore} this
18857 * @param {String} html
18862 * @event editorevent
18863 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18864 * @param {Roo.HtmlEditorCore} this
18870 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18872 // defaults : white / black...
18873 this.applyBlacklists();
18880 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
18884 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
18890 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18895 * @cfg {Number} height (in pixels)
18899 * @cfg {Number} width (in pixels)
18904 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18907 stylesheets: false,
18912 // private properties
18913 validationEvent : false,
18915 initialized : false,
18917 sourceEditMode : false,
18918 onFocus : Roo.emptyFn,
18920 hideMode:'offsets',
18924 // blacklist + whitelisted elements..
18931 * Protected method that will not generally be called directly. It
18932 * is called when the editor initializes the iframe with HTML contents. Override this method if you
18933 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18935 getDocMarkup : function(){
18939 // inherit styels from page...??
18940 if (this.stylesheets === false) {
18942 Roo.get(document.head).select('style').each(function(node) {
18943 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18946 Roo.get(document.head).select('link').each(function(node) {
18947 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18950 } else if (!this.stylesheets.length) {
18952 st = '<style type="text/css">' +
18953 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18959 st += '<style type="text/css">' +
18960 'IMG { cursor: pointer } ' +
18964 return '<html><head>' + st +
18965 //<style type="text/css">' +
18966 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18968 ' </head><body class="roo-htmleditor-body"></body></html>';
18972 onRender : function(ct, position)
18975 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18976 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18979 this.el.dom.style.border = '0 none';
18980 this.el.dom.setAttribute('tabIndex', -1);
18981 this.el.addClass('x-hidden hide');
18985 if(Roo.isIE){ // fix IE 1px bogus margin
18986 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18990 this.frameId = Roo.id();
18994 var iframe = this.owner.wrap.createChild({
18996 cls: 'form-control', // bootstrap..
18998 name: this.frameId,
18999 frameBorder : 'no',
19000 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19005 this.iframe = iframe.dom;
19007 this.assignDocWin();
19009 this.doc.designMode = 'on';
19012 this.doc.write(this.getDocMarkup());
19016 var task = { // must defer to wait for browser to be ready
19018 //console.log("run task?" + this.doc.readyState);
19019 this.assignDocWin();
19020 if(this.doc.body || this.doc.readyState == 'complete'){
19022 this.doc.designMode="on";
19026 Roo.TaskMgr.stop(task);
19027 this.initEditor.defer(10, this);
19034 Roo.TaskMgr.start(task);
19039 onResize : function(w, h)
19041 Roo.log('resize: ' +w + ',' + h );
19042 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19046 if(typeof w == 'number'){
19048 this.iframe.style.width = w + 'px';
19050 if(typeof h == 'number'){
19052 this.iframe.style.height = h + 'px';
19054 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19061 * Toggles the editor between standard and source edit mode.
19062 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19064 toggleSourceEdit : function(sourceEditMode){
19066 this.sourceEditMode = sourceEditMode === true;
19068 if(this.sourceEditMode){
19070 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19073 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19074 //this.iframe.className = '';
19077 //this.setSize(this.owner.wrap.getSize());
19078 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19085 * Protected method that will not generally be called directly. If you need/want
19086 * custom HTML cleanup, this is the method you should override.
19087 * @param {String} html The HTML to be cleaned
19088 * return {String} The cleaned HTML
19090 cleanHtml : function(html){
19091 html = String(html);
19092 if(html.length > 5){
19093 if(Roo.isSafari){ // strip safari nonsense
19094 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19097 if(html == ' '){
19104 * HTML Editor -> Textarea
19105 * Protected method that will not generally be called directly. Syncs the contents
19106 * of the editor iframe with the textarea.
19108 syncValue : function(){
19109 if(this.initialized){
19110 var bd = (this.doc.body || this.doc.documentElement);
19111 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19112 var html = bd.innerHTML;
19114 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19115 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19117 html = '<div style="'+m[0]+'">' + html + '</div>';
19120 html = this.cleanHtml(html);
19121 // fix up the special chars.. normaly like back quotes in word...
19122 // however we do not want to do this with chinese..
19123 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19124 var cc = b.charCodeAt();
19126 (cc >= 0x4E00 && cc < 0xA000 ) ||
19127 (cc >= 0x3400 && cc < 0x4E00 ) ||
19128 (cc >= 0xf900 && cc < 0xfb00 )
19134 if(this.owner.fireEvent('beforesync', this, html) !== false){
19135 this.el.dom.value = html;
19136 this.owner.fireEvent('sync', this, html);
19142 * Protected method that will not generally be called directly. Pushes the value of the textarea
19143 * into the iframe editor.
19145 pushValue : function(){
19146 if(this.initialized){
19147 var v = this.el.dom.value.trim();
19149 // if(v.length < 1){
19153 if(this.owner.fireEvent('beforepush', this, v) !== false){
19154 var d = (this.doc.body || this.doc.documentElement);
19156 this.cleanUpPaste();
19157 this.el.dom.value = d.innerHTML;
19158 this.owner.fireEvent('push', this, v);
19164 deferFocus : function(){
19165 this.focus.defer(10, this);
19169 focus : function(){
19170 if(this.win && !this.sourceEditMode){
19177 assignDocWin: function()
19179 var iframe = this.iframe;
19182 this.doc = iframe.contentWindow.document;
19183 this.win = iframe.contentWindow;
19185 // if (!Roo.get(this.frameId)) {
19188 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19189 // this.win = Roo.get(this.frameId).dom.contentWindow;
19191 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19195 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19196 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19201 initEditor : function(){
19202 //console.log("INIT EDITOR");
19203 this.assignDocWin();
19207 this.doc.designMode="on";
19209 this.doc.write(this.getDocMarkup());
19212 var dbody = (this.doc.body || this.doc.documentElement);
19213 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19214 // this copies styles from the containing element into thsi one..
19215 // not sure why we need all of this..
19216 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19218 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19219 //ss['background-attachment'] = 'fixed'; // w3c
19220 dbody.bgProperties = 'fixed'; // ie
19221 //Roo.DomHelper.applyStyles(dbody, ss);
19222 Roo.EventManager.on(this.doc, {
19223 //'mousedown': this.onEditorEvent,
19224 'mouseup': this.onEditorEvent,
19225 'dblclick': this.onEditorEvent,
19226 'click': this.onEditorEvent,
19227 'keyup': this.onEditorEvent,
19232 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19234 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19235 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19237 this.initialized = true;
19239 this.owner.fireEvent('initialize', this);
19244 onDestroy : function(){
19250 //for (var i =0; i < this.toolbars.length;i++) {
19251 // // fixme - ask toolbars for heights?
19252 // this.toolbars[i].onDestroy();
19255 //this.wrap.dom.innerHTML = '';
19256 //this.wrap.remove();
19261 onFirstFocus : function(){
19263 this.assignDocWin();
19266 this.activated = true;
19269 if(Roo.isGecko){ // prevent silly gecko errors
19271 var s = this.win.getSelection();
19272 if(!s.focusNode || s.focusNode.nodeType != 3){
19273 var r = s.getRangeAt(0);
19274 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19279 this.execCmd('useCSS', true);
19280 this.execCmd('styleWithCSS', false);
19283 this.owner.fireEvent('activate', this);
19287 adjustFont: function(btn){
19288 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19289 //if(Roo.isSafari){ // safari
19292 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19293 if(Roo.isSafari){ // safari
19294 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19295 v = (v < 10) ? 10 : v;
19296 v = (v > 48) ? 48 : v;
19297 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19302 v = Math.max(1, v+adjust);
19304 this.execCmd('FontSize', v );
19307 onEditorEvent : function(e)
19309 this.owner.fireEvent('editorevent', this, e);
19310 // this.updateToolbar();
19311 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19314 insertTag : function(tg)
19316 // could be a bit smarter... -> wrap the current selected tRoo..
19317 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19319 range = this.createRange(this.getSelection());
19320 var wrappingNode = this.doc.createElement(tg.toLowerCase());
19321 wrappingNode.appendChild(range.extractContents());
19322 range.insertNode(wrappingNode);
19329 this.execCmd("formatblock", tg);
19333 insertText : function(txt)
19337 var range = this.createRange();
19338 range.deleteContents();
19339 //alert(Sender.getAttribute('label'));
19341 range.insertNode(this.doc.createTextNode(txt));
19347 * Executes a Midas editor command on the editor document and performs necessary focus and
19348 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19349 * @param {String} cmd The Midas command
19350 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19352 relayCmd : function(cmd, value){
19354 this.execCmd(cmd, value);
19355 this.owner.fireEvent('editorevent', this);
19356 //this.updateToolbar();
19357 this.owner.deferFocus();
19361 * Executes a Midas editor command directly on the editor document.
19362 * For visual commands, you should use {@link #relayCmd} instead.
19363 * <b>This should only be called after the editor is initialized.</b>
19364 * @param {String} cmd The Midas command
19365 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19367 execCmd : function(cmd, value){
19368 this.doc.execCommand(cmd, false, value === undefined ? null : value);
19375 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19377 * @param {String} text | dom node..
19379 insertAtCursor : function(text)
19384 if(!this.activated){
19390 var r = this.doc.selection.createRange();
19401 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19405 // from jquery ui (MIT licenced)
19407 var win = this.win;
19409 if (win.getSelection && win.getSelection().getRangeAt) {
19410 range = win.getSelection().getRangeAt(0);
19411 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19412 range.insertNode(node);
19413 } else if (win.document.selection && win.document.selection.createRange) {
19414 // no firefox support
19415 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19416 win.document.selection.createRange().pasteHTML(txt);
19418 // no firefox support
19419 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19420 this.execCmd('InsertHTML', txt);
19429 mozKeyPress : function(e){
19431 var c = e.getCharCode(), cmd;
19434 c = String.fromCharCode(c).toLowerCase();
19448 this.cleanUpPaste.defer(100, this);
19456 e.preventDefault();
19464 fixKeys : function(){ // load time branching for fastest keydown performance
19466 return function(e){
19467 var k = e.getKey(), r;
19470 r = this.doc.selection.createRange();
19473 r.pasteHTML('    ');
19480 r = this.doc.selection.createRange();
19482 var target = r.parentElement();
19483 if(!target || target.tagName.toLowerCase() != 'li'){
19485 r.pasteHTML('<br />');
19491 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19492 this.cleanUpPaste.defer(100, this);
19498 }else if(Roo.isOpera){
19499 return function(e){
19500 var k = e.getKey();
19504 this.execCmd('InsertHTML','    ');
19507 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19508 this.cleanUpPaste.defer(100, this);
19513 }else if(Roo.isSafari){
19514 return function(e){
19515 var k = e.getKey();
19519 this.execCmd('InsertText','\t');
19523 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19524 this.cleanUpPaste.defer(100, this);
19532 getAllAncestors: function()
19534 var p = this.getSelectedNode();
19537 a.push(p); // push blank onto stack..
19538 p = this.getParentElement();
19542 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19546 a.push(this.doc.body);
19550 lastSelNode : false,
19553 getSelection : function()
19555 this.assignDocWin();
19556 return Roo.isIE ? this.doc.selection : this.win.getSelection();
19559 getSelectedNode: function()
19561 // this may only work on Gecko!!!
19563 // should we cache this!!!!
19568 var range = this.createRange(this.getSelection()).cloneRange();
19571 var parent = range.parentElement();
19573 var testRange = range.duplicate();
19574 testRange.moveToElementText(parent);
19575 if (testRange.inRange(range)) {
19578 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19581 parent = parent.parentElement;
19586 // is ancestor a text element.
19587 var ac = range.commonAncestorContainer;
19588 if (ac.nodeType == 3) {
19589 ac = ac.parentNode;
19592 var ar = ac.childNodes;
19595 var other_nodes = [];
19596 var has_other_nodes = false;
19597 for (var i=0;i<ar.length;i++) {
19598 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
19601 // fullly contained node.
19603 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19608 // probably selected..
19609 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19610 other_nodes.push(ar[i]);
19614 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
19619 has_other_nodes = true;
19621 if (!nodes.length && other_nodes.length) {
19622 nodes= other_nodes;
19624 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19630 createRange: function(sel)
19632 // this has strange effects when using with
19633 // top toolbar - not sure if it's a great idea.
19634 //this.editor.contentWindow.focus();
19635 if (typeof sel != "undefined") {
19637 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19639 return this.doc.createRange();
19642 return this.doc.createRange();
19645 getParentElement: function()
19648 this.assignDocWin();
19649 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19651 var range = this.createRange(sel);
19654 var p = range.commonAncestorContainer;
19655 while (p.nodeType == 3) { // text node
19666 * Range intersection.. the hard stuff...
19670 * [ -- selected range --- ]
19674 * if end is before start or hits it. fail.
19675 * if start is after end or hits it fail.
19677 * if either hits (but other is outside. - then it's not
19683 // @see http://www.thismuchiknow.co.uk/?p=64.
19684 rangeIntersectsNode : function(range, node)
19686 var nodeRange = node.ownerDocument.createRange();
19688 nodeRange.selectNode(node);
19690 nodeRange.selectNodeContents(node);
19693 var rangeStartRange = range.cloneRange();
19694 rangeStartRange.collapse(true);
19696 var rangeEndRange = range.cloneRange();
19697 rangeEndRange.collapse(false);
19699 var nodeStartRange = nodeRange.cloneRange();
19700 nodeStartRange.collapse(true);
19702 var nodeEndRange = nodeRange.cloneRange();
19703 nodeEndRange.collapse(false);
19705 return rangeStartRange.compareBoundaryPoints(
19706 Range.START_TO_START, nodeEndRange) == -1 &&
19707 rangeEndRange.compareBoundaryPoints(
19708 Range.START_TO_START, nodeStartRange) == 1;
19712 rangeCompareNode : function(range, node)
19714 var nodeRange = node.ownerDocument.createRange();
19716 nodeRange.selectNode(node);
19718 nodeRange.selectNodeContents(node);
19722 range.collapse(true);
19724 nodeRange.collapse(true);
19726 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19727 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
19729 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19731 var nodeIsBefore = ss == 1;
19732 var nodeIsAfter = ee == -1;
19734 if (nodeIsBefore && nodeIsAfter)
19736 if (!nodeIsBefore && nodeIsAfter)
19737 return 1; //right trailed.
19739 if (nodeIsBefore && !nodeIsAfter)
19740 return 2; // left trailed.
19745 // private? - in a new class?
19746 cleanUpPaste : function()
19748 // cleans up the whole document..
19749 Roo.log('cleanuppaste');
19751 this.cleanUpChildren(this.doc.body);
19752 var clean = this.cleanWordChars(this.doc.body.innerHTML);
19753 if (clean != this.doc.body.innerHTML) {
19754 this.doc.body.innerHTML = clean;
19759 cleanWordChars : function(input) {// change the chars to hex code
19760 var he = Roo.HtmlEditorCore;
19762 var output = input;
19763 Roo.each(he.swapCodes, function(sw) {
19764 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19766 output = output.replace(swapper, sw[1]);
19773 cleanUpChildren : function (n)
19775 if (!n.childNodes.length) {
19778 for (var i = n.childNodes.length-1; i > -1 ; i--) {
19779 this.cleanUpChild(n.childNodes[i]);
19786 cleanUpChild : function (node)
19789 //console.log(node);
19790 if (node.nodeName == "#text") {
19791 // clean up silly Windows -- stuff?
19794 if (node.nodeName == "#comment") {
19795 node.parentNode.removeChild(node);
19796 // clean up silly Windows -- stuff?
19799 var lcname = node.tagName.toLowerCase();
19800 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19801 // whitelist of tags..
19803 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19805 node.parentNode.removeChild(node);
19810 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19812 // remove <a name=....> as rendering on yahoo mailer is borked with this.
19813 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19815 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19816 // remove_keep_children = true;
19819 if (remove_keep_children) {
19820 this.cleanUpChildren(node);
19821 // inserts everything just before this node...
19822 while (node.childNodes.length) {
19823 var cn = node.childNodes[0];
19824 node.removeChild(cn);
19825 node.parentNode.insertBefore(cn, node);
19827 node.parentNode.removeChild(node);
19831 if (!node.attributes || !node.attributes.length) {
19832 this.cleanUpChildren(node);
19836 function cleanAttr(n,v)
19839 if (v.match(/^\./) || v.match(/^\//)) {
19842 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19845 if (v.match(/^#/)) {
19848 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19849 node.removeAttribute(n);
19853 var cwhite = this.cwhite;
19854 var cblack = this.cblack;
19856 function cleanStyle(n,v)
19858 if (v.match(/expression/)) { //XSS?? should we even bother..
19859 node.removeAttribute(n);
19863 var parts = v.split(/;/);
19866 Roo.each(parts, function(p) {
19867 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19871 var l = p.split(':').shift().replace(/\s+/g,'');
19872 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19874 if ( cwhite.length && cblack.indexOf(l) > -1) {
19875 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19876 //node.removeAttribute(n);
19880 // only allow 'c whitelisted system attributes'
19881 if ( cwhite.length && cwhite.indexOf(l) < 0) {
19882 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19883 //node.removeAttribute(n);
19893 if (clean.length) {
19894 node.setAttribute(n, clean.join(';'));
19896 node.removeAttribute(n);
19902 for (var i = node.attributes.length-1; i > -1 ; i--) {
19903 var a = node.attributes[i];
19906 if (a.name.toLowerCase().substr(0,2)=='on') {
19907 node.removeAttribute(a.name);
19910 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19911 node.removeAttribute(a.name);
19914 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19915 cleanAttr(a.name,a.value); // fixme..
19918 if (a.name == 'style') {
19919 cleanStyle(a.name,a.value);
19922 /// clean up MS crap..
19923 // tecnically this should be a list of valid class'es..
19926 if (a.name == 'class') {
19927 if (a.value.match(/^Mso/)) {
19928 node.className = '';
19931 if (a.value.match(/body/)) {
19932 node.className = '';
19943 this.cleanUpChildren(node);
19949 * Clean up MS wordisms...
19951 cleanWord : function(node)
19956 this.cleanWord(this.doc.body);
19959 if (node.nodeName == "#text") {
19960 // clean up silly Windows -- stuff?
19963 if (node.nodeName == "#comment") {
19964 node.parentNode.removeChild(node);
19965 // clean up silly Windows -- stuff?
19969 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19970 node.parentNode.removeChild(node);
19974 // remove - but keep children..
19975 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19976 while (node.childNodes.length) {
19977 var cn = node.childNodes[0];
19978 node.removeChild(cn);
19979 node.parentNode.insertBefore(cn, node);
19981 node.parentNode.removeChild(node);
19982 this.iterateChildren(node, this.cleanWord);
19986 if (node.className.length) {
19988 var cn = node.className.split(/\W+/);
19990 Roo.each(cn, function(cls) {
19991 if (cls.match(/Mso[a-zA-Z]+/)) {
19996 node.className = cna.length ? cna.join(' ') : '';
19998 node.removeAttribute("class");
20002 if (node.hasAttribute("lang")) {
20003 node.removeAttribute("lang");
20006 if (node.hasAttribute("style")) {
20008 var styles = node.getAttribute("style").split(";");
20010 Roo.each(styles, function(s) {
20011 if (!s.match(/:/)) {
20014 var kv = s.split(":");
20015 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20018 // what ever is left... we allow.
20021 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20022 if (!nstyle.length) {
20023 node.removeAttribute('style');
20026 this.iterateChildren(node, this.cleanWord);
20032 * iterateChildren of a Node, calling fn each time, using this as the scole..
20033 * @param {DomNode} node node to iterate children of.
20034 * @param {Function} fn method of this class to call on each item.
20036 iterateChildren : function(node, fn)
20038 if (!node.childNodes.length) {
20041 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20042 fn.call(this, node.childNodes[i])
20048 * cleanTableWidths.
20050 * Quite often pasting from word etc.. results in tables with column and widths.
20051 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20054 cleanTableWidths : function(node)
20059 this.cleanTableWidths(this.doc.body);
20064 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20067 Roo.log(node.tagName);
20068 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20069 this.iterateChildren(node, this.cleanTableWidths);
20072 if (node.hasAttribute('width')) {
20073 node.removeAttribute('width');
20077 if (node.hasAttribute("style")) {
20080 var styles = node.getAttribute("style").split(";");
20082 Roo.each(styles, function(s) {
20083 if (!s.match(/:/)) {
20086 var kv = s.split(":");
20087 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20090 // what ever is left... we allow.
20093 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20094 if (!nstyle.length) {
20095 node.removeAttribute('style');
20099 this.iterateChildren(node, this.cleanTableWidths);
20107 domToHTML : function(currentElement, depth, nopadtext) {
20109 depth = depth || 0;
20110 nopadtext = nopadtext || false;
20112 if (!currentElement) {
20113 return this.domToHTML(this.doc.body);
20116 //Roo.log(currentElement);
20118 var allText = false;
20119 var nodeName = currentElement.nodeName;
20120 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20122 if (nodeName == '#text') {
20124 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20129 if (nodeName != 'BODY') {
20132 // Prints the node tagName, such as <A>, <IMG>, etc
20135 for(i = 0; i < currentElement.attributes.length;i++) {
20137 var aname = currentElement.attributes.item(i).name;
20138 if (!currentElement.attributes.item(i).value.length) {
20141 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20144 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20153 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20156 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20161 // Traverse the tree
20163 var currentElementChild = currentElement.childNodes.item(i);
20164 var allText = true;
20165 var innerHTML = '';
20167 while (currentElementChild) {
20168 // Formatting code (indent the tree so it looks nice on the screen)
20169 var nopad = nopadtext;
20170 if (lastnode == 'SPAN') {
20174 if (currentElementChild.nodeName == '#text') {
20175 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20176 toadd = nopadtext ? toadd : toadd.trim();
20177 if (!nopad && toadd.length > 80) {
20178 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20180 innerHTML += toadd;
20183 currentElementChild = currentElement.childNodes.item(i);
20189 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20191 // Recursively traverse the tree structure of the child node
20192 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20193 lastnode = currentElementChild.nodeName;
20195 currentElementChild=currentElement.childNodes.item(i);
20201 // The remaining code is mostly for formatting the tree
20202 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20207 ret+= "</"+tagName+">";
20213 applyBlacklists : function()
20215 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20216 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20220 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20221 if (b.indexOf(tag) > -1) {
20224 this.white.push(tag);
20228 Roo.each(w, function(tag) {
20229 if (b.indexOf(tag) > -1) {
20232 if (this.white.indexOf(tag) > -1) {
20235 this.white.push(tag);
20240 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20241 if (w.indexOf(tag) > -1) {
20244 this.black.push(tag);
20248 Roo.each(b, function(tag) {
20249 if (w.indexOf(tag) > -1) {
20252 if (this.black.indexOf(tag) > -1) {
20255 this.black.push(tag);
20260 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
20261 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
20265 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20266 if (b.indexOf(tag) > -1) {
20269 this.cwhite.push(tag);
20273 Roo.each(w, function(tag) {
20274 if (b.indexOf(tag) > -1) {
20277 if (this.cwhite.indexOf(tag) > -1) {
20280 this.cwhite.push(tag);
20285 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20286 if (w.indexOf(tag) > -1) {
20289 this.cblack.push(tag);
20293 Roo.each(b, function(tag) {
20294 if (w.indexOf(tag) > -1) {
20297 if (this.cblack.indexOf(tag) > -1) {
20300 this.cblack.push(tag);
20305 setStylesheets : function(stylesheets)
20307 if(typeof(stylesheets) == 'string'){
20308 Roo.get(this.iframe.contentDocument.head).createChild({
20310 rel : 'stylesheet',
20319 Roo.each(stylesheets, function(s) {
20324 Roo.get(_this.iframe.contentDocument.head).createChild({
20326 rel : 'stylesheet',
20335 removeStylesheets : function()
20339 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20344 // hide stuff that is not compatible
20358 * @event specialkey
20362 * @cfg {String} fieldClass @hide
20365 * @cfg {String} focusClass @hide
20368 * @cfg {String} autoCreate @hide
20371 * @cfg {String} inputType @hide
20374 * @cfg {String} invalidClass @hide
20377 * @cfg {String} invalidText @hide
20380 * @cfg {String} msgFx @hide
20383 * @cfg {String} validateOnBlur @hide
20387 Roo.HtmlEditorCore.white = [
20388 'area', 'br', 'img', 'input', 'hr', 'wbr',
20390 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
20391 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
20392 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
20393 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
20394 'table', 'ul', 'xmp',
20396 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
20399 'dir', 'menu', 'ol', 'ul', 'dl',
20405 Roo.HtmlEditorCore.black = [
20406 // 'embed', 'object', // enable - backend responsiblity to clean thiese
20408 'base', 'basefont', 'bgsound', 'blink', 'body',
20409 'frame', 'frameset', 'head', 'html', 'ilayer',
20410 'iframe', 'layer', 'link', 'meta', 'object',
20411 'script', 'style' ,'title', 'xml' // clean later..
20413 Roo.HtmlEditorCore.clean = [
20414 'script', 'style', 'title', 'xml'
20416 Roo.HtmlEditorCore.remove = [
20421 Roo.HtmlEditorCore.ablack = [
20425 Roo.HtmlEditorCore.aclean = [
20426 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
20430 Roo.HtmlEditorCore.pwhite= [
20431 'http', 'https', 'mailto'
20434 // white listed style attributes.
20435 Roo.HtmlEditorCore.cwhite= [
20436 // 'text-align', /// default is to allow most things..
20442 // black listed style attributes.
20443 Roo.HtmlEditorCore.cblack= [
20444 // 'font-size' -- this can be set by the project
20448 Roo.HtmlEditorCore.swapCodes =[
20467 * @class Roo.bootstrap.HtmlEditor
20468 * @extends Roo.bootstrap.TextArea
20469 * Bootstrap HtmlEditor class
20472 * Create a new HtmlEditor
20473 * @param {Object} config The config object
20476 Roo.bootstrap.HtmlEditor = function(config){
20477 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20478 if (!this.toolbars) {
20479 this.toolbars = [];
20481 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20484 * @event initialize
20485 * Fires when the editor is fully initialized (including the iframe)
20486 * @param {HtmlEditor} this
20491 * Fires when the editor is first receives the focus. Any insertion must wait
20492 * until after this event.
20493 * @param {HtmlEditor} this
20497 * @event beforesync
20498 * Fires before the textarea is updated with content from the editor iframe. Return false
20499 * to cancel the sync.
20500 * @param {HtmlEditor} this
20501 * @param {String} html
20505 * @event beforepush
20506 * Fires before the iframe editor is updated with content from the textarea. Return false
20507 * to cancel the push.
20508 * @param {HtmlEditor} this
20509 * @param {String} html
20514 * Fires when the textarea is updated with content from the editor iframe.
20515 * @param {HtmlEditor} this
20516 * @param {String} html
20521 * Fires when the iframe editor is updated with content from the textarea.
20522 * @param {HtmlEditor} this
20523 * @param {String} html
20527 * @event editmodechange
20528 * Fires when the editor switches edit modes
20529 * @param {HtmlEditor} this
20530 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20532 editmodechange: true,
20534 * @event editorevent
20535 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20536 * @param {HtmlEditor} this
20540 * @event firstfocus
20541 * Fires when on first focus - needed by toolbars..
20542 * @param {HtmlEditor} this
20547 * Auto save the htmlEditor value as a file into Events
20548 * @param {HtmlEditor} this
20552 * @event savedpreview
20553 * preview the saved version of htmlEditor
20554 * @param {HtmlEditor} this
20561 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
20565 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20570 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20575 * @cfg {Number} height (in pixels)
20579 * @cfg {Number} width (in pixels)
20584 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20587 stylesheets: false,
20592 // private properties
20593 validationEvent : false,
20595 initialized : false,
20598 onFocus : Roo.emptyFn,
20600 hideMode:'offsets',
20603 tbContainer : false,
20605 toolbarContainer :function() {
20606 return this.wrap.select('.x-html-editor-tb',true).first();
20610 * Protected method that will not generally be called directly. It
20611 * is called when the editor creates its toolbar. Override this method if you need to
20612 * add custom toolbar buttons.
20613 * @param {HtmlEditor} editor
20615 createToolbar : function(){
20617 Roo.log("create toolbars");
20619 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20620 this.toolbars[0].render(this.toolbarContainer());
20624 // if (!editor.toolbars || !editor.toolbars.length) {
20625 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20628 // for (var i =0 ; i < editor.toolbars.length;i++) {
20629 // editor.toolbars[i] = Roo.factory(
20630 // typeof(editor.toolbars[i]) == 'string' ?
20631 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
20632 // Roo.bootstrap.HtmlEditor);
20633 // editor.toolbars[i].init(editor);
20639 onRender : function(ct, position)
20641 // Roo.log("Call onRender: " + this.xtype);
20643 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20645 this.wrap = this.inputEl().wrap({
20646 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20649 this.editorcore.onRender(ct, position);
20651 if (this.resizable) {
20652 this.resizeEl = new Roo.Resizable(this.wrap, {
20656 minHeight : this.height,
20657 height: this.height,
20658 handles : this.resizable,
20661 resize : function(r, w, h) {
20662 _t.onResize(w,h); // -something
20668 this.createToolbar(this);
20671 if(!this.width && this.resizable){
20672 this.setSize(this.wrap.getSize());
20674 if (this.resizeEl) {
20675 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20676 // should trigger onReize..
20682 onResize : function(w, h)
20684 Roo.log('resize: ' +w + ',' + h );
20685 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20689 if(this.inputEl() ){
20690 if(typeof w == 'number'){
20691 var aw = w - this.wrap.getFrameWidth('lr');
20692 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20695 if(typeof h == 'number'){
20696 var tbh = -11; // fixme it needs to tool bar size!
20697 for (var i =0; i < this.toolbars.length;i++) {
20698 // fixme - ask toolbars for heights?
20699 tbh += this.toolbars[i].el.getHeight();
20700 //if (this.toolbars[i].footer) {
20701 // tbh += this.toolbars[i].footer.el.getHeight();
20709 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20710 ah -= 5; // knock a few pixes off for look..
20711 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20715 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20716 this.editorcore.onResize(ew,eh);
20721 * Toggles the editor between standard and source edit mode.
20722 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20724 toggleSourceEdit : function(sourceEditMode)
20726 this.editorcore.toggleSourceEdit(sourceEditMode);
20728 if(this.editorcore.sourceEditMode){
20729 Roo.log('editor - showing textarea');
20732 // Roo.log(this.syncValue());
20734 this.inputEl().removeClass(['hide', 'x-hidden']);
20735 this.inputEl().dom.removeAttribute('tabIndex');
20736 this.inputEl().focus();
20738 Roo.log('editor - hiding textarea');
20740 // Roo.log(this.pushValue());
20743 this.inputEl().addClass(['hide', 'x-hidden']);
20744 this.inputEl().dom.setAttribute('tabIndex', -1);
20745 //this.deferFocus();
20748 if(this.resizable){
20749 this.setSize(this.wrap.getSize());
20752 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20755 // private (for BoxComponent)
20756 adjustSize : Roo.BoxComponent.prototype.adjustSize,
20758 // private (for BoxComponent)
20759 getResizeEl : function(){
20763 // private (for BoxComponent)
20764 getPositionEl : function(){
20769 initEvents : function(){
20770 this.originalValue = this.getValue();
20774 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20777 // markInvalid : Roo.emptyFn,
20779 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20782 // clearInvalid : Roo.emptyFn,
20784 setValue : function(v){
20785 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20786 this.editorcore.pushValue();
20791 deferFocus : function(){
20792 this.focus.defer(10, this);
20796 focus : function(){
20797 this.editorcore.focus();
20803 onDestroy : function(){
20809 for (var i =0; i < this.toolbars.length;i++) {
20810 // fixme - ask toolbars for heights?
20811 this.toolbars[i].onDestroy();
20814 this.wrap.dom.innerHTML = '';
20815 this.wrap.remove();
20820 onFirstFocus : function(){
20821 //Roo.log("onFirstFocus");
20822 this.editorcore.onFirstFocus();
20823 for (var i =0; i < this.toolbars.length;i++) {
20824 this.toolbars[i].onFirstFocus();
20830 syncValue : function()
20832 this.editorcore.syncValue();
20835 pushValue : function()
20837 this.editorcore.pushValue();
20841 // hide stuff that is not compatible
20855 * @event specialkey
20859 * @cfg {String} fieldClass @hide
20862 * @cfg {String} focusClass @hide
20865 * @cfg {String} autoCreate @hide
20868 * @cfg {String} inputType @hide
20871 * @cfg {String} invalidClass @hide
20874 * @cfg {String} invalidText @hide
20877 * @cfg {String} msgFx @hide
20880 * @cfg {String} validateOnBlur @hide
20889 Roo.namespace('Roo.bootstrap.htmleditor');
20891 * @class Roo.bootstrap.HtmlEditorToolbar1
20896 new Roo.bootstrap.HtmlEditor({
20899 new Roo.bootstrap.HtmlEditorToolbar1({
20900 disable : { fonts: 1 , format: 1, ..., ... , ...],
20906 * @cfg {Object} disable List of elements to disable..
20907 * @cfg {Array} btns List of additional buttons.
20911 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20914 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20917 Roo.apply(this, config);
20919 // default disabled, based on 'good practice'..
20920 this.disable = this.disable || {};
20921 Roo.applyIf(this.disable, {
20924 specialElements : true
20926 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20928 this.editor = config.editor;
20929 this.editorcore = config.editor.editorcore;
20931 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20933 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20934 // dont call parent... till later.
20936 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
20941 editorcore : false,
20946 "h1","h2","h3","h4","h5","h6",
20948 "abbr", "acronym", "address", "cite", "samp", "var",
20952 onRender : function(ct, position)
20954 // Roo.log("Call onRender: " + this.xtype);
20956 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20958 this.el.dom.style.marginBottom = '0';
20960 var editorcore = this.editorcore;
20961 var editor= this.editor;
20964 var btn = function(id,cmd , toggle, handler){
20966 var event = toggle ? 'toggle' : 'click';
20971 xns: Roo.bootstrap,
20974 enableToggle:toggle !== false,
20976 pressed : toggle ? false : null,
20979 a.listeners[toggle ? 'toggle' : 'click'] = function() {
20980 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
20989 xns: Roo.bootstrap,
20990 glyphicon : 'font',
20994 xns: Roo.bootstrap,
20998 Roo.each(this.formats, function(f) {
20999 style.menu.items.push({
21001 xns: Roo.bootstrap,
21002 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21007 editorcore.insertTag(this.tagname);
21014 children.push(style);
21017 btn('bold',false,true);
21018 btn('italic',false,true);
21019 btn('align-left', 'justifyleft',true);
21020 btn('align-center', 'justifycenter',true);
21021 btn('align-right' , 'justifyright',true);
21022 btn('link', false, false, function(btn) {
21023 //Roo.log("create link?");
21024 var url = prompt(this.createLinkText, this.defaultLinkValue);
21025 if(url && url != 'http:/'+'/'){
21026 this.editorcore.relayCmd('createlink', url);
21029 btn('list','insertunorderedlist',true);
21030 btn('pencil', false,true, function(btn){
21033 this.toggleSourceEdit(btn.pressed);
21039 xns: Roo.bootstrap,
21044 xns: Roo.bootstrap,
21049 cog.menu.items.push({
21051 xns: Roo.bootstrap,
21052 html : Clean styles,
21057 editorcore.insertTag(this.tagname);
21066 this.xtype = 'NavSimplebar';
21068 for(var i=0;i< children.length;i++) {
21070 this.buttons.add(this.addxtypeChild(children[i]));
21074 editor.on('editorevent', this.updateToolbar, this);
21076 onBtnClick : function(id)
21078 this.editorcore.relayCmd(id);
21079 this.editorcore.focus();
21083 * Protected method that will not generally be called directly. It triggers
21084 * a toolbar update by reading the markup state of the current selection in the editor.
21086 updateToolbar: function(){
21088 if(!this.editorcore.activated){
21089 this.editor.onFirstFocus(); // is this neeed?
21093 var btns = this.buttons;
21094 var doc = this.editorcore.doc;
21095 btns.get('bold').setActive(doc.queryCommandState('bold'));
21096 btns.get('italic').setActive(doc.queryCommandState('italic'));
21097 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21099 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21100 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21101 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21103 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21104 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21107 var ans = this.editorcore.getAllAncestors();
21108 if (this.formatCombo) {
21111 var store = this.formatCombo.store;
21112 this.formatCombo.setValue("");
21113 for (var i =0; i < ans.length;i++) {
21114 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21116 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21124 // hides menus... - so this cant be on a menu...
21125 Roo.bootstrap.MenuMgr.hideAll();
21127 Roo.bootstrap.MenuMgr.hideAll();
21128 //this.editorsyncValue();
21130 onFirstFocus: function() {
21131 this.buttons.each(function(item){
21135 toggleSourceEdit : function(sourceEditMode){
21138 if(sourceEditMode){
21139 Roo.log("disabling buttons");
21140 this.buttons.each( function(item){
21141 if(item.cmd != 'pencil'){
21147 Roo.log("enabling buttons");
21148 if(this.editorcore.initialized){
21149 this.buttons.each( function(item){
21155 Roo.log("calling toggole on editor");
21156 // tell the editor that it's been pressed..
21157 this.editor.toggleSourceEdit(sourceEditMode);
21167 * @class Roo.bootstrap.Table.AbstractSelectionModel
21168 * @extends Roo.util.Observable
21169 * Abstract base class for grid SelectionModels. It provides the interface that should be
21170 * implemented by descendant classes. This class should not be directly instantiated.
21173 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21174 this.locked = false;
21175 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21179 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21180 /** @ignore Called by the grid automatically. Do not call directly. */
21181 init : function(grid){
21187 * Locks the selections.
21190 this.locked = true;
21194 * Unlocks the selections.
21196 unlock : function(){
21197 this.locked = false;
21201 * Returns true if the selections are locked.
21202 * @return {Boolean}
21204 isLocked : function(){
21205 return this.locked;
21209 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21210 * @class Roo.bootstrap.Table.RowSelectionModel
21211 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21212 * It supports multiple selections and keyboard selection/navigation.
21214 * @param {Object} config
21217 Roo.bootstrap.Table.RowSelectionModel = function(config){
21218 Roo.apply(this, config);
21219 this.selections = new Roo.util.MixedCollection(false, function(o){
21224 this.lastActive = false;
21228 * @event selectionchange
21229 * Fires when the selection changes
21230 * @param {SelectionModel} this
21232 "selectionchange" : true,
21234 * @event afterselectionchange
21235 * Fires after the selection changes (eg. by key press or clicking)
21236 * @param {SelectionModel} this
21238 "afterselectionchange" : true,
21240 * @event beforerowselect
21241 * Fires when a row is selected being selected, return false to cancel.
21242 * @param {SelectionModel} this
21243 * @param {Number} rowIndex The selected index
21244 * @param {Boolean} keepExisting False if other selections will be cleared
21246 "beforerowselect" : true,
21249 * Fires when a row is selected.
21250 * @param {SelectionModel} this
21251 * @param {Number} rowIndex The selected index
21252 * @param {Roo.data.Record} r The record
21254 "rowselect" : true,
21256 * @event rowdeselect
21257 * Fires when a row is deselected.
21258 * @param {SelectionModel} this
21259 * @param {Number} rowIndex The selected index
21261 "rowdeselect" : true
21263 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21264 this.locked = false;
21267 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
21269 * @cfg {Boolean} singleSelect
21270 * True to allow selection of only one row at a time (defaults to false)
21272 singleSelect : false,
21275 initEvents : function(){
21277 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21278 this.grid.on("mousedown", this.handleMouseDown, this);
21279 }else{ // allow click to work like normal
21280 this.grid.on("rowclick", this.handleDragableRowClick, this);
21283 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21284 "up" : function(e){
21286 this.selectPrevious(e.shiftKey);
21287 }else if(this.last !== false && this.lastActive !== false){
21288 var last = this.last;
21289 this.selectRange(this.last, this.lastActive-1);
21290 this.grid.getView().focusRow(this.lastActive);
21291 if(last !== false){
21295 this.selectFirstRow();
21297 this.fireEvent("afterselectionchange", this);
21299 "down" : function(e){
21301 this.selectNext(e.shiftKey);
21302 }else if(this.last !== false && this.lastActive !== false){
21303 var last = this.last;
21304 this.selectRange(this.last, this.lastActive+1);
21305 this.grid.getView().focusRow(this.lastActive);
21306 if(last !== false){
21310 this.selectFirstRow();
21312 this.fireEvent("afterselectionchange", this);
21317 var view = this.grid.view;
21318 view.on("refresh", this.onRefresh, this);
21319 view.on("rowupdated", this.onRowUpdated, this);
21320 view.on("rowremoved", this.onRemove, this);
21324 onRefresh : function(){
21325 var ds = this.grid.dataSource, i, v = this.grid.view;
21326 var s = this.selections;
21327 s.each(function(r){
21328 if((i = ds.indexOfId(r.id)) != -1){
21337 onRemove : function(v, index, r){
21338 this.selections.remove(r);
21342 onRowUpdated : function(v, index, r){
21343 if(this.isSelected(r)){
21344 v.onRowSelect(index);
21350 * @param {Array} records The records to select
21351 * @param {Boolean} keepExisting (optional) True to keep existing selections
21353 selectRecords : function(records, keepExisting){
21355 this.clearSelections();
21357 var ds = this.grid.dataSource;
21358 for(var i = 0, len = records.length; i < len; i++){
21359 this.selectRow(ds.indexOf(records[i]), true);
21364 * Gets the number of selected rows.
21367 getCount : function(){
21368 return this.selections.length;
21372 * Selects the first row in the grid.
21374 selectFirstRow : function(){
21379 * Select the last row.
21380 * @param {Boolean} keepExisting (optional) True to keep existing selections
21382 selectLastRow : function(keepExisting){
21383 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21387 * Selects the row immediately following the last selected row.
21388 * @param {Boolean} keepExisting (optional) True to keep existing selections
21390 selectNext : function(keepExisting){
21391 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21392 this.selectRow(this.last+1, keepExisting);
21393 this.grid.getView().focusRow(this.last);
21398 * Selects the row that precedes the last selected row.
21399 * @param {Boolean} keepExisting (optional) True to keep existing selections
21401 selectPrevious : function(keepExisting){
21403 this.selectRow(this.last-1, keepExisting);
21404 this.grid.getView().focusRow(this.last);
21409 * Returns the selected records
21410 * @return {Array} Array of selected records
21412 getSelections : function(){
21413 return [].concat(this.selections.items);
21417 * Returns the first selected record.
21420 getSelected : function(){
21421 return this.selections.itemAt(0);
21426 * Clears all selections.
21428 clearSelections : function(fast){
21429 if(this.locked) return;
21431 var ds = this.grid.dataSource;
21432 var s = this.selections;
21433 s.each(function(r){
21434 this.deselectRow(ds.indexOfId(r.id));
21438 this.selections.clear();
21445 * Selects all rows.
21447 selectAll : function(){
21448 if(this.locked) return;
21449 this.selections.clear();
21450 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21451 this.selectRow(i, true);
21456 * Returns True if there is a selection.
21457 * @return {Boolean}
21459 hasSelection : function(){
21460 return this.selections.length > 0;
21464 * Returns True if the specified row is selected.
21465 * @param {Number/Record} record The record or index of the record to check
21466 * @return {Boolean}
21468 isSelected : function(index){
21469 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21470 return (r && this.selections.key(r.id) ? true : false);
21474 * Returns True if the specified record id is selected.
21475 * @param {String} id The id of record to check
21476 * @return {Boolean}
21478 isIdSelected : function(id){
21479 return (this.selections.key(id) ? true : false);
21483 handleMouseDown : function(e, t){
21484 var view = this.grid.getView(), rowIndex;
21485 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21488 if(e.shiftKey && this.last !== false){
21489 var last = this.last;
21490 this.selectRange(last, rowIndex, e.ctrlKey);
21491 this.last = last; // reset the last
21492 view.focusRow(rowIndex);
21494 var isSelected = this.isSelected(rowIndex);
21495 if(e.button !== 0 && isSelected){
21496 view.focusRow(rowIndex);
21497 }else if(e.ctrlKey && isSelected){
21498 this.deselectRow(rowIndex);
21499 }else if(!isSelected){
21500 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21501 view.focusRow(rowIndex);
21504 this.fireEvent("afterselectionchange", this);
21507 handleDragableRowClick : function(grid, rowIndex, e)
21509 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21510 this.selectRow(rowIndex, false);
21511 grid.view.focusRow(rowIndex);
21512 this.fireEvent("afterselectionchange", this);
21517 * Selects multiple rows.
21518 * @param {Array} rows Array of the indexes of the row to select
21519 * @param {Boolean} keepExisting (optional) True to keep existing selections
21521 selectRows : function(rows, keepExisting){
21523 this.clearSelections();
21525 for(var i = 0, len = rows.length; i < len; i++){
21526 this.selectRow(rows[i], true);
21531 * Selects a range of rows. All rows in between startRow and endRow are also selected.
21532 * @param {Number} startRow The index of the first row in the range
21533 * @param {Number} endRow The index of the last row in the range
21534 * @param {Boolean} keepExisting (optional) True to retain existing selections
21536 selectRange : function(startRow, endRow, keepExisting){
21537 if(this.locked) return;
21539 this.clearSelections();
21541 if(startRow <= endRow){
21542 for(var i = startRow; i <= endRow; i++){
21543 this.selectRow(i, true);
21546 for(var i = startRow; i >= endRow; i--){
21547 this.selectRow(i, true);
21553 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21554 * @param {Number} startRow The index of the first row in the range
21555 * @param {Number} endRow The index of the last row in the range
21557 deselectRange : function(startRow, endRow, preventViewNotify){
21558 if(this.locked) return;
21559 for(var i = startRow; i <= endRow; i++){
21560 this.deselectRow(i, preventViewNotify);
21566 * @param {Number} row The index of the row to select
21567 * @param {Boolean} keepExisting (optional) True to keep existing selections
21569 selectRow : function(index, keepExisting, preventViewNotify){
21570 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21571 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21572 if(!keepExisting || this.singleSelect){
21573 this.clearSelections();
21575 var r = this.grid.dataSource.getAt(index);
21576 this.selections.add(r);
21577 this.last = this.lastActive = index;
21578 if(!preventViewNotify){
21579 this.grid.getView().onRowSelect(index);
21581 this.fireEvent("rowselect", this, index, r);
21582 this.fireEvent("selectionchange", this);
21588 * @param {Number} row The index of the row to deselect
21590 deselectRow : function(index, preventViewNotify){
21591 if(this.locked) return;
21592 if(this.last == index){
21595 if(this.lastActive == index){
21596 this.lastActive = false;
21598 var r = this.grid.dataSource.getAt(index);
21599 this.selections.remove(r);
21600 if(!preventViewNotify){
21601 this.grid.getView().onRowDeselect(index);
21603 this.fireEvent("rowdeselect", this, index);
21604 this.fireEvent("selectionchange", this);
21608 restoreLast : function(){
21610 this.last = this._last;
21615 acceptsNav : function(row, col, cm){
21616 return !cm.isHidden(col) && cm.isCellEditable(col, row);
21620 onEditorKey : function(field, e){
21621 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21626 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21628 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21630 }else if(k == e.ENTER && !e.ctrlKey){
21634 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21636 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21638 }else if(k == e.ESC){
21642 g.startEditing(newCell[0], newCell[1]);
21647 * Ext JS Library 1.1.1
21648 * Copyright(c) 2006-2007, Ext JS, LLC.
21650 * Originally Released Under LGPL - original licence link has changed is not relivant.
21653 * <script type="text/javascript">
21657 * @class Roo.bootstrap.PagingToolbar
21658 * @extends Roo.bootstrap.NavSimplebar
21659 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21661 * Create a new PagingToolbar
21662 * @param {Object} config The config object
21663 * @param {Roo.data.Store} store
21665 Roo.bootstrap.PagingToolbar = function(config)
21667 // old args format still supported... - xtype is prefered..
21668 // created from xtype...
21670 var ds = config.dataSource;
21673 Roo.log(this.store);
21675 if (this.store && !ds) {
21677 this.store= Roo.factory(this.store, Roo.data);
21679 ds.xmodule = this.xmodule || false;
21682 this.toolbarItems = [];
21683 if (config.items) {
21684 this.toolbarItems = config.items;
21685 // config.items = [];
21688 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21696 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21700 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21702 * @cfg {Roo.data.Store} dataSource
21703 * The underlying data store providing the paged data
21706 * @cfg {String/HTMLElement/Element} container
21707 * container The id or element that will contain the toolbar
21710 * @cfg {Boolean} displayInfo
21711 * True to display the displayMsg (defaults to false)
21714 * @cfg {Number} pageSize
21715 * The number of records to display per page (defaults to 20)
21719 * @cfg {String} displayMsg
21720 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21722 displayMsg : 'Displaying {0} - {1} of {2}',
21724 * @cfg {String} emptyMsg
21725 * The message to display when no records are found (defaults to "No data to display")
21727 emptyMsg : 'No data to display',
21729 * Customizable piece of the default paging text (defaults to "Page")
21732 beforePageText : "Page",
21734 * Customizable piece of the default paging text (defaults to "of %0")
21737 afterPageText : "of {0}",
21739 * Customizable piece of the default paging text (defaults to "First Page")
21742 firstText : "First Page",
21744 * Customizable piece of the default paging text (defaults to "Previous Page")
21747 prevText : "Previous Page",
21749 * Customizable piece of the default paging text (defaults to "Next Page")
21752 nextText : "Next Page",
21754 * Customizable piece of the default paging text (defaults to "Last Page")
21757 lastText : "Last Page",
21759 * Customizable piece of the default paging text (defaults to "Refresh")
21762 refreshText : "Refresh",
21766 onRender : function(ct, position)
21768 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21769 this.navgroup.parentId = this.id;
21770 this.navgroup.onRender(this.el, null);
21771 // add the buttons to the navgroup
21773 if(this.displayInfo){
21774 Roo.log(this.el.select('ul.navbar-nav',true).first());
21775 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21776 this.displayEl = this.el.select('.x-paging-info', true).first();
21777 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21778 // this.displayEl = navel.el.select('span',true).first();
21784 Roo.each(_this.buttons, function(e){
21785 Roo.factory(e).onRender(_this.el, null);
21789 Roo.each(_this.toolbarItems, function(e) {
21790 _this.navgroup.addItem(e);
21794 this.first = this.navgroup.addItem({
21795 tooltip: this.firstText,
21797 icon : 'fa fa-backward',
21799 preventDefault: true,
21800 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21803 this.prev = this.navgroup.addItem({
21804 tooltip: this.prevText,
21806 icon : 'fa fa-step-backward',
21808 preventDefault: true,
21809 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
21811 //this.addSeparator();
21814 var field = this.navgroup.addItem( {
21816 cls : 'x-paging-position',
21818 html : this.beforePageText +
21819 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21820 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
21823 this.field = field.el.select('input', true).first();
21824 this.field.on("keydown", this.onPagingKeydown, this);
21825 this.field.on("focus", function(){this.dom.select();});
21828 this.afterTextEl = field.el.select('.x-paging-after',true).first();
21829 //this.field.setHeight(18);
21830 //this.addSeparator();
21831 this.next = this.navgroup.addItem({
21832 tooltip: this.nextText,
21834 html : ' <i class="fa fa-step-forward">',
21836 preventDefault: true,
21837 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
21839 this.last = this.navgroup.addItem({
21840 tooltip: this.lastText,
21841 icon : 'fa fa-forward',
21844 preventDefault: true,
21845 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
21847 //this.addSeparator();
21848 this.loading = this.navgroup.addItem({
21849 tooltip: this.refreshText,
21850 icon: 'fa fa-refresh',
21851 preventDefault: true,
21852 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21858 updateInfo : function(){
21859 if(this.displayEl){
21860 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21861 var msg = count == 0 ?
21865 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
21867 this.displayEl.update(msg);
21872 onLoad : function(ds, r, o){
21873 this.cursor = o.params ? o.params.start : 0;
21874 var d = this.getPageData(),
21878 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21879 this.field.dom.value = ap;
21880 this.first.setDisabled(ap == 1);
21881 this.prev.setDisabled(ap == 1);
21882 this.next.setDisabled(ap == ps);
21883 this.last.setDisabled(ap == ps);
21884 this.loading.enable();
21889 getPageData : function(){
21890 var total = this.ds.getTotalCount();
21893 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21894 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21899 onLoadError : function(){
21900 this.loading.enable();
21904 onPagingKeydown : function(e){
21905 var k = e.getKey();
21906 var d = this.getPageData();
21908 var v = this.field.dom.value, pageNum;
21909 if(!v || isNaN(pageNum = parseInt(v, 10))){
21910 this.field.dom.value = d.activePage;
21913 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21914 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21917 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))
21919 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21920 this.field.dom.value = pageNum;
21921 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21924 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21926 var v = this.field.dom.value, pageNum;
21927 var increment = (e.shiftKey) ? 10 : 1;
21928 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21930 if(!v || isNaN(pageNum = parseInt(v, 10))) {
21931 this.field.dom.value = d.activePage;
21934 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21936 this.field.dom.value = parseInt(v, 10) + increment;
21937 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21938 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21945 beforeLoad : function(){
21947 this.loading.disable();
21952 onClick : function(which){
21961 ds.load({params:{start: 0, limit: this.pageSize}});
21964 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21967 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21970 var total = ds.getTotalCount();
21971 var extra = total % this.pageSize;
21972 var lastStart = extra ? (total - extra) : total-this.pageSize;
21973 ds.load({params:{start: lastStart, limit: this.pageSize}});
21976 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21982 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21983 * @param {Roo.data.Store} store The data store to unbind
21985 unbind : function(ds){
21986 ds.un("beforeload", this.beforeLoad, this);
21987 ds.un("load", this.onLoad, this);
21988 ds.un("loadexception", this.onLoadError, this);
21989 ds.un("remove", this.updateInfo, this);
21990 ds.un("add", this.updateInfo, this);
21991 this.ds = undefined;
21995 * Binds the paging toolbar to the specified {@link Roo.data.Store}
21996 * @param {Roo.data.Store} store The data store to bind
21998 bind : function(ds){
21999 ds.on("beforeload", this.beforeLoad, this);
22000 ds.on("load", this.onLoad, this);
22001 ds.on("loadexception", this.onLoadError, this);
22002 ds.on("remove", this.updateInfo, this);
22003 ds.on("add", this.updateInfo, this);
22014 * @class Roo.bootstrap.MessageBar
22015 * @extends Roo.bootstrap.Component
22016 * Bootstrap MessageBar class
22017 * @cfg {String} html contents of the MessageBar
22018 * @cfg {String} weight (info | success | warning | danger) default info
22019 * @cfg {String} beforeClass insert the bar before the given class
22020 * @cfg {Boolean} closable (true | false) default false
22021 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22024 * Create a new Element
22025 * @param {Object} config The config object
22028 Roo.bootstrap.MessageBar = function(config){
22029 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22032 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22038 beforeClass: 'bootstrap-sticky-wrap',
22040 getAutoCreate : function(){
22044 cls: 'alert alert-dismissable alert-' + this.weight,
22049 html: this.html || ''
22055 cfg.cls += ' alert-messages-fixed';
22069 onRender : function(ct, position)
22071 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22074 var cfg = Roo.apply({}, this.getAutoCreate());
22078 cfg.cls += ' ' + this.cls;
22081 cfg.style = this.style;
22083 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22085 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22088 this.el.select('>button.close').on('click', this.hide, this);
22094 if (!this.rendered) {
22100 this.fireEvent('show', this);
22106 if (!this.rendered) {
22112 this.fireEvent('hide', this);
22115 update : function()
22117 // var e = this.el.dom.firstChild;
22119 // if(this.closable){
22120 // e = e.nextSibling;
22123 // e.data = this.html || '';
22125 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22141 * @class Roo.bootstrap.Graph
22142 * @extends Roo.bootstrap.Component
22143 * Bootstrap Graph class
22147 @cfg {String} graphtype bar | vbar | pie
22148 @cfg {number} g_x coodinator | centre x (pie)
22149 @cfg {number} g_y coodinator | centre y (pie)
22150 @cfg {number} g_r radius (pie)
22151 @cfg {number} g_height height of the chart (respected by all elements in the set)
22152 @cfg {number} g_width width of the chart (respected by all elements in the set)
22153 @cfg {Object} title The title of the chart
22156 -opts (object) options for the chart
22158 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22159 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22161 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.
22162 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22164 o stretch (boolean)
22166 -opts (object) options for the pie
22169 o startAngle (number)
22170 o endAngle (number)
22174 * Create a new Input
22175 * @param {Object} config The config object
22178 Roo.bootstrap.Graph = function(config){
22179 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22185 * The img click event for the img.
22186 * @param {Roo.EventObject} e
22192 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22203 //g_colors: this.colors,
22210 getAutoCreate : function(){
22221 onRender : function(ct,position){
22222 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22223 this.raphael = Raphael(this.el.dom);
22225 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22226 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22227 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22228 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22230 r.text(160, 10, "Single Series Chart").attr(txtattr);
22231 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22232 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22233 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22235 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22236 r.barchart(330, 10, 300, 220, data1);
22237 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22238 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22241 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22242 // r.barchart(30, 30, 560, 250, xdata, {
22243 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22244 // axis : "0 0 1 1",
22245 // axisxlabels : xdata
22246 // //yvalues : cols,
22249 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22251 // this.load(null,xdata,{
22252 // axis : "0 0 1 1",
22253 // axisxlabels : xdata
22258 load : function(graphtype,xdata,opts){
22259 this.raphael.clear();
22261 graphtype = this.graphtype;
22266 var r = this.raphael,
22267 fin = function () {
22268 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22270 fout = function () {
22271 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22273 pfin = function() {
22274 this.sector.stop();
22275 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22278 this.label[0].stop();
22279 this.label[0].attr({ r: 7.5 });
22280 this.label[1].attr({ "font-weight": 800 });
22283 pfout = function() {
22284 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22287 this.label[0].animate({ r: 5 }, 500, "bounce");
22288 this.label[1].attr({ "font-weight": 400 });
22294 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22297 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22300 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
22301 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22303 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22310 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22315 setTitle: function(o)
22320 initEvents: function() {
22323 this.el.on('click', this.onClick, this);
22327 onClick : function(e)
22329 Roo.log('img onclick');
22330 this.fireEvent('click', this, e);
22342 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22345 * @class Roo.bootstrap.dash.NumberBox
22346 * @extends Roo.bootstrap.Component
22347 * Bootstrap NumberBox class
22348 * @cfg {String} headline Box headline
22349 * @cfg {String} content Box content
22350 * @cfg {String} icon Box icon
22351 * @cfg {String} footer Footer text
22352 * @cfg {String} fhref Footer href
22355 * Create a new NumberBox
22356 * @param {Object} config The config object
22360 Roo.bootstrap.dash.NumberBox = function(config){
22361 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22365 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
22374 getAutoCreate : function(){
22378 cls : 'small-box ',
22386 cls : 'roo-headline',
22387 html : this.headline
22391 cls : 'roo-content',
22392 html : this.content
22406 cls : 'ion ' + this.icon
22415 cls : 'small-box-footer',
22416 href : this.fhref || '#',
22420 cfg.cn.push(footer);
22427 onRender : function(ct,position){
22428 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22435 setHeadline: function (value)
22437 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22440 setFooter: function (value, href)
22442 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22445 this.el.select('a.small-box-footer',true).first().attr('href', href);
22450 setContent: function (value)
22452 this.el.select('.roo-content',true).first().dom.innerHTML = value;
22455 initEvents: function()
22469 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22472 * @class Roo.bootstrap.dash.TabBox
22473 * @extends Roo.bootstrap.Component
22474 * Bootstrap TabBox class
22475 * @cfg {String} title Title of the TabBox
22476 * @cfg {String} icon Icon of the TabBox
22477 * @cfg {Boolean} showtabs (true|false) show the tabs default true
22478 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22481 * Create a new TabBox
22482 * @param {Object} config The config object
22486 Roo.bootstrap.dash.TabBox = function(config){
22487 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22492 * When a pane is added
22493 * @param {Roo.bootstrap.dash.TabPane} pane
22497 * @event activatepane
22498 * When a pane is activated
22499 * @param {Roo.bootstrap.dash.TabPane} pane
22501 "activatepane" : true
22509 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
22514 tabScrollable : false,
22516 getChildContainer : function()
22518 return this.el.select('.tab-content', true).first();
22521 getAutoCreate : function(){
22525 cls: 'pull-left header',
22533 cls: 'fa ' + this.icon
22539 cls: 'nav nav-tabs pull-right',
22545 if(this.tabScrollable){
22552 cls: 'nav nav-tabs pull-right',
22563 cls: 'nav-tabs-custom',
22568 cls: 'tab-content no-padding',
22576 initEvents : function()
22578 //Roo.log('add add pane handler');
22579 this.on('addpane', this.onAddPane, this);
22582 * Updates the box title
22583 * @param {String} html to set the title to.
22585 setTitle : function(value)
22587 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22589 onAddPane : function(pane)
22591 this.panes.push(pane);
22592 //Roo.log('addpane');
22594 // tabs are rendere left to right..
22595 if(!this.showtabs){
22599 var ctr = this.el.select('.nav-tabs', true).first();
22602 var existing = ctr.select('.nav-tab',true);
22603 var qty = existing.getCount();;
22606 var tab = ctr.createChild({
22608 cls : 'nav-tab' + (qty ? '' : ' active'),
22616 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22619 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22621 pane.el.addClass('active');
22626 onTabClick : function(ev,un,ob,pane)
22628 //Roo.log('tab - prev default');
22629 ev.preventDefault();
22632 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22633 pane.tab.addClass('active');
22634 //Roo.log(pane.title);
22635 this.getChildContainer().select('.tab-pane',true).removeClass('active');
22636 // technically we should have a deactivate event.. but maybe add later.
22637 // and it should not de-activate the selected tab...
22638 this.fireEvent('activatepane', pane);
22639 pane.el.addClass('active');
22640 pane.fireEvent('activate');
22645 getActivePane : function()
22648 Roo.each(this.panes, function(p) {
22649 if(p.el.hasClass('active')){
22670 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22672 * @class Roo.bootstrap.TabPane
22673 * @extends Roo.bootstrap.Component
22674 * Bootstrap TabPane class
22675 * @cfg {Boolean} active (false | true) Default false
22676 * @cfg {String} title title of panel
22680 * Create a new TabPane
22681 * @param {Object} config The config object
22684 Roo.bootstrap.dash.TabPane = function(config){
22685 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22691 * When a pane is activated
22692 * @param {Roo.bootstrap.dash.TabPane} pane
22699 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
22704 // the tabBox that this is attached to.
22707 getAutoCreate : function()
22715 cfg.cls += ' active';
22720 initEvents : function()
22722 //Roo.log('trigger add pane handler');
22723 this.parent().fireEvent('addpane', this)
22727 * Updates the tab title
22728 * @param {String} html to set the title to.
22730 setTitle: function(str)
22736 this.tab.select('a', true).first().dom.innerHTML = str;
22753 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22756 * @class Roo.bootstrap.menu.Menu
22757 * @extends Roo.bootstrap.Component
22758 * Bootstrap Menu class - container for Menu
22759 * @cfg {String} html Text of the menu
22760 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22761 * @cfg {String} icon Font awesome icon
22762 * @cfg {String} pos Menu align to (top | bottom) default bottom
22766 * Create a new Menu
22767 * @param {Object} config The config object
22771 Roo.bootstrap.menu.Menu = function(config){
22772 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22776 * @event beforeshow
22777 * Fires before this menu is displayed
22778 * @param {Roo.bootstrap.menu.Menu} this
22782 * @event beforehide
22783 * Fires before this menu is hidden
22784 * @param {Roo.bootstrap.menu.Menu} this
22789 * Fires after this menu is displayed
22790 * @param {Roo.bootstrap.menu.Menu} this
22795 * Fires after this menu is hidden
22796 * @param {Roo.bootstrap.menu.Menu} this
22801 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22802 * @param {Roo.bootstrap.menu.Menu} this
22803 * @param {Roo.EventObject} e
22810 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
22814 weight : 'default',
22819 getChildContainer : function() {
22820 if(this.isSubMenu){
22824 return this.el.select('ul.dropdown-menu', true).first();
22827 getAutoCreate : function()
22832 cls : 'roo-menu-text',
22840 cls : 'fa ' + this.icon
22851 cls : 'dropdown-button btn btn-' + this.weight,
22856 cls : 'dropdown-toggle btn btn-' + this.weight,
22866 cls : 'dropdown-menu'
22872 if(this.pos == 'top'){
22873 cfg.cls += ' dropup';
22876 if(this.isSubMenu){
22879 cls : 'dropdown-menu'
22886 onRender : function(ct, position)
22888 this.isSubMenu = ct.hasClass('dropdown-submenu');
22890 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22893 initEvents : function()
22895 if(this.isSubMenu){
22899 this.hidden = true;
22901 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22902 this.triggerEl.on('click', this.onTriggerPress, this);
22904 this.buttonEl = this.el.select('button.dropdown-button', true).first();
22905 this.buttonEl.on('click', this.onClick, this);
22911 if(this.isSubMenu){
22915 return this.el.select('ul.dropdown-menu', true).first();
22918 onClick : function(e)
22920 this.fireEvent("click", this, e);
22923 onTriggerPress : function(e)
22925 if (this.isVisible()) {
22932 isVisible : function(){
22933 return !this.hidden;
22938 this.fireEvent("beforeshow", this);
22940 this.hidden = false;
22941 this.el.addClass('open');
22943 Roo.get(document).on("mouseup", this.onMouseUp, this);
22945 this.fireEvent("show", this);
22952 this.fireEvent("beforehide", this);
22954 this.hidden = true;
22955 this.el.removeClass('open');
22957 Roo.get(document).un("mouseup", this.onMouseUp);
22959 this.fireEvent("hide", this);
22962 onMouseUp : function()
22976 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22979 * @class Roo.bootstrap.menu.Item
22980 * @extends Roo.bootstrap.Component
22981 * Bootstrap MenuItem class
22982 * @cfg {Boolean} submenu (true | false) default false
22983 * @cfg {String} html text of the item
22984 * @cfg {String} href the link
22985 * @cfg {Boolean} disable (true | false) default false
22986 * @cfg {Boolean} preventDefault (true | false) default true
22987 * @cfg {String} icon Font awesome icon
22988 * @cfg {String} pos Submenu align to (left | right) default right
22992 * Create a new Item
22993 * @param {Object} config The config object
22997 Roo.bootstrap.menu.Item = function(config){
22998 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23002 * Fires when the mouse is hovering over this menu
23003 * @param {Roo.bootstrap.menu.Item} this
23004 * @param {Roo.EventObject} e
23009 * Fires when the mouse exits this menu
23010 * @param {Roo.bootstrap.menu.Item} this
23011 * @param {Roo.EventObject} e
23017 * The raw click event for the entire grid.
23018 * @param {Roo.EventObject} e
23024 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23029 preventDefault: true,
23034 getAutoCreate : function()
23039 cls : 'roo-menu-item-text',
23047 cls : 'fa ' + this.icon
23056 href : this.href || '#',
23063 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23067 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23069 if(this.pos == 'left'){
23070 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23077 initEvents : function()
23079 this.el.on('mouseover', this.onMouseOver, this);
23080 this.el.on('mouseout', this.onMouseOut, this);
23082 this.el.select('a', true).first().on('click', this.onClick, this);
23086 onClick : function(e)
23088 if(this.preventDefault){
23089 e.preventDefault();
23092 this.fireEvent("click", this, e);
23095 onMouseOver : function(e)
23097 if(this.submenu && this.pos == 'left'){
23098 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23101 this.fireEvent("mouseover", this, e);
23104 onMouseOut : function(e)
23106 this.fireEvent("mouseout", this, e);
23118 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23121 * @class Roo.bootstrap.menu.Separator
23122 * @extends Roo.bootstrap.Component
23123 * Bootstrap Separator class
23126 * Create a new Separator
23127 * @param {Object} config The config object
23131 Roo.bootstrap.menu.Separator = function(config){
23132 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23135 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23137 getAutoCreate : function(){
23158 * @class Roo.bootstrap.Tooltip
23159 * Bootstrap Tooltip class
23160 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23161 * to determine which dom element triggers the tooltip.
23163 * It needs to add support for additional attributes like tooltip-position
23166 * Create a new Toolti
23167 * @param {Object} config The config object
23170 Roo.bootstrap.Tooltip = function(config){
23171 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23174 Roo.apply(Roo.bootstrap.Tooltip, {
23176 * @function init initialize tooltip monitoring.
23180 currentTip : false,
23181 currentRegion : false,
23187 Roo.get(document).on('mouseover', this.enter ,this);
23188 Roo.get(document).on('mouseout', this.leave, this);
23191 this.currentTip = new Roo.bootstrap.Tooltip();
23194 enter : function(ev)
23196 var dom = ev.getTarget();
23198 //Roo.log(['enter',dom]);
23199 var el = Roo.fly(dom);
23200 if (this.currentEl) {
23202 //Roo.log(this.currentEl);
23203 //Roo.log(this.currentEl.contains(dom));
23204 if (this.currentEl == el) {
23207 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23215 if (this.currentTip.el) {
23216 this.currentTip.el.hide(); // force hiding...
23221 // you can not look for children, as if el is the body.. then everythign is the child..
23222 if (!el.attr('tooltip')) { //
23223 if (!el.select("[tooltip]").elements.length) {
23226 // is the mouse over this child...?
23227 bindEl = el.select("[tooltip]").first();
23228 var xy = ev.getXY();
23229 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23230 //Roo.log("not in region.");
23233 //Roo.log("child element over..");
23236 this.currentEl = bindEl;
23237 this.currentTip.bind(bindEl);
23238 this.currentRegion = Roo.lib.Region.getRegion(dom);
23239 this.currentTip.enter();
23242 leave : function(ev)
23244 var dom = ev.getTarget();
23245 //Roo.log(['leave',dom]);
23246 if (!this.currentEl) {
23251 if (dom != this.currentEl.dom) {
23254 var xy = ev.getXY();
23255 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
23258 // only activate leave if mouse cursor is outside... bounding box..
23263 if (this.currentTip) {
23264 this.currentTip.leave();
23266 //Roo.log('clear currentEl');
23267 this.currentEl = false;
23272 'left' : ['r-l', [-2,0], 'right'],
23273 'right' : ['l-r', [2,0], 'left'],
23274 'bottom' : ['t-b', [0,2], 'top'],
23275 'top' : [ 'b-t', [0,-2], 'bottom']
23281 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
23286 delay : null, // can be { show : 300 , hide: 500}
23290 hoverState : null, //???
23292 placement : 'bottom',
23294 getAutoCreate : function(){
23301 cls : 'tooltip-arrow'
23304 cls : 'tooltip-inner'
23311 bind : function(el)
23317 enter : function () {
23319 if (this.timeout != null) {
23320 clearTimeout(this.timeout);
23323 this.hoverState = 'in';
23324 //Roo.log("enter - show");
23325 if (!this.delay || !this.delay.show) {
23330 this.timeout = setTimeout(function () {
23331 if (_t.hoverState == 'in') {
23334 }, this.delay.show);
23338 clearTimeout(this.timeout);
23340 this.hoverState = 'out';
23341 if (!this.delay || !this.delay.hide) {
23347 this.timeout = setTimeout(function () {
23348 //Roo.log("leave - timeout");
23350 if (_t.hoverState == 'out') {
23352 Roo.bootstrap.Tooltip.currentEl = false;
23360 this.render(document.body);
23363 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23365 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23367 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23369 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23371 var placement = typeof this.placement == 'function' ?
23372 this.placement.call(this, this.el, on_el) :
23375 var autoToken = /\s?auto?\s?/i;
23376 var autoPlace = autoToken.test(placement);
23378 placement = placement.replace(autoToken, '') || 'top';
23382 //this.el.setXY([0,0]);
23384 //this.el.dom.style.display='block';
23386 //this.el.appendTo(on_el);
23388 var p = this.getPosition();
23389 var box = this.el.getBox();
23395 var align = Roo.bootstrap.Tooltip.alignment[placement];
23397 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23399 if(placement == 'top' || placement == 'bottom'){
23401 placement = 'right';
23404 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23405 placement = 'left';
23409 align = Roo.bootstrap.Tooltip.alignment[placement];
23411 this.el.alignTo(this.bindEl, align[0],align[1]);
23412 //var arrow = this.el.select('.arrow',true).first();
23413 //arrow.set(align[2],
23415 this.el.addClass(placement);
23417 this.el.addClass('in fade');
23419 this.hoverState = null;
23421 if (this.el.hasClass('fade')) {
23432 //this.el.setXY([0,0]);
23433 this.el.removeClass('in');
23449 * @class Roo.bootstrap.LocationPicker
23450 * @extends Roo.bootstrap.Component
23451 * Bootstrap LocationPicker class
23452 * @cfg {Number} latitude Position when init default 0
23453 * @cfg {Number} longitude Position when init default 0
23454 * @cfg {Number} zoom default 15
23455 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23456 * @cfg {Boolean} mapTypeControl default false
23457 * @cfg {Boolean} disableDoubleClickZoom default false
23458 * @cfg {Boolean} scrollwheel default true
23459 * @cfg {Boolean} streetViewControl default false
23460 * @cfg {Number} radius default 0
23461 * @cfg {String} locationName
23462 * @cfg {Boolean} draggable default true
23463 * @cfg {Boolean} enableAutocomplete default false
23464 * @cfg {Boolean} enableReverseGeocode default true
23465 * @cfg {String} markerTitle
23468 * Create a new LocationPicker
23469 * @param {Object} config The config object
23473 Roo.bootstrap.LocationPicker = function(config){
23475 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23480 * Fires when the picker initialized.
23481 * @param {Roo.bootstrap.LocationPicker} this
23482 * @param {Google Location} location
23486 * @event positionchanged
23487 * Fires when the picker position changed.
23488 * @param {Roo.bootstrap.LocationPicker} this
23489 * @param {Google Location} location
23491 positionchanged : true,
23494 * Fires when the map resize.
23495 * @param {Roo.bootstrap.LocationPicker} this
23500 * Fires when the map show.
23501 * @param {Roo.bootstrap.LocationPicker} this
23506 * Fires when the map hide.
23507 * @param {Roo.bootstrap.LocationPicker} this
23512 * Fires when click the map.
23513 * @param {Roo.bootstrap.LocationPicker} this
23514 * @param {Map event} e
23518 * @event mapRightClick
23519 * Fires when right click the map.
23520 * @param {Roo.bootstrap.LocationPicker} this
23521 * @param {Map event} e
23523 mapRightClick : true,
23525 * @event markerClick
23526 * Fires when click the marker.
23527 * @param {Roo.bootstrap.LocationPicker} this
23528 * @param {Map event} e
23530 markerClick : true,
23532 * @event markerRightClick
23533 * Fires when right click the marker.
23534 * @param {Roo.bootstrap.LocationPicker} this
23535 * @param {Map event} e
23537 markerRightClick : true,
23539 * @event OverlayViewDraw
23540 * Fires when OverlayView Draw
23541 * @param {Roo.bootstrap.LocationPicker} this
23543 OverlayViewDraw : true,
23545 * @event OverlayViewOnAdd
23546 * Fires when OverlayView Draw
23547 * @param {Roo.bootstrap.LocationPicker} this
23549 OverlayViewOnAdd : true,
23551 * @event OverlayViewOnRemove
23552 * Fires when OverlayView Draw
23553 * @param {Roo.bootstrap.LocationPicker} this
23555 OverlayViewOnRemove : true,
23557 * @event OverlayViewShow
23558 * Fires when OverlayView Draw
23559 * @param {Roo.bootstrap.LocationPicker} this
23560 * @param {Pixel} cpx
23562 OverlayViewShow : true,
23564 * @event OverlayViewHide
23565 * Fires when OverlayView Draw
23566 * @param {Roo.bootstrap.LocationPicker} this
23568 OverlayViewHide : true
23573 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
23575 gMapContext: false,
23581 mapTypeControl: false,
23582 disableDoubleClickZoom: false,
23584 streetViewControl: false,
23588 enableAutocomplete: false,
23589 enableReverseGeocode: true,
23592 getAutoCreate: function()
23597 cls: 'roo-location-picker'
23603 initEvents: function(ct, position)
23605 if(!this.el.getWidth() || this.isApplied()){
23609 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23614 initial: function()
23616 if(!this.mapTypeId){
23617 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23620 this.gMapContext = this.GMapContext();
23622 this.initOverlayView();
23624 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23628 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23629 _this.setPosition(_this.gMapContext.marker.position);
23632 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23633 _this.fireEvent('mapClick', this, event);
23637 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23638 _this.fireEvent('mapRightClick', this, event);
23642 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23643 _this.fireEvent('markerClick', this, event);
23647 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23648 _this.fireEvent('markerRightClick', this, event);
23652 this.setPosition(this.gMapContext.location);
23654 this.fireEvent('initial', this, this.gMapContext.location);
23657 initOverlayView: function()
23661 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23665 _this.fireEvent('OverlayViewDraw', _this);
23670 _this.fireEvent('OverlayViewOnAdd', _this);
23673 onRemove: function()
23675 _this.fireEvent('OverlayViewOnRemove', _this);
23678 show: function(cpx)
23680 _this.fireEvent('OverlayViewShow', _this, cpx);
23685 _this.fireEvent('OverlayViewHide', _this);
23691 fromLatLngToContainerPixel: function(event)
23693 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23696 isApplied: function()
23698 return this.getGmapContext() == false ? false : true;
23701 getGmapContext: function()
23703 return this.gMapContext
23706 GMapContext: function()
23708 var position = new google.maps.LatLng(this.latitude, this.longitude);
23710 var _map = new google.maps.Map(this.el.dom, {
23713 mapTypeId: this.mapTypeId,
23714 mapTypeControl: this.mapTypeControl,
23715 disableDoubleClickZoom: this.disableDoubleClickZoom,
23716 scrollwheel: this.scrollwheel,
23717 streetViewControl: this.streetViewControl,
23718 locationName: this.locationName,
23719 draggable: this.draggable,
23720 enableAutocomplete: this.enableAutocomplete,
23721 enableReverseGeocode: this.enableReverseGeocode
23724 var _marker = new google.maps.Marker({
23725 position: position,
23727 title: this.markerTitle,
23728 draggable: this.draggable
23735 location: position,
23736 radius: this.radius,
23737 locationName: this.locationName,
23738 addressComponents: {
23739 formatted_address: null,
23740 addressLine1: null,
23741 addressLine2: null,
23743 streetNumber: null,
23747 stateOrProvince: null
23750 domContainer: this.el.dom,
23751 geodecoder: new google.maps.Geocoder()
23755 drawCircle: function(center, radius, options)
23757 if (this.gMapContext.circle != null) {
23758 this.gMapContext.circle.setMap(null);
23762 options = Roo.apply({}, options, {
23763 strokeColor: "#0000FF",
23764 strokeOpacity: .35,
23766 fillColor: "#0000FF",
23770 options.map = this.gMapContext.map;
23771 options.radius = radius;
23772 options.center = center;
23773 this.gMapContext.circle = new google.maps.Circle(options);
23774 return this.gMapContext.circle;
23780 setPosition: function(location)
23782 this.gMapContext.location = location;
23783 this.gMapContext.marker.setPosition(location);
23784 this.gMapContext.map.panTo(location);
23785 this.drawCircle(location, this.gMapContext.radius, {});
23789 if (this.gMapContext.settings.enableReverseGeocode) {
23790 this.gMapContext.geodecoder.geocode({
23791 latLng: this.gMapContext.location
23792 }, function(results, status) {
23794 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23795 _this.gMapContext.locationName = results[0].formatted_address;
23796 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23798 _this.fireEvent('positionchanged', this, location);
23805 this.fireEvent('positionchanged', this, location);
23810 google.maps.event.trigger(this.gMapContext.map, "resize");
23812 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23814 this.fireEvent('resize', this);
23817 setPositionByLatLng: function(latitude, longitude)
23819 this.setPosition(new google.maps.LatLng(latitude, longitude));
23822 getCurrentPosition: function()
23825 latitude: this.gMapContext.location.lat(),
23826 longitude: this.gMapContext.location.lng()
23830 getAddressName: function()
23832 return this.gMapContext.locationName;
23835 getAddressComponents: function()
23837 return this.gMapContext.addressComponents;
23840 address_component_from_google_geocode: function(address_components)
23844 for (var i = 0; i < address_components.length; i++) {
23845 var component = address_components[i];
23846 if (component.types.indexOf("postal_code") >= 0) {
23847 result.postalCode = component.short_name;
23848 } else if (component.types.indexOf("street_number") >= 0) {
23849 result.streetNumber = component.short_name;
23850 } else if (component.types.indexOf("route") >= 0) {
23851 result.streetName = component.short_name;
23852 } else if (component.types.indexOf("neighborhood") >= 0) {
23853 result.city = component.short_name;
23854 } else if (component.types.indexOf("locality") >= 0) {
23855 result.city = component.short_name;
23856 } else if (component.types.indexOf("sublocality") >= 0) {
23857 result.district = component.short_name;
23858 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23859 result.stateOrProvince = component.short_name;
23860 } else if (component.types.indexOf("country") >= 0) {
23861 result.country = component.short_name;
23865 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23866 result.addressLine2 = "";
23870 setZoomLevel: function(zoom)
23872 this.gMapContext.map.setZoom(zoom);
23885 this.fireEvent('show', this);
23896 this.fireEvent('hide', this);
23901 Roo.apply(Roo.bootstrap.LocationPicker, {
23903 OverlayView : function(map, options)
23905 options = options || {};
23919 * @class Roo.bootstrap.Alert
23920 * @extends Roo.bootstrap.Component
23921 * Bootstrap Alert class
23922 * @cfg {String} title The title of alert
23923 * @cfg {String} html The content of alert
23924 * @cfg {String} weight ( success | info | warning | danger )
23925 * @cfg {String} faicon font-awesomeicon
23928 * Create a new alert
23929 * @param {Object} config The config object
23933 Roo.bootstrap.Alert = function(config){
23934 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23938 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
23945 getAutoCreate : function()
23954 cls : 'roo-alert-icon'
23959 cls : 'roo-alert-title',
23964 cls : 'roo-alert-text',
23971 cfg.cn[0].cls += ' fa ' + this.faicon;
23975 cfg.cls += ' alert-' + this.weight;
23981 initEvents: function()
23983 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23986 setTitle : function(str)
23988 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23991 setText : function(str)
23993 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23996 setWeight : function(weight)
23999 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24002 this.weight = weight;
24004 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24007 setIcon : function(icon)
24010 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24015 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24036 * @class Roo.bootstrap.UploadCropbox
24037 * @extends Roo.bootstrap.Component
24038 * Bootstrap UploadCropbox class
24039 * @cfg {String} emptyText show when image has been loaded
24040 * @cfg {String} rotateNotify show when image too small to rotate
24041 * @cfg {Number} errorTimeout default 3000
24042 * @cfg {Number} minWidth default 300
24043 * @cfg {Number} minHeight default 300
24044 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24045 * @cfg {Boolean} isDocument (true|false) default false
24048 * Create a new UploadCropbox
24049 * @param {Object} config The config object
24052 Roo.bootstrap.UploadCropbox = function(config){
24053 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24057 * @event beforeselectfile
24058 * Fire before select file
24059 * @param {Roo.bootstrap.UploadCropbox} this
24061 "beforeselectfile" : true,
24064 * Fire after initEvent
24065 * @param {Roo.bootstrap.UploadCropbox} this
24070 * Fire after initEvent
24071 * @param {Roo.bootstrap.UploadCropbox} this
24072 * @param {String} data
24077 * Fire when preparing the file data
24078 * @param {Roo.bootstrap.UploadCropbox} this
24079 * @param {Object} file
24084 * Fire when get exception
24085 * @param {Roo.bootstrap.UploadCropbox} this
24086 * @param {Object} options
24088 "exception" : true,
24090 * @event beforeloadcanvas
24091 * Fire before load the canvas
24092 * @param {Roo.bootstrap.UploadCropbox} this
24093 * @param {String} src
24095 "beforeloadcanvas" : true,
24098 * Fire when trash image
24099 * @param {Roo.bootstrap.UploadCropbox} this
24104 * Fire when download the image
24105 * @param {Roo.bootstrap.UploadCropbox} this
24109 * @event footerbuttonclick
24110 * Fire when footerbuttonclick
24111 * @param {Roo.bootstrap.UploadCropbox} this
24112 * @param {String} type
24114 "footerbuttonclick" : true,
24118 * @param {Roo.bootstrap.UploadCropbox} this
24123 * Fire when rotate the image
24124 * @param {Roo.bootstrap.UploadCropbox} this
24125 * @param {String} pos
24130 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24133 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24135 emptyText : 'Click to upload image',
24136 rotateNotify : 'Image is too small to rotate',
24137 errorTimeout : 3000,
24151 cropType : 'image/jpeg',
24153 canvasLoaded : false,
24154 isDocument : false,
24156 getAutoCreate : function()
24160 cls : 'roo-upload-cropbox',
24164 cls : 'roo-upload-cropbox-body',
24165 style : 'cursor:pointer',
24169 cls : 'roo-upload-cropbox-preview'
24173 cls : 'roo-upload-cropbox-thumb'
24177 cls : 'roo-upload-cropbox-empty-notify',
24178 html : this.emptyText
24182 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24183 html : this.rotateNotify
24189 cls : 'roo-upload-cropbox-footer',
24192 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24202 onRender : function(ct, position)
24204 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24206 if (this.buttons.length) {
24208 Roo.each(this.buttons, function(bb) {
24210 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24212 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24218 initEvents : function()
24220 this.urlAPI = (window.createObjectURL && window) ||
24221 (window.URL && URL.revokeObjectURL && URL) ||
24222 (window.webkitURL && webkitURL);
24224 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24225 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24227 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24228 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24230 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24231 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24232 this.thumbEl.hide();
24234 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24235 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24237 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24238 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24239 this.errorEl.hide();
24241 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24242 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24243 this.footerEl.hide();
24245 this.setThumbBoxSize();
24251 this.fireEvent('initial', this);
24258 window.addEventListener("resize", function() { _this.resize(); } );
24260 this.bodyEl.on('click', this.beforeSelectFile, this);
24263 this.bodyEl.on('touchstart', this.onTouchStart, this);
24264 this.bodyEl.on('touchmove', this.onTouchMove, this);
24265 this.bodyEl.on('touchend', this.onTouchEnd, this);
24269 this.bodyEl.on('mousedown', this.onMouseDown, this);
24270 this.bodyEl.on('mousemove', this.onMouseMove, this);
24271 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24272 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24273 Roo.get(document).on('mouseup', this.onMouseUp, this);
24280 this.baseScale = 1;
24282 this.baseRotate = 1;
24283 this.dragable = false;
24284 this.pinching = false;
24287 this.cropData = false;
24288 this.notifyEl.dom.innerHTML = this.emptyText;
24292 resize : function()
24294 if(this.fireEvent('resize', this) != false){
24295 this.setThumbBoxPosition();
24296 this.setCanvasPosition();
24300 onFooterButtonClick : function(e, el, o, type)
24303 case 'rotate-left' :
24304 this.onRotateLeft(e);
24306 case 'rotate-right' :
24307 this.onRotateRight(e);
24310 this.beforeSelectFile(e);
24325 this.fireEvent('footerbuttonclick', this, type);
24328 beforeSelectFile : function(e)
24330 this.fireEvent('beforeselectfile', this);
24333 trash : function(e)
24335 this.fireEvent('trash', this);
24338 download : function(e)
24340 this.fireEvent('download', this);
24343 loadCanvas : function(src)
24345 if(this.fireEvent('beforeloadcanvas', this, src) != false){
24349 this.imageEl = document.createElement('img');
24353 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24355 this.imageEl.src = src;
24359 onLoadCanvas : function()
24361 this.bodyEl.un('click', this.beforeSelectFile, this);
24363 this.notifyEl.hide();
24364 this.thumbEl.show();
24365 this.footerEl.show();
24367 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24368 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24370 this.baseRotateLevel();
24372 if(this.isDocument){
24373 this.setThumbBoxSize();
24376 this.setThumbBoxPosition();
24378 this.baseScaleLevel();
24384 this.canvasLoaded = true;
24388 setCanvasPosition : function()
24390 if(!this.canvasEl){
24394 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24395 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24397 this.previewEl.setLeft(pw);
24398 this.previewEl.setTop(ph);
24402 onMouseDown : function(e)
24406 this.dragable = true;
24407 this.pinching = false;
24409 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24410 this.dragable = false;
24414 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24415 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24419 onMouseMove : function(e)
24423 if(!this.canvasLoaded){
24427 if (!this.dragable){
24431 var minX = Math.ceil(this.thumbEl.getLeft(true));
24432 var minY = Math.ceil(this.thumbEl.getTop(true));
24434 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24435 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24437 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24438 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24440 x = x - this.mouseX;
24441 y = y - this.mouseY;
24443 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24444 var bgY = Math.ceil(y + this.previewEl.getTop(true));
24446 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24447 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24449 this.previewEl.setLeft(bgX);
24450 this.previewEl.setTop(bgY);
24452 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24453 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24456 onMouseUp : function(e)
24460 this.dragable = false;
24463 onMouseWheel : function(e)
24467 this.startScale = this.scale;
24469 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24471 if(!this.zoomable()){
24472 this.scale = this.startScale;
24481 zoomable : function()
24483 var minScale = this.thumbEl.getWidth() / this.minWidth;
24485 if(this.minWidth < this.minHeight){
24486 minScale = this.thumbEl.getHeight() / this.minHeight;
24489 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24490 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24494 (this.rotate == 0 || this.rotate == 180) &&
24496 width > this.imageEl.OriginWidth ||
24497 height > this.imageEl.OriginHeight ||
24498 (width < this.minWidth && height < this.minHeight)
24506 (this.rotate == 90 || this.rotate == 270) &&
24508 width > this.imageEl.OriginWidth ||
24509 height > this.imageEl.OriginHeight ||
24510 (width < this.minHeight && height < this.minWidth)
24517 !this.isDocument &&
24518 (this.rotate == 0 || this.rotate == 180) &&
24520 width < this.minWidth ||
24521 width > this.imageEl.OriginWidth ||
24522 height < this.minHeight ||
24523 height > this.imageEl.OriginHeight
24530 !this.isDocument &&
24531 (this.rotate == 90 || this.rotate == 270) &&
24533 width < this.minHeight ||
24534 width > this.imageEl.OriginWidth ||
24535 height < this.minWidth ||
24536 height > this.imageEl.OriginHeight
24546 onRotateLeft : function(e)
24548 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24550 var minScale = this.thumbEl.getWidth() / this.minWidth;
24552 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24553 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24555 this.startScale = this.scale;
24557 while (this.getScaleLevel() < minScale){
24559 this.scale = this.scale + 1;
24561 if(!this.zoomable()){
24566 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24567 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24572 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24579 this.scale = this.startScale;
24581 this.onRotateFail();
24586 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24588 if(this.isDocument){
24589 this.setThumbBoxSize();
24590 this.setThumbBoxPosition();
24591 this.setCanvasPosition();
24596 this.fireEvent('rotate', this, 'left');
24600 onRotateRight : function(e)
24602 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24604 var minScale = this.thumbEl.getWidth() / this.minWidth;
24606 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24607 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24609 this.startScale = this.scale;
24611 while (this.getScaleLevel() < minScale){
24613 this.scale = this.scale + 1;
24615 if(!this.zoomable()){
24620 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24621 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24626 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24633 this.scale = this.startScale;
24635 this.onRotateFail();
24640 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24642 if(this.isDocument){
24643 this.setThumbBoxSize();
24644 this.setThumbBoxPosition();
24645 this.setCanvasPosition();
24650 this.fireEvent('rotate', this, 'right');
24653 onRotateFail : function()
24655 this.errorEl.show(true);
24659 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24664 this.previewEl.dom.innerHTML = '';
24666 var canvasEl = document.createElement("canvas");
24668 var contextEl = canvasEl.getContext("2d");
24670 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24671 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24672 var center = this.imageEl.OriginWidth / 2;
24674 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24675 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24676 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24677 center = this.imageEl.OriginHeight / 2;
24680 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24682 contextEl.translate(center, center);
24683 contextEl.rotate(this.rotate * Math.PI / 180);
24685 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24687 this.canvasEl = document.createElement("canvas");
24689 this.contextEl = this.canvasEl.getContext("2d");
24691 switch (this.rotate) {
24694 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24695 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24697 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24702 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24703 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24705 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24706 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);
24710 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24715 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24716 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24718 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24719 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);
24723 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);
24728 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24729 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24731 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24732 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24736 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);
24743 this.previewEl.appendChild(this.canvasEl);
24745 this.setCanvasPosition();
24750 if(!this.canvasLoaded){
24754 var imageCanvas = document.createElement("canvas");
24756 var imageContext = imageCanvas.getContext("2d");
24758 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24759 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24761 var center = imageCanvas.width / 2;
24763 imageContext.translate(center, center);
24765 imageContext.rotate(this.rotate * Math.PI / 180);
24767 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24769 var canvas = document.createElement("canvas");
24771 var context = canvas.getContext("2d");
24773 canvas.width = this.minWidth;
24774 canvas.height = this.minHeight;
24776 switch (this.rotate) {
24779 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24780 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24782 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24783 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24785 var targetWidth = this.minWidth - 2 * x;
24786 var targetHeight = this.minHeight - 2 * y;
24790 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24791 scale = targetWidth / width;
24794 if(x > 0 && y == 0){
24795 scale = targetHeight / height;
24798 if(x > 0 && y > 0){
24799 scale = targetWidth / width;
24801 if(width < height){
24802 scale = targetHeight / height;
24806 context.scale(scale, scale);
24808 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24809 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24811 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24812 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24814 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24819 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24820 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24822 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24823 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24825 var targetWidth = this.minWidth - 2 * x;
24826 var targetHeight = this.minHeight - 2 * y;
24830 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24831 scale = targetWidth / width;
24834 if(x > 0 && y == 0){
24835 scale = targetHeight / height;
24838 if(x > 0 && y > 0){
24839 scale = targetWidth / width;
24841 if(width < height){
24842 scale = targetHeight / height;
24846 context.scale(scale, scale);
24848 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24849 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24851 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24852 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24854 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24856 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24861 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24862 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24864 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24865 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24867 var targetWidth = this.minWidth - 2 * x;
24868 var targetHeight = this.minHeight - 2 * y;
24872 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24873 scale = targetWidth / width;
24876 if(x > 0 && y == 0){
24877 scale = targetHeight / height;
24880 if(x > 0 && y > 0){
24881 scale = targetWidth / width;
24883 if(width < height){
24884 scale = targetHeight / height;
24888 context.scale(scale, scale);
24890 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24891 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24893 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24894 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24896 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24897 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24899 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24904 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24905 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24907 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24908 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24910 var targetWidth = this.minWidth - 2 * x;
24911 var targetHeight = this.minHeight - 2 * y;
24915 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24916 scale = targetWidth / width;
24919 if(x > 0 && y == 0){
24920 scale = targetHeight / height;
24923 if(x > 0 && y > 0){
24924 scale = targetWidth / width;
24926 if(width < height){
24927 scale = targetHeight / height;
24931 context.scale(scale, scale);
24933 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24934 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24936 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24937 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24939 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24941 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24948 this.cropData = canvas.toDataURL(this.cropType);
24950 this.fireEvent('crop', this, this.cropData);
24954 setThumbBoxSize : function()
24958 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
24959 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
24960 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
24962 this.minWidth = width;
24963 this.minHeight = height;
24965 if(this.rotate == 90 || this.rotate == 270){
24966 this.minWidth = height;
24967 this.minHeight = width;
24972 width = Math.ceil(this.minWidth * height / this.minHeight);
24974 if(this.minWidth > this.minHeight){
24976 height = Math.ceil(this.minHeight * width / this.minWidth);
24979 this.thumbEl.setStyle({
24980 width : width + 'px',
24981 height : height + 'px'
24988 setThumbBoxPosition : function()
24990 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24991 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24993 this.thumbEl.setLeft(x);
24994 this.thumbEl.setTop(y);
24998 baseRotateLevel : function()
25000 this.baseRotate = 1;
25003 typeof(this.exif) != 'undefined' &&
25004 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25005 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25007 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25010 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25014 baseScaleLevel : function()
25018 if(this.isDocument){
25020 if(this.baseRotate == 6 || this.baseRotate == 8){
25022 height = this.thumbEl.getHeight();
25023 this.baseScale = height / this.imageEl.OriginWidth;
25025 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25026 width = this.thumbEl.getWidth();
25027 this.baseScale = width / this.imageEl.OriginHeight;
25033 height = this.thumbEl.getHeight();
25034 this.baseScale = height / this.imageEl.OriginHeight;
25036 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25037 width = this.thumbEl.getWidth();
25038 this.baseScale = width / this.imageEl.OriginWidth;
25044 if(this.baseRotate == 6 || this.baseRotate == 8){
25046 width = this.thumbEl.getHeight();
25047 this.baseScale = width / this.imageEl.OriginHeight;
25049 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25050 height = this.thumbEl.getWidth();
25051 this.baseScale = height / this.imageEl.OriginHeight;
25054 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25055 height = this.thumbEl.getWidth();
25056 this.baseScale = height / this.imageEl.OriginHeight;
25058 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25059 width = this.thumbEl.getHeight();
25060 this.baseScale = width / this.imageEl.OriginWidth;
25067 width = this.thumbEl.getWidth();
25068 this.baseScale = width / this.imageEl.OriginWidth;
25070 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25071 height = this.thumbEl.getHeight();
25072 this.baseScale = height / this.imageEl.OriginHeight;
25075 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25077 height = this.thumbEl.getHeight();
25078 this.baseScale = height / this.imageEl.OriginHeight;
25080 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25081 width = this.thumbEl.getWidth();
25082 this.baseScale = width / this.imageEl.OriginWidth;
25090 getScaleLevel : function()
25092 return this.baseScale * Math.pow(1.1, this.scale);
25095 onTouchStart : function(e)
25097 if(!this.canvasLoaded){
25098 this.beforeSelectFile(e);
25102 var touches = e.browserEvent.touches;
25108 if(touches.length == 1){
25109 this.onMouseDown(e);
25113 if(touches.length != 2){
25119 for(var i = 0, finger; finger = touches[i]; i++){
25120 coords.push(finger.pageX, finger.pageY);
25123 var x = Math.pow(coords[0] - coords[2], 2);
25124 var y = Math.pow(coords[1] - coords[3], 2);
25126 this.startDistance = Math.sqrt(x + y);
25128 this.startScale = this.scale;
25130 this.pinching = true;
25131 this.dragable = false;
25135 onTouchMove : function(e)
25137 if(!this.pinching && !this.dragable){
25141 var touches = e.browserEvent.touches;
25148 this.onMouseMove(e);
25154 for(var i = 0, finger; finger = touches[i]; i++){
25155 coords.push(finger.pageX, finger.pageY);
25158 var x = Math.pow(coords[0] - coords[2], 2);
25159 var y = Math.pow(coords[1] - coords[3], 2);
25161 this.endDistance = Math.sqrt(x + y);
25163 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25165 if(!this.zoomable()){
25166 this.scale = this.startScale;
25174 onTouchEnd : function(e)
25176 this.pinching = false;
25177 this.dragable = false;
25181 prepare : function(file)
25186 if(typeof(file) === 'string'){
25187 this.loadCanvas(file);
25191 if(!file || !this.urlAPI){
25196 this.cropType = file.type;
25200 if(this.fireEvent('prepare', this, this.file) != false){
25202 var reader = new FileReader();
25204 reader.onload = function (e) {
25205 if (e.target.error) {
25206 Roo.log(e.target.error);
25210 var buffer = e.target.result,
25211 dataView = new DataView(buffer),
25213 maxOffset = dataView.byteLength - 4,
25217 if (dataView.getUint16(0) === 0xffd8) {
25218 while (offset < maxOffset) {
25219 markerBytes = dataView.getUint16(offset);
25221 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25222 markerLength = dataView.getUint16(offset + 2) + 2;
25223 if (offset + markerLength > dataView.byteLength) {
25224 Roo.log('Invalid meta data: Invalid segment size.');
25228 if(markerBytes == 0xffe1){
25229 _this.parseExifData(
25236 offset += markerLength;
25246 var url = _this.urlAPI.createObjectURL(_this.file);
25248 _this.loadCanvas(url);
25253 reader.readAsArrayBuffer(this.file);
25259 parseExifData : function(dataView, offset, length)
25261 var tiffOffset = offset + 10,
25265 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25266 // No Exif data, might be XMP data instead
25270 // Check for the ASCII code for "Exif" (0x45786966):
25271 if (dataView.getUint32(offset + 4) !== 0x45786966) {
25272 // No Exif data, might be XMP data instead
25275 if (tiffOffset + 8 > dataView.byteLength) {
25276 Roo.log('Invalid Exif data: Invalid segment size.');
25279 // Check for the two null bytes:
25280 if (dataView.getUint16(offset + 8) !== 0x0000) {
25281 Roo.log('Invalid Exif data: Missing byte alignment offset.');
25284 // Check the byte alignment:
25285 switch (dataView.getUint16(tiffOffset)) {
25287 littleEndian = true;
25290 littleEndian = false;
25293 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25296 // Check for the TIFF tag marker (0x002A):
25297 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25298 Roo.log('Invalid Exif data: Missing TIFF marker.');
25301 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25302 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25304 this.parseExifTags(
25307 tiffOffset + dirOffset,
25312 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25317 if (dirOffset + 6 > dataView.byteLength) {
25318 Roo.log('Invalid Exif data: Invalid directory offset.');
25321 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25322 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25323 if (dirEndOffset + 4 > dataView.byteLength) {
25324 Roo.log('Invalid Exif data: Invalid directory size.');
25327 for (i = 0; i < tagsNumber; i += 1) {
25331 dirOffset + 2 + 12 * i, // tag offset
25335 // Return the offset to the next directory:
25336 return dataView.getUint32(dirEndOffset, littleEndian);
25339 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
25341 var tag = dataView.getUint16(offset, littleEndian);
25343 this.exif[tag] = this.getExifValue(
25347 dataView.getUint16(offset + 2, littleEndian), // tag type
25348 dataView.getUint32(offset + 4, littleEndian), // tag length
25353 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25355 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25364 Roo.log('Invalid Exif data: Invalid tag type.');
25368 tagSize = tagType.size * length;
25369 // Determine if the value is contained in the dataOffset bytes,
25370 // or if the value at the dataOffset is a pointer to the actual data:
25371 dataOffset = tagSize > 4 ?
25372 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25373 if (dataOffset + tagSize > dataView.byteLength) {
25374 Roo.log('Invalid Exif data: Invalid data offset.');
25377 if (length === 1) {
25378 return tagType.getValue(dataView, dataOffset, littleEndian);
25381 for (i = 0; i < length; i += 1) {
25382 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25385 if (tagType.ascii) {
25387 // Concatenate the chars:
25388 for (i = 0; i < values.length; i += 1) {
25390 // Ignore the terminating NULL byte(s):
25391 if (c === '\u0000') {
25403 Roo.apply(Roo.bootstrap.UploadCropbox, {
25405 'Orientation': 0x0112
25409 1: 0, //'top-left',
25411 3: 180, //'bottom-right',
25412 // 4: 'bottom-left',
25414 6: 90, //'right-top',
25415 // 7: 'right-bottom',
25416 8: 270 //'left-bottom'
25420 // byte, 8-bit unsigned int:
25422 getValue: function (dataView, dataOffset) {
25423 return dataView.getUint8(dataOffset);
25427 // ascii, 8-bit byte:
25429 getValue: function (dataView, dataOffset) {
25430 return String.fromCharCode(dataView.getUint8(dataOffset));
25435 // short, 16 bit int:
25437 getValue: function (dataView, dataOffset, littleEndian) {
25438 return dataView.getUint16(dataOffset, littleEndian);
25442 // long, 32 bit int:
25444 getValue: function (dataView, dataOffset, littleEndian) {
25445 return dataView.getUint32(dataOffset, littleEndian);
25449 // rational = two long values, first is numerator, second is denominator:
25451 getValue: function (dataView, dataOffset, littleEndian) {
25452 return dataView.getUint32(dataOffset, littleEndian) /
25453 dataView.getUint32(dataOffset + 4, littleEndian);
25457 // slong, 32 bit signed int:
25459 getValue: function (dataView, dataOffset, littleEndian) {
25460 return dataView.getInt32(dataOffset, littleEndian);
25464 // srational, two slongs, first is numerator, second is denominator:
25466 getValue: function (dataView, dataOffset, littleEndian) {
25467 return dataView.getInt32(dataOffset, littleEndian) /
25468 dataView.getInt32(dataOffset + 4, littleEndian);
25478 cls : 'btn-group roo-upload-cropbox-rotate-left',
25479 action : 'rotate-left',
25483 cls : 'btn btn-default',
25484 html : '<i class="fa fa-undo"></i>'
25490 cls : 'btn-group roo-upload-cropbox-picture',
25491 action : 'picture',
25495 cls : 'btn btn-default',
25496 html : '<i class="fa fa-picture-o"></i>'
25502 cls : 'btn-group roo-upload-cropbox-rotate-right',
25503 action : 'rotate-right',
25507 cls : 'btn btn-default',
25508 html : '<i class="fa fa-repeat"></i>'
25516 cls : 'btn-group roo-upload-cropbox-rotate-left',
25517 action : 'rotate-left',
25521 cls : 'btn btn-default',
25522 html : '<i class="fa fa-undo"></i>'
25528 cls : 'btn-group roo-upload-cropbox-download',
25529 action : 'download',
25533 cls : 'btn btn-default',
25534 html : '<i class="fa fa-download"></i>'
25540 cls : 'btn-group roo-upload-cropbox-crop',
25545 cls : 'btn btn-default',
25546 html : '<i class="fa fa-crop"></i>'
25552 cls : 'btn-group roo-upload-cropbox-trash',
25557 cls : 'btn btn-default',
25558 html : '<i class="fa fa-trash"></i>'
25564 cls : 'btn-group roo-upload-cropbox-rotate-right',
25565 action : 'rotate-right',
25569 cls : 'btn btn-default',
25570 html : '<i class="fa fa-repeat"></i>'
25578 cls : 'btn-group roo-upload-cropbox-rotate-left',
25579 action : 'rotate-left',
25583 cls : 'btn btn-default',
25584 html : '<i class="fa fa-undo"></i>'
25590 cls : 'btn-group roo-upload-cropbox-rotate-right',
25591 action : 'rotate-right',
25595 cls : 'btn btn-default',
25596 html : '<i class="fa fa-repeat"></i>'
25609 * @class Roo.bootstrap.DocumentManager
25610 * @extends Roo.bootstrap.Component
25611 * Bootstrap DocumentManager class
25612 * @cfg {String} paramName default 'imageUpload'
25613 * @cfg {String} method default POST
25614 * @cfg {String} url action url
25615 * @cfg {Number} boxes number of boxes default 12
25616 * @cfg {Boolean} multiple multiple upload default true
25617 * @cfg {Number} minWidth default 300
25618 * @cfg {Number} minHeight default 300
25619 * @cfg {Number} thumbSize default 300
25620 * @cfg {String} fieldLabel
25621 * @cfg {Number} labelWidth default 4
25622 * @cfg {String} labelAlign (left|top) default left
25623 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25626 * Create a new DocumentManager
25627 * @param {Object} config The config object
25630 Roo.bootstrap.DocumentManager = function(config){
25631 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25636 * Fire when initial the DocumentManager
25637 * @param {Roo.bootstrap.DocumentManager} this
25642 * inspect selected file
25643 * @param {Roo.bootstrap.DocumentManager} this
25644 * @param {File} file
25649 * Fire when xhr load exception
25650 * @param {Roo.bootstrap.DocumentManager} this
25651 * @param {XMLHttpRequest} xhr
25653 "exception" : true,
25656 * prepare the form data
25657 * @param {Roo.bootstrap.DocumentManager} this
25658 * @param {Object} formData
25663 * Fire when remove the file
25664 * @param {Roo.bootstrap.DocumentManager} this
25665 * @param {Object} file
25670 * Fire after refresh the file
25671 * @param {Roo.bootstrap.DocumentManager} this
25676 * Fire after click the image
25677 * @param {Roo.bootstrap.DocumentManager} this
25678 * @param {Object} file
25683 * Fire when upload a image and editable set to true
25684 * @param {Roo.bootstrap.DocumentManager} this
25685 * @param {Object} file
25692 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
25703 paramName : 'imageUpload',
25706 labelAlign : 'left',
25710 getAutoCreate : function()
25712 var managerWidget = {
25714 cls : 'roo-document-manager',
25718 cls : 'roo-document-manager-selector',
25723 cls : 'roo-document-manager-uploader',
25727 cls : 'roo-document-manager-upload-btn',
25728 html : '<i class="fa fa-plus"></i>'
25739 cls : 'column col-md-12',
25744 if(this.fieldLabel.length){
25749 cls : 'column col-md-12',
25750 html : this.fieldLabel
25754 cls : 'column col-md-12',
25759 if(this.labelAlign == 'left'){
25763 cls : 'column col-md-' + this.labelWidth,
25764 html : this.fieldLabel
25768 cls : 'column col-md-' + (12 - this.labelWidth),
25778 cls : 'row clearfix',
25786 initEvents : function()
25788 this.managerEl = this.el.select('.roo-document-manager', true).first();
25789 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25791 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25792 this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25793 this.selectorEl.hide();
25796 this.selectorEl.attr('multiple', 'multiple');
25799 this.selectorEl.on('change', this.onFileSelected, this);
25801 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25802 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25804 this.uploader.on('click', this.onUploaderClick, this);
25806 this.renderProgressDialog();
25810 window.addEventListener("resize", function() { _this.refresh(); } );
25812 this.fireEvent('initial', this);
25815 renderProgressDialog : function()
25819 this.progressDialog = new Roo.bootstrap.Modal({
25820 cls : 'roo-document-manager-progress-dialog',
25821 allow_close : false,
25831 btnclick : function() {
25832 _this.uploadCancel();
25838 this.progressDialog.render(Roo.get(document.body));
25840 this.progress = new Roo.bootstrap.Progress({
25841 cls : 'roo-document-manager-progress',
25846 this.progress.render(this.progressDialog.getChildContainer());
25848 this.progressBar = new Roo.bootstrap.ProgressBar({
25849 cls : 'roo-document-manager-progress-bar',
25852 aria_valuemax : 12,
25856 this.progressBar.render(this.progress.getChildContainer());
25859 onUploaderClick : function(e)
25861 e.preventDefault();
25862 this.selectorEl.dom.click();
25865 onFileSelected : function(e)
25867 e.preventDefault();
25869 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25873 Roo.each(this.selectorEl.dom.files, function(file){
25874 if(this.fireEvent('inspect', this, file) != false){
25875 this.files.push(file);
25885 this.selectorEl.dom.value = '';
25887 if(!this.files.length){
25891 if(this.files.length > this.boxes){
25892 this.files = this.files.slice(0, this.boxes);
25895 this.uploader.show();
25897 if(this.files.length > this.boxes - 1){
25898 this.uploader.hide();
25905 Roo.each(this.files, function(file){
25907 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25908 var f = this.renderPreview(file);
25913 this.delegates.push(
25915 _this.process(file);
25916 }).createDelegate(this)
25921 this.files = files;
25923 if(!this.delegates.length){
25928 this.progressBar.aria_valuemax = this.delegates.length;
25935 arrange : function()
25937 if(!this.delegates.length){
25938 this.progressDialog.hide();
25943 var delegate = this.delegates.shift();
25945 this.progressDialog.show();
25947 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
25949 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
25954 refresh : function()
25956 this.uploader.show();
25958 if(this.files.length > this.boxes - 1){
25959 this.uploader.hide();
25962 Roo.isTouch ? this.closable(false) : this.closable(true);
25964 this.fireEvent('refresh', this);
25967 onRemove : function(e, el, o)
25969 e.preventDefault();
25971 this.fireEvent('remove', this, o);
25975 remove : function(o)
25979 Roo.each(this.files, function(file){
25980 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25989 this.files = files;
25994 onClick : function(e, el, o)
25996 e.preventDefault();
25998 this.fireEvent('click', this, o);
26002 closable : function(closable)
26004 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26006 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26018 xhrOnLoad : function(xhr)
26020 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26024 if (xhr.readyState !== 4) {
26026 this.fireEvent('exception', this, xhr);
26030 var response = Roo.decode(xhr.responseText);
26032 if(!response.success){
26034 this.fireEvent('exception', this, xhr);
26038 var file = this.renderPreview(response.data);
26040 this.files.push(file);
26046 xhrOnError : function()
26048 Roo.log('xhr on error');
26050 var response = Roo.decode(xhr.responseText);
26057 process : function(file)
26059 if(this.editable && file.type.indexOf('image') != -1){
26060 this.fireEvent('edit', this, file);
26064 this.uploadStart(file, false);
26069 uploadStart : function(file, crop)
26071 this.xhr = new XMLHttpRequest();
26073 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26078 file.xhr = this.xhr;
26080 this.managerEl.createChild({
26082 cls : 'roo-document-manager-loading',
26086 tooltip : file.name,
26087 cls : 'roo-document-manager-thumb',
26088 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26094 this.xhr.open(this.method, this.url, true);
26097 "Accept": "application/json",
26098 "Cache-Control": "no-cache",
26099 "X-Requested-With": "XMLHttpRequest"
26102 for (var headerName in headers) {
26103 var headerValue = headers[headerName];
26105 this.xhr.setRequestHeader(headerName, headerValue);
26111 this.xhr.onload = function()
26113 _this.xhrOnLoad(_this.xhr);
26116 this.xhr.onerror = function()
26118 _this.xhrOnError(_this.xhr);
26121 var formData = new FormData();
26123 formData.append('returnHTML', 'NO');
26126 formData.append('crop', crop);
26129 formData.append(this.paramName, file, file.name);
26131 if(this.fireEvent('prepare', this, formData) != false){
26132 this.xhr.send(formData);
26136 uploadCancel : function()
26140 this.delegates = [];
26142 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26149 renderPreview : function(file)
26151 if(typeof(file.target) != 'undefined' && file.target){
26155 var previewEl = this.managerEl.createChild({
26157 cls : 'roo-document-manager-preview',
26161 tooltip : file.filename,
26162 cls : 'roo-document-manager-thumb',
26163 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26168 html : '<i class="fa fa-times-circle"></i>'
26173 var close = previewEl.select('button.close', true).first();
26175 close.on('click', this.onRemove, this, file);
26177 file.target = previewEl;
26179 var image = previewEl.select('img', true).first();
26183 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26185 image.on('click', this.onClick, this, file);
26191 onPreviewLoad : function(file, image)
26193 if(typeof(file.target) == 'undefined' || !file.target){
26197 var width = image.dom.naturalWidth || image.dom.width;
26198 var height = image.dom.naturalHeight || image.dom.height;
26200 if(width > height){
26201 file.target.addClass('wide');
26205 file.target.addClass('tall');
26216 * @class Roo.bootstrap.DocumentViewer
26217 * @extends Roo.bootstrap.Component
26218 * Bootstrap DocumentViewer class
26221 * Create a new DocumentViewer
26222 * @param {Object} config The config object
26225 Roo.bootstrap.DocumentViewer = function(config){
26226 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26231 * Fire after initEvent
26232 * @param {Roo.bootstrap.DocumentViewer} this
26238 * @param {Roo.bootstrap.DocumentViewer} this
26243 * Fire after trash button
26244 * @param {Roo.bootstrap.DocumentViewer} this
26251 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
26253 getAutoCreate : function()
26257 cls : 'roo-document-viewer',
26261 cls : 'roo-document-viewer-body',
26265 cls : 'roo-document-viewer-thumb',
26269 cls : 'roo-document-viewer-image'
26277 cls : 'roo-document-viewer-footer',
26280 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26288 cls : 'btn btn-default roo-document-viewer-trash',
26289 html : '<i class="fa fa-trash"></i>'
26302 initEvents : function()
26305 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26306 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26308 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26309 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26311 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26312 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26314 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26315 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26317 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26318 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26320 this.bodyEl.on('click', this.onClick, this);
26322 this.trashBtn.on('click', this.onTrash, this);
26326 initial : function()
26328 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26331 this.fireEvent('initial', this);
26335 onClick : function(e)
26337 e.preventDefault();
26339 this.fireEvent('click', this);
26342 onTrash : function(e)
26344 e.preventDefault();
26346 this.fireEvent('trash', this);