4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
14 * @class Roo.data.SortTypes
16 * Defines the default sorting (casting?) comparison functions used when sorting data.
18 Roo.data.SortTypes = {
20 * Default sort that does nothing
21 * @param {Mixed} s The value being converted
22 * @return {Mixed} The comparison value
29 * The regular expression used to strip tags
33 stripTagsRE : /<\/?[^>]+>/gi,
36 * Strips all HTML tags to sort on text only
37 * @param {Mixed} s The value being converted
38 * @return {String} The comparison value
41 return String(s).replace(this.stripTagsRE, "");
45 * Strips all HTML tags to sort on text only - Case insensitive
46 * @param {Mixed} s The value being converted
47 * @return {String} The comparison value
49 asUCText : function(s){
50 return String(s).toUpperCase().replace(this.stripTagsRE, "");
54 * Case insensitive string
55 * @param {Mixed} s The value being converted
56 * @return {String} The comparison value
58 asUCString : function(s) {
59 return String(s).toUpperCase();
64 * @param {Mixed} s The value being converted
65 * @return {Number} The comparison value
67 asDate : function(s) {
71 if(s instanceof Date){
74 return Date.parse(String(s));
79 * @param {Mixed} s The value being converted
80 * @return {Float} The comparison value
82 asFloat : function(s) {
83 var val = parseFloat(String(s).replace(/,/g, ""));
92 * @param {Mixed} s The value being converted
93 * @return {Number} The comparison value
96 var val = parseInt(String(s).replace(/,/g, ""));
104 * Ext JS Library 1.1.1
105 * Copyright(c) 2006-2007, Ext JS, LLC.
107 * Originally Released Under LGPL - original licence link has changed is not relivant.
110 * <script type="text/javascript">
114 * @class Roo.data.Record
115 * Instances of this class encapsulate both record <em>definition</em> information, and record
116 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117 * to access Records cached in an {@link Roo.data.Store} object.<br>
119 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
123 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
125 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126 * {@link #create}. The parameters are the same.
127 * @param {Array} data An associative Array of data values keyed by the field name.
128 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130 * not specified an integer id is generated.
132 Roo.data.Record = function(data, id){
133 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
138 * Generate a constructor for a specific record layout.
139 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141 * Each field definition object may contain the following properties: <ul>
142 * <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,
143 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146 * is being used, then this is a string containing the javascript expression to reference the data relative to
147 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148 * to the data item relative to the record element. If the mapping expression is the same as the field name,
149 * this may be omitted.</p></li>
150 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151 * <ul><li>auto (Default, implies no conversion)</li>
156 * <li>date</li></ul></p></li>
157 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160 * by the Reader into an object that will be stored in the Record. It is passed the
161 * following parameters:<ul>
162 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
164 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
166 * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168 {name: 'title', mapping: 'topic_title'},
169 {name: 'author', mapping: 'username'},
170 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171 {name: 'lastPost', mapping: 'post_time', type: 'date'},
172 {name: 'lastPoster', mapping: 'user2'},
173 {name: 'excerpt', mapping: 'post_text'}
176 var myNewRecord = new TopicRecord({
177 title: 'Do my job please',
180 lastPost: new Date(),
181 lastPoster: 'Animal',
182 excerpt: 'No way dude!'
184 myStore.add(myNewRecord);
189 Roo.data.Record.create = function(o){
191 f.superclass.constructor.apply(this, arguments);
193 Roo.extend(f, Roo.data.Record);
195 p.fields = new Roo.util.MixedCollection(false, function(field){
198 for(var i = 0, len = o.length; i < len; i++){
199 p.fields.add(new Roo.data.Field(o[i]));
201 f.getField = function(name){
202 return p.fields.get(name);
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
212 Roo.data.Record.prototype = {
214 * Readonly flag - true if this record has been modified.
223 join : function(store){
228 * Set the named field to the specified value.
229 * @param {String} name The name of the field to set.
230 * @param {Object} value The value to set the field to.
232 set : function(name, value){
233 if(this.data[name] == value){
240 if(typeof this.modified[name] == 'undefined'){
241 this.modified[name] = this.data[name];
243 this.data[name] = value;
244 if(!this.editing && this.store){
245 this.store.afterEdit(this);
250 * Get the value of the named field.
251 * @param {String} name The name of the field to get the value of.
252 * @return {Object} The value of the field.
254 get : function(name){
255 return this.data[name];
259 beginEdit : function(){
265 cancelEdit : function(){
266 this.editing = false;
267 delete this.modified;
271 endEdit : function(){
272 this.editing = false;
273 if(this.dirty && this.store){
274 this.store.afterEdit(this);
279 * Usually called by the {@link Roo.data.Store} which owns the Record.
280 * Rejects all changes made to the Record since either creation, or the last commit operation.
281 * Modified fields are reverted to their original values.
283 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284 * of reject operations.
287 var m = this.modified;
289 if(typeof m[n] != "function"){
294 delete this.modified;
295 this.editing = false;
297 this.store.afterReject(this);
302 * Usually called by the {@link Roo.data.Store} which owns the Record.
303 * Commits all changes made to the Record since either creation, or the last commit operation.
305 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306 * of commit operations.
310 delete this.modified;
311 this.editing = false;
313 this.store.afterCommit(this);
318 hasError : function(){
319 return this.error != null;
323 clearError : function(){
328 * Creates a copy of this record.
329 * @param {String} id (optional) A new record id if you don't want to use this record's id
332 copy : function(newId) {
333 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
337 * Ext JS Library 1.1.1
338 * Copyright(c) 2006-2007, Ext JS, LLC.
340 * Originally Released Under LGPL - original licence link has changed is not relivant.
343 * <script type="text/javascript">
349 * @class Roo.data.Store
350 * @extends Roo.util.Observable
351 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
354 * 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
355 * has no knowledge of the format of the data returned by the Proxy.<br>
357 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358 * instances from the data object. These records are cached and made available through accessor functions.
360 * Creates a new Store.
361 * @param {Object} config A config object containing the objects needed for the Store to access data,
362 * and read the data into Records.
364 Roo.data.Store = function(config){
365 this.data = new Roo.util.MixedCollection(false);
366 this.data.getKey = function(o){
369 this.baseParams = {};
376 "multisort" : "_multisort"
379 if(config && config.data){
380 this.inlineData = config.data;
384 Roo.apply(this, config);
386 if(this.reader){ // reader passed
387 this.reader = Roo.factory(this.reader, Roo.data);
388 this.reader.xmodule = this.xmodule || false;
389 if(!this.recordType){
390 this.recordType = this.reader.recordType;
392 if(this.reader.onMetaChange){
393 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
398 this.fields = this.recordType.prototype.fields;
405 * Fires when the data cache has changed, and a widget which is using this Store
406 * as a Record cache should refresh its view.
407 * @param {Store} this
412 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413 * @param {Store} this
414 * @param {Object} meta The JSON metadata
419 * Fires when Records have been added to the Store
420 * @param {Store} this
421 * @param {Roo.data.Record[]} records The array of Records added
422 * @param {Number} index The index at which the record(s) were added
427 * Fires when a Record has been removed from the Store
428 * @param {Store} this
429 * @param {Roo.data.Record} record The Record that was removed
430 * @param {Number} index The index at which the record was removed
435 * Fires when a Record has been updated
436 * @param {Store} this
437 * @param {Roo.data.Record} record The Record that was updated
438 * @param {String} operation The update operation being performed. Value may be one of:
441 Roo.data.Record.REJECT
442 Roo.data.Record.COMMIT
448 * Fires when the data cache has been cleared.
449 * @param {Store} this
454 * Fires before a request is made for a new data object. If the beforeload handler returns false
455 * the load action will be canceled.
456 * @param {Store} this
457 * @param {Object} options The loading options that were specified (see {@link #load} for details)
461 * @event beforeloadadd
462 * Fires after a new set of Records has been loaded.
463 * @param {Store} this
464 * @param {Roo.data.Record[]} records The Records that were loaded
465 * @param {Object} options The loading options that were specified (see {@link #load} for details)
467 beforeloadadd : true,
470 * Fires after a new set of Records has been loaded, before they are added to the store.
471 * @param {Store} this
472 * @param {Roo.data.Record[]} records The Records that were loaded
473 * @param {Object} options The loading options that were specified (see {@link #load} for details)
474 * @params {Object} return from reader
478 * @event loadexception
479 * Fires if an exception occurs in the Proxy during loading.
480 * Called with the signature of the Proxy's "loadexception" event.
481 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
484 * @param {Object} return from JsonData.reader() - success, totalRecords, records
485 * @param {Object} load options
486 * @param {Object} jsonData from your request (normally this contains the Exception)
492 this.proxy = Roo.factory(this.proxy, Roo.data);
493 this.proxy.xmodule = this.xmodule || false;
494 this.relayEvents(this.proxy, ["loadexception"]);
496 this.sortToggle = {};
497 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
499 Roo.data.Store.superclass.constructor.call(this);
502 this.loadData(this.inlineData);
503 delete this.inlineData;
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
509 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
510 * without a remote query - used by combo/forms at present.
514 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
517 * @cfg {Array} data Inline data to be loaded when the store is initialized.
520 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
524 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525 * on any HTTP request
528 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
531 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
535 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
541 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542 * loaded or when a record is removed. (defaults to false).
544 pruneModifiedRecords : false,
550 * Add Records to the Store and fires the add event.
551 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
553 add : function(records){
554 records = [].concat(records);
555 for(var i = 0, len = records.length; i < len; i++){
556 records[i].join(this);
558 var index = this.data.length;
559 this.data.addAll(records);
560 this.fireEvent("add", this, records, index);
564 * Remove a Record from the Store and fires the remove event.
565 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
567 remove : function(record){
568 var index = this.data.indexOf(record);
569 this.data.removeAt(index);
570 if(this.pruneModifiedRecords){
571 this.modified.remove(record);
573 this.fireEvent("remove", this, record, index);
577 * Remove all Records from the Store and fires the clear event.
579 removeAll : function(){
581 if(this.pruneModifiedRecords){
584 this.fireEvent("clear", this);
588 * Inserts Records to the Store at the given index and fires the add event.
589 * @param {Number} index The start index at which to insert the passed Records.
590 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592 insert : function(index, records){
593 records = [].concat(records);
594 for(var i = 0, len = records.length; i < len; i++){
595 this.data.insert(index, records[i]);
596 records[i].join(this);
598 this.fireEvent("add", this, records, index);
602 * Get the index within the cache of the passed Record.
603 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
604 * @return {Number} The index of the passed Record. Returns -1 if not found.
606 indexOf : function(record){
607 return this.data.indexOf(record);
611 * Get the index within the cache of the Record with the passed id.
612 * @param {String} id The id of the Record to find.
613 * @return {Number} The index of the Record. Returns -1 if not found.
615 indexOfId : function(id){
616 return this.data.indexOfKey(id);
620 * Get the Record with the specified id.
621 * @param {String} id The id of the Record to find.
622 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624 getById : function(id){
625 return this.data.key(id);
629 * Get the Record at the specified index.
630 * @param {Number} index The index of the Record to find.
631 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633 getAt : function(index){
634 return this.data.itemAt(index);
638 * Returns a range of Records between specified indices.
639 * @param {Number} startIndex (optional) The starting index (defaults to 0)
640 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
641 * @return {Roo.data.Record[]} An array of Records
643 getRange : function(start, end){
644 return this.data.getRange(start, end);
648 storeOptions : function(o){
649 o = Roo.apply({}, o);
652 this.lastOptions = o;
656 * Loads the Record cache from the configured Proxy using the configured Reader.
658 * If using remote paging, then the first load call must specify the <em>start</em>
659 * and <em>limit</em> properties in the options.params property to establish the initial
660 * position within the dataset, and the number of Records to cache on each read from the Proxy.
662 * <strong>It is important to note that for remote data sources, loading is asynchronous,
663 * and this call will return before the new data has been loaded. Perform any post-processing
664 * in a callback function, or in a "load" event handler.</strong>
666 * @param {Object} options An object containing properties which control loading options:<ul>
667 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
668 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
669 * passed the following arguments:<ul>
670 * <li>r : Roo.data.Record[]</li>
671 * <li>options: Options object from the load call</li>
672 * <li>success: Boolean success indicator</li></ul></li>
673 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
674 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
677 load : function(options){
678 options = options || {};
679 if(this.fireEvent("beforeload", this, options) !== false){
680 this.storeOptions(options);
681 var p = Roo.apply(options.params || {}, this.baseParams);
682 // if meta was not loaded from remote source.. try requesting it.
683 if (!this.reader.metaFromRemote) {
686 if(this.sortInfo && this.remoteSort){
687 var pn = this.paramNames;
688 p[pn["sort"]] = this.sortInfo.field;
689 p[pn["dir"]] = this.sortInfo.direction;
691 if (this.multiSort) {
692 var pn = this.paramNames;
693 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
696 this.proxy.load(p, this.reader, this.loadRecords, this, options);
701 * Reloads the Record cache from the configured Proxy using the configured Reader and
702 * the options from the last load operation performed.
703 * @param {Object} options (optional) An object containing properties which may override the options
704 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
705 * the most recently used options are reused).
707 reload : function(options){
708 this.load(Roo.applyIf(options||{}, this.lastOptions));
712 // Called as a callback by the Reader during a load operation.
713 loadRecords : function(o, options, success){
714 if(!o || success === false){
715 if(success !== false){
716 this.fireEvent("load", this, [], options, o);
718 if(options.callback){
719 options.callback.call(options.scope || this, [], options, false);
723 // if data returned failure - throw an exception.
724 if (o.success === false) {
725 // show a message if no listener is registered.
726 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
727 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729 // loadmask wil be hooked into this..
730 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
733 var r = o.records, t = o.totalRecords || r.length;
735 this.fireEvent("beforeloadadd", this, r, options, o);
737 if(!options || options.add !== true){
738 if(this.pruneModifiedRecords){
741 for(var i = 0, len = r.length; i < len; i++){
745 this.data = this.snapshot;
746 delete this.snapshot;
750 this.totalLength = t;
752 this.fireEvent("datachanged", this);
754 this.totalLength = Math.max(t, this.data.length+r.length);
757 this.fireEvent("load", this, r, options, o);
758 if(options.callback){
759 options.callback.call(options.scope || this, r, options, true);
765 * Loads data from a passed data block. A Reader which understands the format of the data
766 * must have been configured in the constructor.
767 * @param {Object} data The data block from which to read the Records. The format of the data expected
768 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
769 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
771 loadData : function(o, append){
772 var r = this.reader.readRecords(o);
773 this.loadRecords(r, {add: append}, true);
777 * Gets the number of cached records.
779 * <em>If using paging, this may not be the total size of the dataset. If the data object
780 * used by the Reader contains the dataset size, then the getTotalCount() function returns
781 * the data set size</em>
783 getCount : function(){
784 return this.data.length || 0;
788 * Gets the total number of records in the dataset as returned by the server.
790 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
791 * the dataset size</em>
793 getTotalCount : function(){
794 return this.totalLength || 0;
798 * Returns the sort state of the Store as an object with two properties:
800 field {String} The name of the field by which the Records are sorted
801 direction {String} The sort order, "ASC" or "DESC"
804 getSortState : function(){
805 return this.sortInfo;
809 applySort : function(){
810 if(this.sortInfo && !this.remoteSort){
811 var s = this.sortInfo, f = s.field;
812 var st = this.fields.get(f).sortType;
813 var fn = function(r1, r2){
814 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
815 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
817 this.data.sort(s.direction, fn);
818 if(this.snapshot && this.snapshot != this.data){
819 this.snapshot.sort(s.direction, fn);
825 * Sets the default sort column and order to be used by the next load operation.
826 * @param {String} fieldName The name of the field to sort by.
827 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
829 setDefaultSort : function(field, dir){
830 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
835 * If remote sorting is used, the sort is performed on the server, and the cache is
836 * reloaded. If local sorting is used, the cache is sorted internally.
837 * @param {String} fieldName The name of the field to sort by.
838 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840 sort : function(fieldName, dir){
841 var f = this.fields.get(fieldName);
843 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
845 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
846 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
851 this.sortToggle[f.name] = dir;
852 this.sortInfo = {field: f.name, direction: dir};
853 if(!this.remoteSort){
855 this.fireEvent("datachanged", this);
857 this.load(this.lastOptions);
862 * Calls the specified function for each of the Records in the cache.
863 * @param {Function} fn The function to call. The Record is passed as the first parameter.
864 * Returning <em>false</em> aborts and exits the iteration.
865 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
867 each : function(fn, scope){
868 this.data.each(fn, scope);
872 * Gets all records modified since the last commit. Modified records are persisted across load operations
873 * (e.g., during paging).
874 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
876 getModifiedRecords : function(){
877 return this.modified;
881 createFilterFn : function(property, value, anyMatch){
882 if(!value.exec){ // not a regex
883 value = String(value);
884 if(value.length == 0){
887 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
890 return value.test(r.data[property]);
895 * Sums the value of <i>property</i> for each record between start and end and returns the result.
896 * @param {String} property A field on your records
897 * @param {Number} start The record index to start at (defaults to 0)
898 * @param {Number} end The last record index to include (defaults to length - 1)
899 * @return {Number} The sum
901 sum : function(property, start, end){
902 var rs = this.data.items, v = 0;
904 end = (end || end === 0) ? end : rs.length-1;
906 for(var i = start; i <= end; i++){
907 v += (rs[i].data[property] || 0);
913 * Filter the records by a specified property.
914 * @param {String} field A field on your records
915 * @param {String/RegExp} value Either a string that the field
916 * should start with or a RegExp to test against the field
917 * @param {Boolean} anyMatch True to match any part not just the beginning
919 filter : function(property, value, anyMatch){
920 var fn = this.createFilterFn(property, value, anyMatch);
921 return fn ? this.filterBy(fn) : this.clearFilter();
925 * Filter by a function. The specified function will be called with each
926 * record in this data source. If the function returns true the record is included,
927 * otherwise it is filtered.
928 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
929 * @param {Object} scope (optional) The scope of the function (defaults to this)
931 filterBy : function(fn, scope){
932 this.snapshot = this.snapshot || this.data;
933 this.data = this.queryBy(fn, scope||this);
934 this.fireEvent("datachanged", this);
938 * Query the records by a specified property.
939 * @param {String} field A field on your records
940 * @param {String/RegExp} value Either a string that the field
941 * should start with or a RegExp to test against the field
942 * @param {Boolean} anyMatch True to match any part not just the beginning
943 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
945 query : function(property, value, anyMatch){
946 var fn = this.createFilterFn(property, value, anyMatch);
947 return fn ? this.queryBy(fn) : this.data.clone();
951 * Query by a function. The specified function will be called with each
952 * record in this data source. If the function returns true the record is included
954 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
955 * @param {Object} scope (optional) The scope of the function (defaults to this)
956 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
958 queryBy : function(fn, scope){
959 var data = this.snapshot || this.data;
960 return data.filterBy(fn, scope||this);
964 * Collects unique values for a particular dataIndex from this store.
965 * @param {String} dataIndex The property to collect
966 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
967 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
968 * @return {Array} An array of the unique values
970 collect : function(dataIndex, allowNull, bypassFilter){
971 var d = (bypassFilter === true && this.snapshot) ?
972 this.snapshot.items : this.data.items;
973 var v, sv, r = [], l = {};
974 for(var i = 0, len = d.length; i < len; i++){
975 v = d[i].data[dataIndex];
977 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
986 * Revert to a view of the Record cache with no filtering applied.
987 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
989 clearFilter : function(suppressEvent){
990 if(this.snapshot && this.snapshot != this.data){
991 this.data = this.snapshot;
992 delete this.snapshot;
993 if(suppressEvent !== true){
994 this.fireEvent("datachanged", this);
1000 afterEdit : function(record){
1001 if(this.modified.indexOf(record) == -1){
1002 this.modified.push(record);
1004 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1008 afterReject : function(record){
1009 this.modified.remove(record);
1010 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1014 afterCommit : function(record){
1015 this.modified.remove(record);
1016 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1020 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1021 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1023 commitChanges : function(){
1024 var m = this.modified.slice(0);
1026 for(var i = 0, len = m.length; i < len; i++){
1032 * Cancel outstanding changes on all changed records.
1034 rejectChanges : function(){
1035 var m = this.modified.slice(0);
1037 for(var i = 0, len = m.length; i < len; i++){
1042 onMetaChange : function(meta, rtype, o){
1043 this.recordType = rtype;
1044 this.fields = rtype.prototype.fields;
1045 delete this.snapshot;
1046 this.sortInfo = meta.sortInfo || this.sortInfo;
1048 this.fireEvent('metachange', this, this.reader.meta);
1051 moveIndex : function(data, type)
1053 var index = this.indexOf(data);
1055 var newIndex = index + type;
1059 this.insert(newIndex, data);
1064 * Ext JS Library 1.1.1
1065 * Copyright(c) 2006-2007, Ext JS, LLC.
1067 * Originally Released Under LGPL - original licence link has changed is not relivant.
1070 * <script type="text/javascript">
1074 * @class Roo.data.SimpleStore
1075 * @extends Roo.data.Store
1076 * Small helper class to make creating Stores from Array data easier.
1077 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1078 * @cfg {Array} fields An array of field definition objects, or field name strings.
1079 * @cfg {Array} data The multi-dimensional array of data
1081 * @param {Object} config
1083 Roo.data.SimpleStore = function(config){
1084 Roo.data.SimpleStore.superclass.constructor.call(this, {
1086 reader: new Roo.data.ArrayReader({
1089 Roo.data.Record.create(config.fields)
1091 proxy : new Roo.data.MemoryProxy(config.data)
1095 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1097 * Ext JS Library 1.1.1
1098 * Copyright(c) 2006-2007, Ext JS, LLC.
1100 * Originally Released Under LGPL - original licence link has changed is not relivant.
1103 * <script type="text/javascript">
1108 * @extends Roo.data.Store
1109 * @class Roo.data.JsonStore
1110 * Small helper class to make creating Stores for JSON data easier. <br/>
1112 var store = new Roo.data.JsonStore({
1113 url: 'get-images.php',
1115 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1118 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1119 * JsonReader and HttpProxy (unless inline data is provided).</b>
1120 * @cfg {Array} fields An array of field definition objects, or field name strings.
1122 * @param {Object} config
1124 Roo.data.JsonStore = function(c){
1125 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1126 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1127 reader: new Roo.data.JsonReader(c, c.fields)
1130 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1132 * Ext JS Library 1.1.1
1133 * Copyright(c) 2006-2007, Ext JS, LLC.
1135 * Originally Released Under LGPL - original licence link has changed is not relivant.
1138 * <script type="text/javascript">
1142 Roo.data.Field = function(config){
1143 if(typeof config == "string"){
1144 config = {name: config};
1146 Roo.apply(this, config);
1152 var st = Roo.data.SortTypes;
1153 // named sortTypes are supported, here we look them up
1154 if(typeof this.sortType == "string"){
1155 this.sortType = st[this.sortType];
1158 // set default sortType for strings and dates
1162 this.sortType = st.asUCString;
1165 this.sortType = st.asDate;
1168 this.sortType = st.none;
1173 var stripRe = /[\$,%]/g;
1175 // prebuilt conversion function for this field, instead of
1176 // switching every time we're reading a value
1178 var cv, dateFormat = this.dateFormat;
1183 cv = function(v){ return v; };
1186 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1190 return v !== undefined && v !== null && v !== '' ?
1191 parseInt(String(v).replace(stripRe, ""), 10) : '';
1196 return v !== undefined && v !== null && v !== '' ?
1197 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1202 cv = function(v){ return v === true || v === "true" || v == 1; };
1209 if(v instanceof Date){
1213 if(dateFormat == "timestamp"){
1214 return new Date(v*1000);
1216 return Date.parseDate(v, dateFormat);
1218 var parsed = Date.parse(v);
1219 return parsed ? new Date(parsed) : null;
1228 Roo.data.Field.prototype = {
1236 * Ext JS Library 1.1.1
1237 * Copyright(c) 2006-2007, Ext JS, LLC.
1239 * Originally Released Under LGPL - original licence link has changed is not relivant.
1242 * <script type="text/javascript">
1245 // Base class for reading structured data from a data source. This class is intended to be
1246 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1249 * @class Roo.data.DataReader
1250 * Base class for reading structured data from a data source. This class is intended to be
1251 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1254 Roo.data.DataReader = function(meta, recordType){
1258 this.recordType = recordType instanceof Array ?
1259 Roo.data.Record.create(recordType) : recordType;
1262 Roo.data.DataReader.prototype = {
1264 * Create an empty record
1265 * @param {Object} data (optional) - overlay some values
1266 * @return {Roo.data.Record} record created.
1268 newRow : function(d) {
1270 this.recordType.prototype.fields.each(function(c) {
1272 case 'int' : da[c.name] = 0; break;
1273 case 'date' : da[c.name] = new Date(); break;
1274 case 'float' : da[c.name] = 0.0; break;
1275 case 'boolean' : da[c.name] = false; break;
1276 default : da[c.name] = ""; break;
1280 return new this.recordType(Roo.apply(da, d));
1285 * Ext JS Library 1.1.1
1286 * Copyright(c) 2006-2007, Ext JS, LLC.
1288 * Originally Released Under LGPL - original licence link has changed is not relivant.
1291 * <script type="text/javascript">
1295 * @class Roo.data.DataProxy
1296 * @extends Roo.data.Observable
1297 * This class is an abstract base class for implementations which provide retrieval of
1298 * unformatted data objects.<br>
1300 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1301 * (of the appropriate type which knows how to parse the data object) to provide a block of
1302 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1304 * Custom implementations must implement the load method as described in
1305 * {@link Roo.data.HttpProxy#load}.
1307 Roo.data.DataProxy = function(){
1311 * Fires before a network request is made to retrieve a data object.
1312 * @param {Object} This DataProxy object.
1313 * @param {Object} params The params parameter to the load function.
1318 * Fires before the load method's callback is called.
1319 * @param {Object} This DataProxy object.
1320 * @param {Object} o The data object.
1321 * @param {Object} arg The callback argument object passed to the load function.
1325 * @event loadexception
1326 * Fires if an Exception occurs during data retrieval.
1327 * @param {Object} This DataProxy object.
1328 * @param {Object} o The data object.
1329 * @param {Object} arg The callback argument object passed to the load function.
1330 * @param {Object} e The Exception.
1332 loadexception : true
1334 Roo.data.DataProxy.superclass.constructor.call(this);
1337 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1340 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1344 * Ext JS Library 1.1.1
1345 * Copyright(c) 2006-2007, Ext JS, LLC.
1347 * Originally Released Under LGPL - original licence link has changed is not relivant.
1350 * <script type="text/javascript">
1353 * @class Roo.data.MemoryProxy
1354 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1355 * to the Reader when its load method is called.
1357 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1359 Roo.data.MemoryProxy = function(data){
1363 Roo.data.MemoryProxy.superclass.constructor.call(this);
1367 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1370 * Load data from the requested source (in this case an in-memory
1371 * data object passed to the constructor), read the data object into
1372 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1373 * process that block using the passed callback.
1374 * @param {Object} params This parameter is not used by the MemoryProxy class.
1375 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1376 * object into a block of Roo.data.Records.
1377 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1378 * The function must be passed <ul>
1379 * <li>The Record block object</li>
1380 * <li>The "arg" argument from the load function</li>
1381 * <li>A boolean success indicator</li>
1383 * @param {Object} scope The scope in which to call the callback
1384 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1386 load : function(params, reader, callback, scope, arg){
1387 params = params || {};
1390 result = reader.readRecords(this.data);
1392 this.fireEvent("loadexception", this, arg, null, e);
1393 callback.call(scope, null, arg, false);
1396 callback.call(scope, result, arg, true);
1400 update : function(params, records){
1405 * Ext JS Library 1.1.1
1406 * Copyright(c) 2006-2007, Ext JS, LLC.
1408 * Originally Released Under LGPL - original licence link has changed is not relivant.
1411 * <script type="text/javascript">
1414 * @class Roo.data.HttpProxy
1415 * @extends Roo.data.DataProxy
1416 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1417 * configured to reference a certain URL.<br><br>
1419 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1420 * from which the running page was served.<br><br>
1422 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1424 * Be aware that to enable the browser to parse an XML document, the server must set
1425 * the Content-Type header in the HTTP response to "text/xml".
1427 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1428 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1429 * will be used to make the request.
1431 Roo.data.HttpProxy = function(conn){
1432 Roo.data.HttpProxy.superclass.constructor.call(this);
1433 // is conn a conn config or a real conn?
1435 this.useAjax = !conn || !conn.events;
1439 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1440 // thse are take from connection...
1443 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1446 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1447 * extra parameters to each request made by this object. (defaults to undefined)
1450 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1451 * to each request made by this object. (defaults to undefined)
1454 * @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)
1457 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1460 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1466 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1470 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1471 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1472 * a finer-grained basis than the DataProxy events.
1474 getConnection : function(){
1475 return this.useAjax ? Roo.Ajax : this.conn;
1479 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1480 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1481 * process that block using the passed callback.
1482 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1483 * for the request to the remote server.
1484 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1485 * object into a block of Roo.data.Records.
1486 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1487 * The function must be passed <ul>
1488 * <li>The Record block object</li>
1489 * <li>The "arg" argument from the load function</li>
1490 * <li>A boolean success indicator</li>
1492 * @param {Object} scope The scope in which to call the callback
1493 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1495 load : function(params, reader, callback, scope, arg){
1496 if(this.fireEvent("beforeload", this, params) !== false){
1498 params : params || {},
1500 callback : callback,
1505 callback : this.loadResponse,
1509 Roo.applyIf(o, this.conn);
1510 if(this.activeRequest){
1511 Roo.Ajax.abort(this.activeRequest);
1513 this.activeRequest = Roo.Ajax.request(o);
1515 this.conn.request(o);
1518 callback.call(scope||this, null, arg, false);
1523 loadResponse : function(o, success, response){
1524 delete this.activeRequest;
1526 this.fireEvent("loadexception", this, o, response);
1527 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1532 result = o.reader.read(response);
1534 this.fireEvent("loadexception", this, o, response, e);
1535 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1539 this.fireEvent("load", this, o, o.request.arg);
1540 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1544 update : function(dataSet){
1549 updateResponse : function(dataSet){
1554 * Ext JS Library 1.1.1
1555 * Copyright(c) 2006-2007, Ext JS, LLC.
1557 * Originally Released Under LGPL - original licence link has changed is not relivant.
1560 * <script type="text/javascript">
1564 * @class Roo.data.ScriptTagProxy
1565 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1566 * other than the originating domain of the running page.<br><br>
1568 * <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
1569 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1571 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1572 * source code that is used as the source inside a <script> tag.<br><br>
1574 * In order for the browser to process the returned data, the server must wrap the data object
1575 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1576 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1577 * depending on whether the callback name was passed:
1580 boolean scriptTag = false;
1581 String cb = request.getParameter("callback");
1584 response.setContentType("text/javascript");
1586 response.setContentType("application/x-json");
1588 Writer out = response.getWriter();
1590 out.write(cb + "(");
1592 out.print(dataBlock.toJsonString());
1599 * @param {Object} config A configuration object.
1601 Roo.data.ScriptTagProxy = function(config){
1602 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1603 Roo.apply(this, config);
1604 this.head = document.getElementsByTagName("head")[0];
1607 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1609 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1611 * @cfg {String} url The URL from which to request the data object.
1614 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1618 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1619 * the server the name of the callback function set up by the load call to process the returned data object.
1620 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1621 * javascript output which calls this named function passing the data object as its only parameter.
1623 callbackParam : "callback",
1625 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1626 * name to the request.
1631 * Load data from the configured URL, read the data object into
1632 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1633 * process that block using the passed callback.
1634 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1635 * for the request to the remote server.
1636 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1637 * object into a block of Roo.data.Records.
1638 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1639 * The function must be passed <ul>
1640 * <li>The Record block object</li>
1641 * <li>The "arg" argument from the load function</li>
1642 * <li>A boolean success indicator</li>
1644 * @param {Object} scope The scope in which to call the callback
1645 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1647 load : function(params, reader, callback, scope, arg){
1648 if(this.fireEvent("beforeload", this, params) !== false){
1650 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1653 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1655 url += "&_dc=" + (new Date().getTime());
1657 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1660 cb : "stcCallback"+transId,
1661 scriptId : "stcScript"+transId,
1665 callback : callback,
1671 window[trans.cb] = function(o){
1672 conn.handleResponse(o, trans);
1675 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1677 if(this.autoAbort !== false){
1681 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1683 var script = document.createElement("script");
1684 script.setAttribute("src", url);
1685 script.setAttribute("type", "text/javascript");
1686 script.setAttribute("id", trans.scriptId);
1687 this.head.appendChild(script);
1691 callback.call(scope||this, null, arg, false);
1696 isLoading : function(){
1697 return this.trans ? true : false;
1701 * Abort the current server request.
1704 if(this.isLoading()){
1705 this.destroyTrans(this.trans);
1710 destroyTrans : function(trans, isLoaded){
1711 this.head.removeChild(document.getElementById(trans.scriptId));
1712 clearTimeout(trans.timeoutId);
1714 window[trans.cb] = undefined;
1716 delete window[trans.cb];
1719 // if hasn't been loaded, wait for load to remove it to prevent script error
1720 window[trans.cb] = function(){
1721 window[trans.cb] = undefined;
1723 delete window[trans.cb];
1730 handleResponse : function(o, trans){
1732 this.destroyTrans(trans, true);
1735 result = trans.reader.readRecords(o);
1737 this.fireEvent("loadexception", this, o, trans.arg, e);
1738 trans.callback.call(trans.scope||window, null, trans.arg, false);
1741 this.fireEvent("load", this, o, trans.arg);
1742 trans.callback.call(trans.scope||window, result, trans.arg, true);
1746 handleFailure : function(trans){
1748 this.destroyTrans(trans, false);
1749 this.fireEvent("loadexception", this, null, trans.arg);
1750 trans.callback.call(trans.scope||window, null, trans.arg, false);
1754 * Ext JS Library 1.1.1
1755 * Copyright(c) 2006-2007, Ext JS, LLC.
1757 * Originally Released Under LGPL - original licence link has changed is not relivant.
1760 * <script type="text/javascript">
1764 * @class Roo.data.JsonReader
1765 * @extends Roo.data.DataReader
1766 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1767 * based on mappings in a provided Roo.data.Record constructor.
1769 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1770 * in the reply previously.
1775 var RecordDef = Roo.data.Record.create([
1776 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1777 {name: 'occupation'} // This field will use "occupation" as the mapping.
1779 var myReader = new Roo.data.JsonReader({
1780 totalProperty: "results", // The property which contains the total dataset size (optional)
1781 root: "rows", // The property which contains an Array of row objects
1782 id: "id" // The property within each row object that provides an ID for the record (optional)
1786 * This would consume a JSON file like this:
1788 { 'results': 2, 'rows': [
1789 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1790 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1793 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1794 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1795 * paged from the remote server.
1796 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1797 * @cfg {String} root name of the property which contains the Array of row objects.
1798 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1799 * @cfg {Array} fields Array of field definition objects
1801 * Create a new JsonReader
1802 * @param {Object} meta Metadata configuration options
1803 * @param {Object} recordType Either an Array of field definition objects,
1804 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1806 Roo.data.JsonReader = function(meta, recordType){
1809 // set some defaults:
1811 totalProperty: 'total',
1812 successProperty : 'success',
1817 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1819 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1822 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1823 * Used by Store query builder to append _requestMeta to params.
1826 metaFromRemote : false,
1828 * This method is only used by a DataProxy which has retrieved data from a remote server.
1829 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1830 * @return {Object} data A data block which is used by an Roo.data.Store object as
1831 * a cache of Roo.data.Records.
1833 read : function(response){
1834 var json = response.responseText;
1836 var o = /* eval:var:o */ eval("("+json+")");
1838 throw {message: "JsonReader.read: Json object not found"};
1844 this.metaFromRemote = true;
1845 this.meta = o.metaData;
1846 this.recordType = Roo.data.Record.create(o.metaData.fields);
1847 this.onMetaChange(this.meta, this.recordType, o);
1849 return this.readRecords(o);
1852 // private function a store will implement
1853 onMetaChange : function(meta, recordType, o){
1860 simpleAccess: function(obj, subsc) {
1867 getJsonAccessor: function(){
1869 return function(expr) {
1871 return(re.test(expr))
1872 ? new Function("obj", "return obj." + expr)
1882 * Create a data block containing Roo.data.Records from an XML document.
1883 * @param {Object} o An object which contains an Array of row objects in the property specified
1884 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1885 * which contains the total size of the dataset.
1886 * @return {Object} data A data block which is used by an Roo.data.Store object as
1887 * a cache of Roo.data.Records.
1889 readRecords : function(o){
1891 * After any data loads, the raw JSON data is available for further custom processing.
1895 var s = this.meta, Record = this.recordType,
1896 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1898 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1900 if(s.totalProperty) {
1901 this.getTotal = this.getJsonAccessor(s.totalProperty);
1903 if(s.successProperty) {
1904 this.getSuccess = this.getJsonAccessor(s.successProperty);
1906 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1908 var g = this.getJsonAccessor(s.id);
1909 this.getId = function(rec) {
1911 return (r === undefined || r === "") ? null : r;
1914 this.getId = function(){return null;};
1917 for(var jj = 0; jj < fl; jj++){
1919 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1920 this.ef[jj] = this.getJsonAccessor(map);
1924 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1925 if(s.totalProperty){
1926 var vt = parseInt(this.getTotal(o), 10);
1931 if(s.successProperty){
1932 var vs = this.getSuccess(o);
1933 if(vs === false || vs === 'false'){
1938 for(var i = 0; i < c; i++){
1941 var id = this.getId(n);
1942 for(var j = 0; j < fl; j++){
1944 var v = this.ef[j](n);
1946 Roo.log('missing convert for ' + f.name);
1950 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1952 var record = new Record(values, id);
1954 records[i] = record;
1960 totalRecords : totalRecords
1965 * Ext JS Library 1.1.1
1966 * Copyright(c) 2006-2007, Ext JS, LLC.
1968 * Originally Released Under LGPL - original licence link has changed is not relivant.
1971 * <script type="text/javascript">
1975 * @class Roo.data.XmlReader
1976 * @extends Roo.data.DataReader
1977 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1978 * based on mappings in a provided Roo.data.Record constructor.<br><br>
1980 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1981 * header in the HTTP response must be set to "text/xml".</em>
1985 var RecordDef = Roo.data.Record.create([
1986 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1987 {name: 'occupation'} // This field will use "occupation" as the mapping.
1989 var myReader = new Roo.data.XmlReader({
1990 totalRecords: "results", // The element which contains the total dataset size (optional)
1991 record: "row", // The repeated element which contains row information
1992 id: "id" // The element within the row that provides an ID for the record (optional)
1996 * This would consume an XML file like this:
2000 <results>2</results>
2003 <name>Bill</name>
2004 <occupation>Gardener</occupation>
2008 <name>Ben</name>
2009 <occupation>Horticulturalist</occupation>
2013 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2014 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2015 * paged from the remote server.
2016 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2017 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2018 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2019 * a record identifier value.
2021 * Create a new XmlReader
2022 * @param {Object} meta Metadata configuration options
2023 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2024 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2025 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2027 Roo.data.XmlReader = function(meta, recordType){
2029 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2031 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2033 * This method is only used by a DataProxy which has retrieved data from a remote server.
2034 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2035 * to contain a method called 'responseXML' that returns an XML document object.
2036 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2037 * a cache of Roo.data.Records.
2039 read : function(response){
2040 var doc = response.responseXML;
2042 throw {message: "XmlReader.read: XML Document not available"};
2044 return this.readRecords(doc);
2048 * Create a data block containing Roo.data.Records from an XML document.
2049 * @param {Object} doc A parsed XML document.
2050 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2051 * a cache of Roo.data.Records.
2053 readRecords : function(doc){
2055 * After any data loads/reads, the raw XML Document is available for further custom processing.
2059 var root = doc.documentElement || doc;
2060 var q = Roo.DomQuery;
2061 var recordType = this.recordType, fields = recordType.prototype.fields;
2062 var sid = this.meta.id;
2063 var totalRecords = 0, success = true;
2064 if(this.meta.totalRecords){
2065 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2068 if(this.meta.success){
2069 var sv = q.selectValue(this.meta.success, root, true);
2070 success = sv !== false && sv !== 'false';
2073 var ns = q.select(this.meta.record, root);
2074 for(var i = 0, len = ns.length; i < len; i++) {
2077 var id = sid ? q.selectValue(sid, n) : undefined;
2078 for(var j = 0, jlen = fields.length; j < jlen; j++){
2079 var f = fields.items[j];
2080 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2084 var record = new recordType(values, id);
2086 records[records.length] = record;
2092 totalRecords : totalRecords || records.length
2097 * Ext JS Library 1.1.1
2098 * Copyright(c) 2006-2007, Ext JS, LLC.
2100 * Originally Released Under LGPL - original licence link has changed is not relivant.
2103 * <script type="text/javascript">
2107 * @class Roo.data.ArrayReader
2108 * @extends Roo.data.DataReader
2109 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2110 * Each element of that Array represents a row of data fields. The
2111 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2112 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2116 var RecordDef = Roo.data.Record.create([
2117 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2118 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2120 var myReader = new Roo.data.ArrayReader({
2121 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2125 * This would consume an Array like this:
2127 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2129 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2131 * Create a new JsonReader
2132 * @param {Object} meta Metadata configuration options.
2133 * @param {Object} recordType Either an Array of field definition objects
2134 * as specified to {@link Roo.data.Record#create},
2135 * or an {@link Roo.data.Record} object
2136 * created using {@link Roo.data.Record#create}.
2138 Roo.data.ArrayReader = function(meta, recordType){
2139 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2142 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2144 * Create a data block containing Roo.data.Records from an XML document.
2145 * @param {Object} o An Array of row objects which represents the dataset.
2146 * @return {Object} data A data block which is used by an Roo.data.Store object as
2147 * a cache of Roo.data.Records.
2149 readRecords : function(o){
2150 var sid = this.meta ? this.meta.id : null;
2151 var recordType = this.recordType, fields = recordType.prototype.fields;
2154 for(var i = 0; i < root.length; i++){
2157 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2158 for(var j = 0, jlen = fields.length; j < jlen; j++){
2159 var f = fields.items[j];
2160 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2161 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2165 var record = new recordType(values, id);
2167 records[records.length] = record;
2171 totalRecords : records.length
2176 * Ext JS Library 1.1.1
2177 * Copyright(c) 2006-2007, Ext JS, LLC.
2179 * Originally Released Under LGPL - original licence link has changed is not relivant.
2182 * <script type="text/javascript">
2187 * @class Roo.data.Tree
2188 * @extends Roo.util.Observable
2189 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2190 * in the tree have most standard DOM functionality.
2192 * @param {Node} root (optional) The root node
2194 Roo.data.Tree = function(root){
2197 * The root node for this tree
2202 this.setRootNode(root);
2207 * Fires when a new child node is appended to a node in this tree.
2208 * @param {Tree} tree The owner tree
2209 * @param {Node} parent The parent node
2210 * @param {Node} node The newly appended node
2211 * @param {Number} index The index of the newly appended node
2216 * Fires when a child node is removed from a node in this tree.
2217 * @param {Tree} tree The owner tree
2218 * @param {Node} parent The parent node
2219 * @param {Node} node The child node removed
2224 * Fires when a node is moved to a new location in the tree
2225 * @param {Tree} tree The owner tree
2226 * @param {Node} node The node moved
2227 * @param {Node} oldParent The old parent of this node
2228 * @param {Node} newParent The new parent of this node
2229 * @param {Number} index The index it was moved to
2234 * Fires when a new child node is inserted in a node in this tree.
2235 * @param {Tree} tree The owner tree
2236 * @param {Node} parent The parent node
2237 * @param {Node} node The child node inserted
2238 * @param {Node} refNode The child node the node was inserted before
2242 * @event beforeappend
2243 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2244 * @param {Tree} tree The owner tree
2245 * @param {Node} parent The parent node
2246 * @param {Node} node The child node to be appended
2248 "beforeappend" : true,
2250 * @event beforeremove
2251 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2252 * @param {Tree} tree The owner tree
2253 * @param {Node} parent The parent node
2254 * @param {Node} node The child node to be removed
2256 "beforeremove" : true,
2259 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2260 * @param {Tree} tree The owner tree
2261 * @param {Node} node The node being moved
2262 * @param {Node} oldParent The parent of the node
2263 * @param {Node} newParent The new parent the node is moving to
2264 * @param {Number} index The index it is being moved to
2266 "beforemove" : true,
2268 * @event beforeinsert
2269 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2270 * @param {Tree} tree The owner tree
2271 * @param {Node} parent The parent node
2272 * @param {Node} node The child node to be inserted
2273 * @param {Node} refNode The child node the node is being inserted before
2275 "beforeinsert" : true
2278 Roo.data.Tree.superclass.constructor.call(this);
2281 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2284 proxyNodeEvent : function(){
2285 return this.fireEvent.apply(this, arguments);
2289 * Returns the root node for this tree.
2292 getRootNode : function(){
2297 * Sets the root node for this tree.
2298 * @param {Node} node
2301 setRootNode : function(node){
2303 node.ownerTree = this;
2305 this.registerNode(node);
2310 * Gets a node in this tree by its id.
2311 * @param {String} id
2314 getNodeById : function(id){
2315 return this.nodeHash[id];
2318 registerNode : function(node){
2319 this.nodeHash[node.id] = node;
2322 unregisterNode : function(node){
2323 delete this.nodeHash[node.id];
2326 toString : function(){
2327 return "[Tree"+(this.id?" "+this.id:"")+"]";
2332 * @class Roo.data.Node
2333 * @extends Roo.util.Observable
2334 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2335 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2337 * @param {Object} attributes The attributes/config for the node
2339 Roo.data.Node = function(attributes){
2341 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2344 this.attributes = attributes || {};
2345 this.leaf = this.attributes.leaf;
2347 * The node id. @type String
2349 this.id = this.attributes.id;
2351 this.id = Roo.id(null, "ynode-");
2352 this.attributes.id = this.id;
2357 * All child nodes of this node. @type Array
2359 this.childNodes = [];
2360 if(!this.childNodes.indexOf){ // indexOf is a must
2361 this.childNodes.indexOf = function(o){
2362 for(var i = 0, len = this.length; i < len; i++){
2371 * The parent node for this node. @type Node
2373 this.parentNode = null;
2375 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2377 this.firstChild = null;
2379 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2381 this.lastChild = null;
2383 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2385 this.previousSibling = null;
2387 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2389 this.nextSibling = null;
2394 * Fires when a new child node is appended
2395 * @param {Tree} tree The owner tree
2396 * @param {Node} this This node
2397 * @param {Node} node The newly appended node
2398 * @param {Number} index The index of the newly appended node
2403 * Fires when a child node is removed
2404 * @param {Tree} tree The owner tree
2405 * @param {Node} this This node
2406 * @param {Node} node The removed node
2411 * Fires when this node is moved to a new location in the tree
2412 * @param {Tree} tree The owner tree
2413 * @param {Node} this This node
2414 * @param {Node} oldParent The old parent of this node
2415 * @param {Node} newParent The new parent of this node
2416 * @param {Number} index The index it was moved to
2421 * Fires when a new child node is inserted.
2422 * @param {Tree} tree The owner tree
2423 * @param {Node} this This node
2424 * @param {Node} node The child node inserted
2425 * @param {Node} refNode The child node the node was inserted before
2429 * @event beforeappend
2430 * Fires before a new child is appended, return false to cancel the append.
2431 * @param {Tree} tree The owner tree
2432 * @param {Node} this This node
2433 * @param {Node} node The child node to be appended
2435 "beforeappend" : true,
2437 * @event beforeremove
2438 * Fires before a child is removed, return false to cancel the remove.
2439 * @param {Tree} tree The owner tree
2440 * @param {Node} this This node
2441 * @param {Node} node The child node to be removed
2443 "beforeremove" : true,
2446 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2447 * @param {Tree} tree The owner tree
2448 * @param {Node} this This node
2449 * @param {Node} oldParent The parent of this node
2450 * @param {Node} newParent The new parent this node is moving to
2451 * @param {Number} index The index it is being moved to
2453 "beforemove" : true,
2455 * @event beforeinsert
2456 * Fires before a new child is inserted, return false to cancel the insert.
2457 * @param {Tree} tree The owner tree
2458 * @param {Node} this This node
2459 * @param {Node} node The child node to be inserted
2460 * @param {Node} refNode The child node the node is being inserted before
2462 "beforeinsert" : true
2464 this.listeners = this.attributes.listeners;
2465 Roo.data.Node.superclass.constructor.call(this);
2468 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2469 fireEvent : function(evtName){
2470 // first do standard event for this node
2471 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2474 // then bubble it up to the tree if the event wasn't cancelled
2475 var ot = this.getOwnerTree();
2477 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2485 * Returns true if this node is a leaf
2488 isLeaf : function(){
2489 return this.leaf === true;
2493 setFirstChild : function(node){
2494 this.firstChild = node;
2498 setLastChild : function(node){
2499 this.lastChild = node;
2504 * Returns true if this node is the last child of its parent
2507 isLast : function(){
2508 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2512 * Returns true if this node is the first child of its parent
2515 isFirst : function(){
2516 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2519 hasChildNodes : function(){
2520 return !this.isLeaf() && this.childNodes.length > 0;
2524 * Insert node(s) as the last child node of this node.
2525 * @param {Node/Array} node The node or Array of nodes to append
2526 * @return {Node} The appended node if single append, or null if an array was passed
2528 appendChild : function(node){
2530 if(node instanceof Array){
2532 }else if(arguments.length > 1){
2535 // if passed an array or multiple args do them one by one
2537 for(var i = 0, len = multi.length; i < len; i++) {
2538 this.appendChild(multi[i]);
2541 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2544 var index = this.childNodes.length;
2545 var oldParent = node.parentNode;
2546 // it's a move, make sure we move it cleanly
2548 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2551 oldParent.removeChild(node);
2553 index = this.childNodes.length;
2555 this.setFirstChild(node);
2557 this.childNodes.push(node);
2558 node.parentNode = this;
2559 var ps = this.childNodes[index-1];
2561 node.previousSibling = ps;
2562 ps.nextSibling = node;
2564 node.previousSibling = null;
2566 node.nextSibling = null;
2567 this.setLastChild(node);
2568 node.setOwnerTree(this.getOwnerTree());
2569 this.fireEvent("append", this.ownerTree, this, node, index);
2571 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2578 * Removes a child node from this node.
2579 * @param {Node} node The node to remove
2580 * @return {Node} The removed node
2582 removeChild : function(node){
2583 var index = this.childNodes.indexOf(node);
2587 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2591 // remove it from childNodes collection
2592 this.childNodes.splice(index, 1);
2595 if(node.previousSibling){
2596 node.previousSibling.nextSibling = node.nextSibling;
2598 if(node.nextSibling){
2599 node.nextSibling.previousSibling = node.previousSibling;
2602 // update child refs
2603 if(this.firstChild == node){
2604 this.setFirstChild(node.nextSibling);
2606 if(this.lastChild == node){
2607 this.setLastChild(node.previousSibling);
2610 node.setOwnerTree(null);
2611 // clear any references from the node
2612 node.parentNode = null;
2613 node.previousSibling = null;
2614 node.nextSibling = null;
2615 this.fireEvent("remove", this.ownerTree, this, node);
2620 * Inserts the first node before the second node in this nodes childNodes collection.
2621 * @param {Node} node The node to insert
2622 * @param {Node} refNode The node to insert before (if null the node is appended)
2623 * @return {Node} The inserted node
2625 insertBefore : function(node, refNode){
2626 if(!refNode){ // like standard Dom, refNode can be null for append
2627 return this.appendChild(node);
2630 if(node == refNode){
2634 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2637 var index = this.childNodes.indexOf(refNode);
2638 var oldParent = node.parentNode;
2639 var refIndex = index;
2641 // when moving internally, indexes will change after remove
2642 if(oldParent == this && this.childNodes.indexOf(node) < index){
2646 // it's a move, make sure we move it cleanly
2648 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2651 oldParent.removeChild(node);
2654 this.setFirstChild(node);
2656 this.childNodes.splice(refIndex, 0, node);
2657 node.parentNode = this;
2658 var ps = this.childNodes[refIndex-1];
2660 node.previousSibling = ps;
2661 ps.nextSibling = node;
2663 node.previousSibling = null;
2665 node.nextSibling = refNode;
2666 refNode.previousSibling = node;
2667 node.setOwnerTree(this.getOwnerTree());
2668 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2670 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2676 * Returns the child node at the specified index.
2677 * @param {Number} index
2680 item : function(index){
2681 return this.childNodes[index];
2685 * Replaces one child node in this node with another.
2686 * @param {Node} newChild The replacement node
2687 * @param {Node} oldChild The node to replace
2688 * @return {Node} The replaced node
2690 replaceChild : function(newChild, oldChild){
2691 this.insertBefore(newChild, oldChild);
2692 this.removeChild(oldChild);
2697 * Returns the index of a child node
2698 * @param {Node} node
2699 * @return {Number} The index of the node or -1 if it was not found
2701 indexOf : function(child){
2702 return this.childNodes.indexOf(child);
2706 * Returns the tree this node is in.
2709 getOwnerTree : function(){
2710 // if it doesn't have one, look for one
2711 if(!this.ownerTree){
2715 this.ownerTree = p.ownerTree;
2721 return this.ownerTree;
2725 * Returns depth of this node (the root node has a depth of 0)
2728 getDepth : function(){
2731 while(p.parentNode){
2739 setOwnerTree : function(tree){
2740 // if it's move, we need to update everyone
2741 if(tree != this.ownerTree){
2743 this.ownerTree.unregisterNode(this);
2745 this.ownerTree = tree;
2746 var cs = this.childNodes;
2747 for(var i = 0, len = cs.length; i < len; i++) {
2748 cs[i].setOwnerTree(tree);
2751 tree.registerNode(this);
2757 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2758 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2759 * @return {String} The path
2761 getPath : function(attr){
2762 attr = attr || "id";
2763 var p = this.parentNode;
2764 var b = [this.attributes[attr]];
2766 b.unshift(p.attributes[attr]);
2769 var sep = this.getOwnerTree().pathSeparator;
2770 return sep + b.join(sep);
2774 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2775 * function call will be the scope provided or the current node. The arguments to the function
2776 * will be the args provided or the current node. If the function returns false at any point,
2777 * the bubble is stopped.
2778 * @param {Function} fn The function to call
2779 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2780 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2782 bubble : function(fn, scope, args){
2785 if(fn.call(scope || p, args || p) === false){
2793 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794 * function call will be the scope provided or the current node. The arguments to the function
2795 * will be the args provided or the current node. If the function returns false at any point,
2796 * the cascade is stopped on that branch.
2797 * @param {Function} fn The function to call
2798 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2801 cascade : function(fn, scope, args){
2802 if(fn.call(scope || this, args || this) !== false){
2803 var cs = this.childNodes;
2804 for(var i = 0, len = cs.length; i < len; i++) {
2805 cs[i].cascade(fn, scope, args);
2811 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2812 * function call will be the scope provided or the current node. The arguments to the function
2813 * will be the args provided or the current node. If the function returns false at any point,
2814 * the iteration stops.
2815 * @param {Function} fn The function to call
2816 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2817 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2819 eachChild : function(fn, scope, args){
2820 var cs = this.childNodes;
2821 for(var i = 0, len = cs.length; i < len; i++) {
2822 if(fn.call(scope || this, args || cs[i]) === false){
2829 * Finds the first child that has the attribute with the specified value.
2830 * @param {String} attribute The attribute name
2831 * @param {Mixed} value The value to search for
2832 * @return {Node} The found child or null if none was found
2834 findChild : function(attribute, value){
2835 var cs = this.childNodes;
2836 for(var i = 0, len = cs.length; i < len; i++) {
2837 if(cs[i].attributes[attribute] == value){
2845 * Finds the first child by a custom function. The child matches if the function passed
2847 * @param {Function} fn
2848 * @param {Object} scope (optional)
2849 * @return {Node} The found child or null if none was found
2851 findChildBy : function(fn, scope){
2852 var cs = this.childNodes;
2853 for(var i = 0, len = cs.length; i < len; i++) {
2854 if(fn.call(scope||cs[i], cs[i]) === true){
2862 * Sorts this nodes children using the supplied sort function
2863 * @param {Function} fn
2864 * @param {Object} scope (optional)
2866 sort : function(fn, scope){
2867 var cs = this.childNodes;
2868 var len = cs.length;
2870 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2872 for(var i = 0; i < len; i++){
2874 n.previousSibling = cs[i-1];
2875 n.nextSibling = cs[i+1];
2877 this.setFirstChild(n);
2880 this.setLastChild(n);
2887 * Returns true if this node is an ancestor (at any point) of the passed node.
2888 * @param {Node} node
2891 contains : function(node){
2892 return node.isAncestor(this);
2896 * Returns true if the passed node is an ancestor (at any point) of this node.
2897 * @param {Node} node
2900 isAncestor : function(node){
2901 var p = this.parentNode;
2911 toString : function(){
2912 return "[Node"+(this.id?" "+this.id:"")+"]";
2916 * Ext JS Library 1.1.1
2917 * Copyright(c) 2006-2007, Ext JS, LLC.
2919 * Originally Released Under LGPL - original licence link has changed is not relivant.
2922 * <script type="text/javascript">
2927 * @extends Roo.Element
2928 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2929 * automatic maintaining of shadow/shim positions.
2930 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2931 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2932 * you can pass a string with a CSS class name. False turns off the shadow.
2933 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2934 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2935 * @cfg {String} cls CSS class to add to the element
2936 * @cfg {Number} zindex Starting z-index (defaults to 11000)
2937 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2939 * @param {Object} config An object with config options.
2940 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2943 Roo.Layer = function(config, existingEl){
2944 config = config || {};
2945 var dh = Roo.DomHelper;
2946 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2948 this.dom = Roo.getDom(existingEl);
2951 var o = config.dh || {tag: "div", cls: "x-layer"};
2952 this.dom = dh.append(pel, o);
2955 this.addClass(config.cls);
2957 this.constrain = config.constrain !== false;
2958 this.visibilityMode = Roo.Element.VISIBILITY;
2960 this.id = this.dom.id = config.id;
2962 this.id = Roo.id(this.dom);
2964 this.zindex = config.zindex || this.getZIndex();
2965 this.position("absolute", this.zindex);
2967 this.shadowOffset = config.shadowOffset || 4;
2968 this.shadow = new Roo.Shadow({
2969 offset : this.shadowOffset,
2970 mode : config.shadow
2973 this.shadowOffset = 0;
2975 this.useShim = config.shim !== false && Roo.useShims;
2976 this.useDisplay = config.useDisplay;
2980 var supr = Roo.Element.prototype;
2982 // shims are shared among layer to keep from having 100 iframes
2985 Roo.extend(Roo.Layer, Roo.Element, {
2987 getZIndex : function(){
2988 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
2991 getShim : function(){
2998 var shim = shims.shift();
3000 shim = this.createShim();
3001 shim.enableDisplayMode('block');
3002 shim.dom.style.display = 'none';
3003 shim.dom.style.visibility = 'visible';
3005 var pn = this.dom.parentNode;
3006 if(shim.dom.parentNode != pn){
3007 pn.insertBefore(shim.dom, this.dom);
3009 shim.setStyle('z-index', this.getZIndex()-2);
3014 hideShim : function(){
3016 this.shim.setDisplayed(false);
3017 shims.push(this.shim);
3022 disableShadow : function(){
3024 this.shadowDisabled = true;
3026 this.lastShadowOffset = this.shadowOffset;
3027 this.shadowOffset = 0;
3031 enableShadow : function(show){
3033 this.shadowDisabled = false;
3034 this.shadowOffset = this.lastShadowOffset;
3035 delete this.lastShadowOffset;
3043 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3044 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3045 sync : function(doShow){
3046 var sw = this.shadow;
3047 if(!this.updating && this.isVisible() && (sw || this.useShim)){
3048 var sh = this.getShim();
3050 var w = this.getWidth(),
3051 h = this.getHeight();
3053 var l = this.getLeft(true),
3054 t = this.getTop(true);
3056 if(sw && !this.shadowDisabled){
3057 if(doShow && !sw.isVisible()){
3060 sw.realign(l, t, w, h);
3066 // fit the shim behind the shadow, so it is shimmed too
3067 var a = sw.adjusts, s = sh.dom.style;
3068 s.left = (Math.min(l, l+a.l))+"px";
3069 s.top = (Math.min(t, t+a.t))+"px";
3070 s.width = (w+a.w)+"px";
3071 s.height = (h+a.h)+"px";
3078 sh.setLeftTop(l, t);
3085 destroy : function(){
3090 this.removeAllListeners();
3091 var pn = this.dom.parentNode;
3093 pn.removeChild(this.dom);
3095 Roo.Element.uncache(this.id);
3098 remove : function(){
3103 beginUpdate : function(){
3104 this.updating = true;
3108 endUpdate : function(){
3109 this.updating = false;
3114 hideUnders : function(negOffset){
3122 constrainXY : function(){
3124 var vw = Roo.lib.Dom.getViewWidth(),
3125 vh = Roo.lib.Dom.getViewHeight();
3126 var s = Roo.get(document).getScroll();
3128 var xy = this.getXY();
3129 var x = xy[0], y = xy[1];
3130 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3131 // only move it if it needs it
3133 // first validate right/bottom
3134 if((x + w) > vw+s.left){
3135 x = vw - w - this.shadowOffset;
3138 if((y + h) > vh+s.top){
3139 y = vh - h - this.shadowOffset;
3142 // then make sure top/left isn't negative
3153 var ay = this.avoidY;
3154 if(y <= ay && (y+h) >= ay){
3160 supr.setXY.call(this, xy);
3166 isVisible : function(){
3167 return this.visible;
3171 showAction : function(){
3172 this.visible = true; // track visibility to prevent getStyle calls
3173 if(this.useDisplay === true){
3174 this.setDisplayed("");
3175 }else if(this.lastXY){
3176 supr.setXY.call(this, this.lastXY);
3177 }else if(this.lastLT){
3178 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3183 hideAction : function(){
3184 this.visible = false;
3185 if(this.useDisplay === true){
3186 this.setDisplayed(false);
3188 this.setLeftTop(-10000,-10000);
3192 // overridden Element method
3193 setVisible : function(v, a, d, c, e){
3198 var cb = function(){
3203 }.createDelegate(this);
3204 supr.setVisible.call(this, true, true, d, cb, e);
3207 this.hideUnders(true);
3216 }.createDelegate(this);
3218 supr.setVisible.call(this, v, a, d, cb, e);
3227 storeXY : function(xy){
3232 storeLeftTop : function(left, top){
3234 this.lastLT = [left, top];
3238 beforeFx : function(){
3239 this.beforeAction();
3240 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3244 afterFx : function(){
3245 Roo.Layer.superclass.afterFx.apply(this, arguments);
3246 this.sync(this.isVisible());
3250 beforeAction : function(){
3251 if(!this.updating && this.shadow){
3256 // overridden Element method
3257 setLeft : function(left){
3258 this.storeLeftTop(left, this.getTop(true));
3259 supr.setLeft.apply(this, arguments);
3263 setTop : function(top){
3264 this.storeLeftTop(this.getLeft(true), top);
3265 supr.setTop.apply(this, arguments);
3269 setLeftTop : function(left, top){
3270 this.storeLeftTop(left, top);
3271 supr.setLeftTop.apply(this, arguments);
3275 setXY : function(xy, a, d, c, e){
3277 this.beforeAction();
3279 var cb = this.createCB(c);
3280 supr.setXY.call(this, xy, a, d, cb, e);
3287 createCB : function(c){
3298 // overridden Element method
3299 setX : function(x, a, d, c, e){
3300 this.setXY([x, this.getY()], a, d, c, e);
3303 // overridden Element method
3304 setY : function(y, a, d, c, e){
3305 this.setXY([this.getX(), y], a, d, c, e);
3308 // overridden Element method
3309 setSize : function(w, h, a, d, c, e){
3310 this.beforeAction();
3311 var cb = this.createCB(c);
3312 supr.setSize.call(this, w, h, a, d, cb, e);
3318 // overridden Element method
3319 setWidth : function(w, a, d, c, e){
3320 this.beforeAction();
3321 var cb = this.createCB(c);
3322 supr.setWidth.call(this, w, a, d, cb, e);
3328 // overridden Element method
3329 setHeight : function(h, a, d, c, e){
3330 this.beforeAction();
3331 var cb = this.createCB(c);
3332 supr.setHeight.call(this, h, a, d, cb, e);
3338 // overridden Element method
3339 setBounds : function(x, y, w, h, a, d, c, e){
3340 this.beforeAction();
3341 var cb = this.createCB(c);
3343 this.storeXY([x, y]);
3344 supr.setXY.call(this, [x, y]);
3345 supr.setSize.call(this, w, h, a, d, cb, e);
3348 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3354 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3355 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3356 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3357 * @param {Number} zindex The new z-index to set
3358 * @return {this} The Layer
3360 setZIndex : function(zindex){
3361 this.zindex = zindex;
3362 this.setStyle("z-index", zindex + 2);
3364 this.shadow.setZIndex(zindex + 1);
3367 this.shim.setStyle("z-index", zindex);
3373 * Ext JS Library 1.1.1
3374 * Copyright(c) 2006-2007, Ext JS, LLC.
3376 * Originally Released Under LGPL - original licence link has changed is not relivant.
3379 * <script type="text/javascript">
3385 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3386 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3387 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3389 * Create a new Shadow
3390 * @param {Object} config The config object
3392 Roo.Shadow = function(config){
3393 Roo.apply(this, config);
3394 if(typeof this.mode != "string"){
3395 this.mode = this.defaultMode;
3397 var o = this.offset, a = {h: 0};
3398 var rad = Math.floor(this.offset/2);
3399 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3405 a.l -= this.offset + rad;
3406 a.t -= this.offset + rad;
3417 a.l -= (this.offset - rad);
3418 a.t -= this.offset + rad;
3420 a.w -= (this.offset - rad)*2;
3431 a.l -= (this.offset - rad);
3432 a.t -= (this.offset - rad);
3434 a.w -= (this.offset + rad + 1);
3435 a.h -= (this.offset + rad);
3444 Roo.Shadow.prototype = {
3446 * @cfg {String} mode
3447 * The shadow display mode. Supports the following options:<br />
3448 * sides: Shadow displays on both sides and bottom only<br />
3449 * frame: Shadow displays equally on all four sides<br />
3450 * drop: Traditional bottom-right drop shadow (default)
3453 * @cfg {String} offset
3454 * The number of pixels to offset the shadow from the element (defaults to 4)
3459 defaultMode: "drop",
3462 * Displays the shadow under the target element
3463 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3465 show : function(target){
3466 target = Roo.get(target);
3468 this.el = Roo.Shadow.Pool.pull();
3469 if(this.el.dom.nextSibling != target.dom){
3470 this.el.insertBefore(target);
3473 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3475 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3478 target.getLeft(true),
3479 target.getTop(true),
3483 this.el.dom.style.display = "block";
3487 * Returns true if the shadow is visible, else false
3489 isVisible : function(){
3490 return this.el ? true : false;
3494 * Direct alignment when values are already available. Show must be called at least once before
3495 * calling this method to ensure it is initialized.
3496 * @param {Number} left The target element left position
3497 * @param {Number} top The target element top position
3498 * @param {Number} width The target element width
3499 * @param {Number} height The target element height
3501 realign : function(l, t, w, h){
3505 var a = this.adjusts, d = this.el.dom, s = d.style;
3507 s.left = (l+a.l)+"px";
3508 s.top = (t+a.t)+"px";
3509 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3511 if(s.width != sws || s.height != shs){
3515 var cn = d.childNodes;
3516 var sww = Math.max(0, (sw-12))+"px";
3517 cn[0].childNodes[1].style.width = sww;
3518 cn[1].childNodes[1].style.width = sww;
3519 cn[2].childNodes[1].style.width = sww;
3520 cn[1].style.height = Math.max(0, (sh-12))+"px";
3530 this.el.dom.style.display = "none";
3531 Roo.Shadow.Pool.push(this.el);
3537 * Adjust the z-index of this shadow
3538 * @param {Number} zindex The new z-index
3540 setZIndex : function(z){
3543 this.el.setStyle("z-index", z);
3548 // Private utility class that manages the internal Shadow cache
3549 Roo.Shadow.Pool = function(){
3551 var markup = Roo.isIE ?
3552 '<div class="x-ie-shadow"></div>' :
3553 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3558 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3559 sh.autoBoxAdjust = false;
3564 push : function(sh){
3570 * Ext JS Library 1.1.1
3571 * Copyright(c) 2006-2007, Ext JS, LLC.
3573 * Originally Released Under LGPL - original licence link has changed is not relivant.
3576 * <script type="text/javascript">
3581 * @class Roo.SplitBar
3582 * @extends Roo.util.Observable
3583 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3587 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3588 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3589 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3590 split.minSize = 100;
3591 split.maxSize = 600;
3592 split.animate = true;
3593 split.on('moved', splitterMoved);
3596 * Create a new SplitBar
3597 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3598 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3599 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3600 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3601 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3602 position of the SplitBar).
3604 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3607 this.el = Roo.get(dragElement, true);
3608 this.el.dom.unselectable = "on";
3610 this.resizingEl = Roo.get(resizingElement, true);
3614 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3615 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3618 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3621 * The minimum size of the resizing element. (Defaults to 0)
3627 * The maximum size of the resizing element. (Defaults to 2000)
3630 this.maxSize = 2000;
3633 * Whether to animate the transition to the new size
3636 this.animate = false;
3639 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3642 this.useShim = false;
3649 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3651 this.proxy = Roo.get(existingProxy).dom;
3654 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3657 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3660 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3663 this.dragSpecs = {};
3666 * @private The adapter to use to positon and resize elements
3668 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3669 this.adapter.init(this);
3671 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3673 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3674 this.el.addClass("x-splitbar-h");
3677 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3678 this.el.addClass("x-splitbar-v");
3684 * Fires when the splitter is moved (alias for {@link #event-moved})
3685 * @param {Roo.SplitBar} this
3686 * @param {Number} newSize the new width or height
3691 * Fires when the splitter is moved
3692 * @param {Roo.SplitBar} this
3693 * @param {Number} newSize the new width or height
3697 * @event beforeresize
3698 * Fires before the splitter is dragged
3699 * @param {Roo.SplitBar} this
3701 "beforeresize" : true,
3703 "beforeapply" : true
3706 Roo.util.Observable.call(this);
3709 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3710 onStartProxyDrag : function(x, y){
3711 this.fireEvent("beforeresize", this);
3713 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3715 o.enableDisplayMode("block");
3716 // all splitbars share the same overlay
3717 Roo.SplitBar.prototype.overlay = o;
3719 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3720 this.overlay.show();
3721 Roo.get(this.proxy).setDisplayed("block");
3722 var size = this.adapter.getElementSize(this);
3723 this.activeMinSize = this.getMinimumSize();;
3724 this.activeMaxSize = this.getMaximumSize();;
3725 var c1 = size - this.activeMinSize;
3726 var c2 = Math.max(this.activeMaxSize - size, 0);
3727 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3728 this.dd.resetConstraints();
3729 this.dd.setXConstraint(
3730 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3731 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3733 this.dd.setYConstraint(0, 0);
3735 this.dd.resetConstraints();
3736 this.dd.setXConstraint(0, 0);
3737 this.dd.setYConstraint(
3738 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3739 this.placement == Roo.SplitBar.TOP ? c2 : c1
3742 this.dragSpecs.startSize = size;
3743 this.dragSpecs.startPoint = [x, y];
3744 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3748 * @private Called after the drag operation by the DDProxy
3750 onEndProxyDrag : function(e){
3751 Roo.get(this.proxy).setDisplayed(false);
3752 var endPoint = Roo.lib.Event.getXY(e);
3754 this.overlay.hide();
3757 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3758 newSize = this.dragSpecs.startSize +
3759 (this.placement == Roo.SplitBar.LEFT ?
3760 endPoint[0] - this.dragSpecs.startPoint[0] :
3761 this.dragSpecs.startPoint[0] - endPoint[0]
3764 newSize = this.dragSpecs.startSize +
3765 (this.placement == Roo.SplitBar.TOP ?
3766 endPoint[1] - this.dragSpecs.startPoint[1] :
3767 this.dragSpecs.startPoint[1] - endPoint[1]
3770 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3771 if(newSize != this.dragSpecs.startSize){
3772 if(this.fireEvent('beforeapply', this, newSize) !== false){
3773 this.adapter.setElementSize(this, newSize);
3774 this.fireEvent("moved", this, newSize);
3775 this.fireEvent("resize", this, newSize);
3781 * Get the adapter this SplitBar uses
3782 * @return The adapter object
3784 getAdapter : function(){
3785 return this.adapter;
3789 * Set the adapter this SplitBar uses
3790 * @param {Object} adapter A SplitBar adapter object
3792 setAdapter : function(adapter){
3793 this.adapter = adapter;
3794 this.adapter.init(this);
3798 * Gets the minimum size for the resizing element
3799 * @return {Number} The minimum size
3801 getMinimumSize : function(){
3802 return this.minSize;
3806 * Sets the minimum size for the resizing element
3807 * @param {Number} minSize The minimum size
3809 setMinimumSize : function(minSize){
3810 this.minSize = minSize;
3814 * Gets the maximum size for the resizing element
3815 * @return {Number} The maximum size
3817 getMaximumSize : function(){
3818 return this.maxSize;
3822 * Sets the maximum size for the resizing element
3823 * @param {Number} maxSize The maximum size
3825 setMaximumSize : function(maxSize){
3826 this.maxSize = maxSize;
3830 * Sets the initialize size for the resizing element
3831 * @param {Number} size The initial size
3833 setCurrentSize : function(size){
3834 var oldAnimate = this.animate;
3835 this.animate = false;
3836 this.adapter.setElementSize(this, size);
3837 this.animate = oldAnimate;
3841 * Destroy this splitbar.
3842 * @param {Boolean} removeEl True to remove the element
3844 destroy : function(removeEl){
3849 this.proxy.parentNode.removeChild(this.proxy);
3857 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3859 Roo.SplitBar.createProxy = function(dir){
3860 var proxy = new Roo.Element(document.createElement("div"));
3861 proxy.unselectable();
3862 var cls = 'x-splitbar-proxy';
3863 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3864 document.body.appendChild(proxy.dom);
3869 * @class Roo.SplitBar.BasicLayoutAdapter
3870 * Default Adapter. It assumes the splitter and resizing element are not positioned
3871 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3873 Roo.SplitBar.BasicLayoutAdapter = function(){
3876 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3877 // do nothing for now
3882 * Called before drag operations to get the current size of the resizing element.
3883 * @param {Roo.SplitBar} s The SplitBar using this adapter
3885 getElementSize : function(s){
3886 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3887 return s.resizingEl.getWidth();
3889 return s.resizingEl.getHeight();
3894 * Called after drag operations to set the size of the resizing element.
3895 * @param {Roo.SplitBar} s The SplitBar using this adapter
3896 * @param {Number} newSize The new size to set
3897 * @param {Function} onComplete A function to be invoked when resizing is complete
3899 setElementSize : function(s, newSize, onComplete){
3900 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3902 s.resizingEl.setWidth(newSize);
3904 onComplete(s, newSize);
3907 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3912 s.resizingEl.setHeight(newSize);
3914 onComplete(s, newSize);
3917 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3924 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3925 * @extends Roo.SplitBar.BasicLayoutAdapter
3926 * Adapter that moves the splitter element to align with the resized sizing element.
3927 * Used with an absolute positioned SplitBar.
3928 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3929 * document.body, make sure you assign an id to the body element.
3931 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3932 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3933 this.container = Roo.get(container);
3936 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3941 getElementSize : function(s){
3942 return this.basic.getElementSize(s);
3945 setElementSize : function(s, newSize, onComplete){
3946 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3949 moveSplitter : function(s){
3950 var yes = Roo.SplitBar;
3951 switch(s.placement){
3953 s.el.setX(s.resizingEl.getRight());
3956 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3959 s.el.setY(s.resizingEl.getBottom());
3962 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3969 * Orientation constant - Create a vertical SplitBar
3973 Roo.SplitBar.VERTICAL = 1;
3976 * Orientation constant - Create a horizontal SplitBar
3980 Roo.SplitBar.HORIZONTAL = 2;
3983 * Placement constant - The resizing element is to the left of the splitter element
3987 Roo.SplitBar.LEFT = 1;
3990 * Placement constant - The resizing element is to the right of the splitter element
3994 Roo.SplitBar.RIGHT = 2;
3997 * Placement constant - The resizing element is positioned above the splitter element
4001 Roo.SplitBar.TOP = 3;
4004 * Placement constant - The resizing element is positioned under splitter element
4008 Roo.SplitBar.BOTTOM = 4;
4011 * Ext JS Library 1.1.1
4012 * Copyright(c) 2006-2007, Ext JS, LLC.
4014 * Originally Released Under LGPL - original licence link has changed is not relivant.
4017 * <script type="text/javascript">
4022 * @extends Roo.util.Observable
4023 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
4024 * This class also supports single and multi selection modes. <br>
4025 * Create a data model bound view:
4027 var store = new Roo.data.Store(...);
4029 var view = new Roo.View({
4031 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
4034 selectedClass: "ydataview-selected",
4038 // listen for node click?
4039 view.on("click", function(vw, index, node, e){
4040 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4044 dataModel.load("foobar.xml");
4046 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4048 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4049 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4051 * Note: old style constructor is still suported (container, template, config)
4055 * @param {Object} config The config object
4058 Roo.View = function(config, depreciated_tpl, depreciated_config){
4060 this.parent = false;
4062 if (typeof(depreciated_tpl) == 'undefined') {
4063 // new way.. - universal constructor.
4064 Roo.apply(this, config);
4065 this.el = Roo.get(this.el);
4068 this.el = Roo.get(config);
4069 this.tpl = depreciated_tpl;
4070 Roo.apply(this, depreciated_config);
4072 this.wrapEl = this.el.wrap().wrap();
4073 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4076 if(typeof(this.tpl) == "string"){
4077 this.tpl = new Roo.Template(this.tpl);
4079 // support xtype ctors..
4080 this.tpl = new Roo.factory(this.tpl, Roo);
4089 * @event beforeclick
4090 * Fires before a click is processed. Returns false to cancel the default action.
4091 * @param {Roo.View} this
4092 * @param {Number} index The index of the target node
4093 * @param {HTMLElement} node The target node
4094 * @param {Roo.EventObject} e The raw event object
4096 "beforeclick" : true,
4099 * Fires when a template node is clicked.
4100 * @param {Roo.View} this
4101 * @param {Number} index The index of the target node
4102 * @param {HTMLElement} node The target node
4103 * @param {Roo.EventObject} e The raw event object
4108 * Fires when a template node is double clicked.
4109 * @param {Roo.View} this
4110 * @param {Number} index The index of the target node
4111 * @param {HTMLElement} node The target node
4112 * @param {Roo.EventObject} e The raw event object
4116 * @event contextmenu
4117 * Fires when a template node is right clicked.
4118 * @param {Roo.View} this
4119 * @param {Number} index The index of the target node
4120 * @param {HTMLElement} node The target node
4121 * @param {Roo.EventObject} e The raw event object
4123 "contextmenu" : true,
4125 * @event selectionchange
4126 * Fires when the selected nodes change.
4127 * @param {Roo.View} this
4128 * @param {Array} selections Array of the selected nodes
4130 "selectionchange" : true,
4133 * @event beforeselect
4134 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4135 * @param {Roo.View} this
4136 * @param {HTMLElement} node The node to be selected
4137 * @param {Array} selections Array of currently selected nodes
4139 "beforeselect" : true,
4141 * @event preparedata
4142 * Fires on every row to render, to allow you to change the data.
4143 * @param {Roo.View} this
4144 * @param {Object} data to be rendered (change this)
4146 "preparedata" : true
4154 "click": this.onClick,
4155 "dblclick": this.onDblClick,
4156 "contextmenu": this.onContextMenu,
4160 this.selections = [];
4162 this.cmp = new Roo.CompositeElementLite([]);
4164 this.store = Roo.factory(this.store, Roo.data);
4165 this.setStore(this.store, true);
4168 if ( this.footer && this.footer.xtype) {
4170 var fctr = this.wrapEl.appendChild(document.createElement("div"));
4172 this.footer.dataSource = this.store;
4173 this.footer.container = fctr;
4174 this.footer = Roo.factory(this.footer, Roo);
4175 fctr.insertFirst(this.el);
4177 // this is a bit insane - as the paging toolbar seems to detach the el..
4178 // dom.parentNode.parentNode.parentNode
4179 // they get detached?
4183 Roo.View.superclass.constructor.call(this);
4188 Roo.extend(Roo.View, Roo.util.Observable, {
4191 * @cfg {Roo.data.Store} store Data store to load data from.
4196 * @cfg {String|Roo.Element} el The container element.
4201 * @cfg {String|Roo.Template} tpl The template used by this View
4205 * @cfg {String} dataName the named area of the template to use as the data area
4206 * Works with domtemplates roo-name="name"
4210 * @cfg {String} selectedClass The css class to add to selected nodes
4212 selectedClass : "x-view-selected",
4214 * @cfg {String} emptyText The empty text to show when nothing is loaded.
4219 * @cfg {String} text to display on mask (default Loading)
4223 * @cfg {Boolean} multiSelect Allow multiple selection
4225 multiSelect : false,
4227 * @cfg {Boolean} singleSelect Allow single selection
4229 singleSelect: false,
4232 * @cfg {Boolean} toggleSelect - selecting
4234 toggleSelect : false,
4237 * @cfg {Boolean} tickable - selecting
4242 * Returns the element this view is bound to.
4243 * @return {Roo.Element}
4252 * Refreshes the view. - called by datachanged on the store. - do not call directly.
4254 refresh : function(){
4255 //Roo.log('refresh');
4258 // if we are using something like 'domtemplate', then
4259 // the what gets used is:
4260 // t.applySubtemplate(NAME, data, wrapping data..)
4261 // the outer template then get' applied with
4262 // the store 'extra data'
4263 // and the body get's added to the
4264 // roo-name="data" node?
4265 // <span class='roo-tpl-{name}'></span> ?????
4269 this.clearSelections();
4272 var records = this.store.getRange();
4273 if(records.length < 1) {
4275 // is this valid?? = should it render a template??
4277 this.el.update(this.emptyText);
4281 if (this.dataName) {
4282 this.el.update(t.apply(this.store.meta)); //????
4283 el = this.el.child('.roo-tpl-' + this.dataName);
4286 for(var i = 0, len = records.length; i < len; i++){
4287 var data = this.prepareData(records[i].data, i, records[i]);
4288 this.fireEvent("preparedata", this, data, i, records[i]);
4290 var d = Roo.apply({}, data);
4293 Roo.apply(d, {'roo-id' : Roo.id()});
4297 Roo.each(this.parent.item, function(item){
4298 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4301 Roo.apply(d, {'roo-data-checked' : 'checked'});
4305 html[html.length] = Roo.util.Format.trim(
4307 t.applySubtemplate(this.dataName, d, this.store.meta) :
4314 el.update(html.join(""));
4315 this.nodes = el.dom.childNodes;
4316 this.updateIndexes(0);
4321 * Function to override to reformat the data that is sent to
4322 * the template for each node.
4323 * DEPRICATED - use the preparedata event handler.
4324 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4325 * a JSON object for an UpdateManager bound view).
4327 prepareData : function(data, index, record)
4329 this.fireEvent("preparedata", this, data, index, record);
4333 onUpdate : function(ds, record){
4334 // Roo.log('on update');
4335 this.clearSelections();
4336 var index = this.store.indexOf(record);
4337 var n = this.nodes[index];
4338 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4339 n.parentNode.removeChild(n);
4340 this.updateIndexes(index, index);
4346 onAdd : function(ds, records, index)
4348 //Roo.log(['on Add', ds, records, index] );
4349 this.clearSelections();
4350 if(this.nodes.length == 0){
4354 var n = this.nodes[index];
4355 for(var i = 0, len = records.length; i < len; i++){
4356 var d = this.prepareData(records[i].data, i, records[i]);
4358 this.tpl.insertBefore(n, d);
4361 this.tpl.append(this.el, d);
4364 this.updateIndexes(index);
4367 onRemove : function(ds, record, index){
4368 // Roo.log('onRemove');
4369 this.clearSelections();
4370 var el = this.dataName ?
4371 this.el.child('.roo-tpl-' + this.dataName) :
4374 el.dom.removeChild(this.nodes[index]);
4375 this.updateIndexes(index);
4379 * Refresh an individual node.
4380 * @param {Number} index
4382 refreshNode : function(index){
4383 this.onUpdate(this.store, this.store.getAt(index));
4386 updateIndexes : function(startIndex, endIndex){
4387 var ns = this.nodes;
4388 startIndex = startIndex || 0;
4389 endIndex = endIndex || ns.length - 1;
4390 for(var i = startIndex; i <= endIndex; i++){
4391 ns[i].nodeIndex = i;
4396 * Changes the data store this view uses and refresh the view.
4397 * @param {Store} store
4399 setStore : function(store, initial){
4400 if(!initial && this.store){
4401 this.store.un("datachanged", this.refresh);
4402 this.store.un("add", this.onAdd);
4403 this.store.un("remove", this.onRemove);
4404 this.store.un("update", this.onUpdate);
4405 this.store.un("clear", this.refresh);
4406 this.store.un("beforeload", this.onBeforeLoad);
4407 this.store.un("load", this.onLoad);
4408 this.store.un("loadexception", this.onLoad);
4412 store.on("datachanged", this.refresh, this);
4413 store.on("add", this.onAdd, this);
4414 store.on("remove", this.onRemove, this);
4415 store.on("update", this.onUpdate, this);
4416 store.on("clear", this.refresh, this);
4417 store.on("beforeload", this.onBeforeLoad, this);
4418 store.on("load", this.onLoad, this);
4419 store.on("loadexception", this.onLoad, this);
4427 * onbeforeLoad - masks the loading area.
4430 onBeforeLoad : function(store,opts)
4432 //Roo.log('onBeforeLoad');
4436 this.el.mask(this.mask ? this.mask : "Loading" );
4438 onLoad : function ()
4445 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4446 * @param {HTMLElement} node
4447 * @return {HTMLElement} The template node
4449 findItemFromChild : function(node){
4450 var el = this.dataName ?
4451 this.el.child('.roo-tpl-' + this.dataName,true) :
4454 if(!node || node.parentNode == el){
4457 var p = node.parentNode;
4458 while(p && p != el){
4459 if(p.parentNode == el){
4468 onClick : function(e){
4469 var item = this.findItemFromChild(e.getTarget());
4471 var index = this.indexOf(item);
4472 if(this.onItemClick(item, index, e) !== false){
4473 this.fireEvent("click", this, index, item, e);
4476 this.clearSelections();
4481 onContextMenu : function(e){
4482 var item = this.findItemFromChild(e.getTarget());
4484 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4489 onDblClick : function(e){
4490 var item = this.findItemFromChild(e.getTarget());
4492 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4496 onItemClick : function(item, index, e)
4498 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4501 if (this.toggleSelect) {
4502 var m = this.isSelected(item) ? 'unselect' : 'select';
4505 _t[m](item, true, false);
4508 if(this.multiSelect || this.singleSelect){
4509 if(this.multiSelect && e.shiftKey && this.lastSelection){
4510 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4512 this.select(item, this.multiSelect && e.ctrlKey);
4513 this.lastSelection = item;
4525 * Get the number of selected nodes.
4528 getSelectionCount : function(){
4529 return this.selections.length;
4533 * Get the currently selected nodes.
4534 * @return {Array} An array of HTMLElements
4536 getSelectedNodes : function(){
4537 return this.selections;
4541 * Get the indexes of the selected nodes.
4544 getSelectedIndexes : function(){
4545 var indexes = [], s = this.selections;
4546 for(var i = 0, len = s.length; i < len; i++){
4547 indexes.push(s[i].nodeIndex);
4553 * Clear all selections
4554 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4556 clearSelections : function(suppressEvent){
4557 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4558 this.cmp.elements = this.selections;
4559 this.cmp.removeClass(this.selectedClass);
4560 this.selections = [];
4562 this.fireEvent("selectionchange", this, this.selections);
4568 * Returns true if the passed node is selected
4569 * @param {HTMLElement/Number} node The node or node index
4572 isSelected : function(node){
4573 var s = this.selections;
4577 node = this.getNode(node);
4578 return s.indexOf(node) !== -1;
4583 * @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
4584 * @param {Boolean} keepExisting (optional) true to keep existing selections
4585 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4587 select : function(nodeInfo, keepExisting, suppressEvent){
4588 if(nodeInfo instanceof Array){
4590 this.clearSelections(true);
4592 for(var i = 0, len = nodeInfo.length; i < len; i++){
4593 this.select(nodeInfo[i], true, true);
4597 var node = this.getNode(nodeInfo);
4598 if(!node || this.isSelected(node)){
4599 return; // already selected.
4602 this.clearSelections(true);
4605 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4606 Roo.fly(node).addClass(this.selectedClass);
4607 this.selections.push(node);
4609 this.fireEvent("selectionchange", this, this.selections);
4617 * @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
4618 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4619 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4621 unselect : function(nodeInfo, keepExisting, suppressEvent)
4623 if(nodeInfo instanceof Array){
4624 Roo.each(this.selections, function(s) {
4625 this.unselect(s, nodeInfo);
4629 var node = this.getNode(nodeInfo);
4630 if(!node || !this.isSelected(node)){
4631 //Roo.log("not selected");
4632 return; // not selected.
4636 Roo.each(this.selections, function(s) {
4638 Roo.fly(node).removeClass(this.selectedClass);
4645 this.selections= ns;
4646 this.fireEvent("selectionchange", this, this.selections);
4650 * Gets a template node.
4651 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4652 * @return {HTMLElement} The node or null if it wasn't found
4654 getNode : function(nodeInfo){
4655 if(typeof nodeInfo == "string"){
4656 return document.getElementById(nodeInfo);
4657 }else if(typeof nodeInfo == "number"){
4658 return this.nodes[nodeInfo];
4664 * Gets a range template nodes.
4665 * @param {Number} startIndex
4666 * @param {Number} endIndex
4667 * @return {Array} An array of nodes
4669 getNodes : function(start, end){
4670 var ns = this.nodes;
4672 end = typeof end == "undefined" ? ns.length - 1 : end;
4675 for(var i = start; i <= end; i++){
4679 for(var i = start; i >= end; i--){
4687 * Finds the index of the passed node
4688 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4689 * @return {Number} The index of the node or -1
4691 indexOf : function(node){
4692 node = this.getNode(node);
4693 if(typeof node.nodeIndex == "number"){
4694 return node.nodeIndex;
4696 var ns = this.nodes;
4697 for(var i = 0, len = ns.length; i < len; i++){
4707 * Ext JS Library 1.1.1
4708 * Copyright(c) 2006-2007, Ext JS, LLC.
4710 * Originally Released Under LGPL - original licence link has changed is not relivant.
4713 * <script type="text/javascript">
4717 * @class Roo.JsonView
4719 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4721 var view = new Roo.JsonView({
4722 container: "my-element",
4723 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4728 // listen for node click?
4729 view.on("click", function(vw, index, node, e){
4730 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4733 // direct load of JSON data
4734 view.load("foobar.php");
4736 // Example from my blog list
4737 var tpl = new Roo.Template(
4738 '<div class="entry">' +
4739 '<a class="entry-title" href="{link}">{title}</a>' +
4740 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4741 "</div><hr />"
4744 var moreView = new Roo.JsonView({
4745 container : "entry-list",
4749 moreView.on("beforerender", this.sortEntries, this);
4751 url: "/blog/get-posts.php",
4752 params: "allposts=true",
4753 text: "Loading Blog Entries..."
4757 * Note: old code is supported with arguments : (container, template, config)
4761 * Create a new JsonView
4763 * @param {Object} config The config object
4766 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4769 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4771 var um = this.el.getUpdateManager();
4772 um.setRenderer(this);
4773 um.on("update", this.onLoad, this);
4774 um.on("failure", this.onLoadException, this);
4777 * @event beforerender
4778 * Fires before rendering of the downloaded JSON data.
4779 * @param {Roo.JsonView} this
4780 * @param {Object} data The JSON data loaded
4784 * Fires when data is loaded.
4785 * @param {Roo.JsonView} this
4786 * @param {Object} data The JSON data loaded
4787 * @param {Object} response The raw Connect response object
4790 * @event loadexception
4791 * Fires when loading fails.
4792 * @param {Roo.JsonView} this
4793 * @param {Object} response The raw Connect response object
4796 'beforerender' : true,
4798 'loadexception' : true
4801 Roo.extend(Roo.JsonView, Roo.View, {
4803 * @type {String} The root property in the loaded JSON object that contains the data
4808 * Refreshes the view.
4810 refresh : function(){
4811 this.clearSelections();
4814 var o = this.jsonData;
4815 if(o && o.length > 0){
4816 for(var i = 0, len = o.length; i < len; i++){
4817 var data = this.prepareData(o[i], i, o);
4818 html[html.length] = this.tpl.apply(data);
4821 html.push(this.emptyText);
4823 this.el.update(html.join(""));
4824 this.nodes = this.el.dom.childNodes;
4825 this.updateIndexes(0);
4829 * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4830 * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4833 url: "your-url.php",
4834 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4835 callback: yourFunction,
4836 scope: yourObject, //(optional scope)
4844 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4845 * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4846 * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
4847 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4848 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4851 var um = this.el.getUpdateManager();
4852 um.update.apply(um, arguments);
4855 // note - render is a standard framework call...
4856 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4857 render : function(el, response){
4859 this.clearSelections();
4863 if (response != '') {
4864 o = Roo.util.JSON.decode(response.responseText);
4867 o = o[this.jsonRoot];
4873 * The current JSON data or null
4876 this.beforeRender();
4881 * Get the number of records in the current JSON dataset
4884 getCount : function(){
4885 return this.jsonData ? this.jsonData.length : 0;
4889 * Returns the JSON object for the specified node(s)
4890 * @param {HTMLElement/Array} node The node or an array of nodes
4891 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4892 * you get the JSON object for the node
4894 getNodeData : function(node){
4895 if(node instanceof Array){
4897 for(var i = 0, len = node.length; i < len; i++){
4898 data.push(this.getNodeData(node[i]));
4902 return this.jsonData[this.indexOf(node)] || null;
4905 beforeRender : function(){
4906 this.snapshot = this.jsonData;
4908 this.sort.apply(this, this.sortInfo);
4910 this.fireEvent("beforerender", this, this.jsonData);
4913 onLoad : function(el, o){
4914 this.fireEvent("load", this, this.jsonData, o);
4917 onLoadException : function(el, o){
4918 this.fireEvent("loadexception", this, o);
4922 * Filter the data by a specific property.
4923 * @param {String} property A property on your JSON objects
4924 * @param {String/RegExp} value Either string that the property values
4925 * should start with, or a RegExp to test against the property
4927 filter : function(property, value){
4930 var ss = this.snapshot;
4931 if(typeof value == "string"){
4932 var vlen = value.length;
4937 value = value.toLowerCase();
4938 for(var i = 0, len = ss.length; i < len; i++){
4940 if(o[property].substr(0, vlen).toLowerCase() == value){
4944 } else if(value.exec){ // regex?
4945 for(var i = 0, len = ss.length; i < len; i++){
4947 if(value.test(o[property])){
4954 this.jsonData = data;
4960 * Filter by a function. The passed function will be called with each
4961 * object in the current dataset. If the function returns true the value is kept,
4962 * otherwise it is filtered.
4963 * @param {Function} fn
4964 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4966 filterBy : function(fn, scope){
4969 var ss = this.snapshot;
4970 for(var i = 0, len = ss.length; i < len; i++){
4972 if(fn.call(scope || this, o)){
4976 this.jsonData = data;
4982 * Clears the current filter.
4984 clearFilter : function(){
4985 if(this.snapshot && this.jsonData != this.snapshot){
4986 this.jsonData = this.snapshot;
4993 * Sorts the data for this view and refreshes it.
4994 * @param {String} property A property on your JSON objects to sort on
4995 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4996 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4998 sort : function(property, dir, sortType){
4999 this.sortInfo = Array.prototype.slice.call(arguments, 0);
5002 var dsc = dir && dir.toLowerCase() == "desc";
5003 var f = function(o1, o2){
5004 var v1 = sortType ? sortType(o1[p]) : o1[p];
5005 var v2 = sortType ? sortType(o2[p]) : o2[p];
5008 return dsc ? +1 : -1;
5010 return dsc ? -1 : +1;
5015 this.jsonData.sort(f);
5017 if(this.jsonData != this.snapshot){
5018 this.snapshot.sort(f);
5024 * Ext JS Library 1.1.1
5025 * Copyright(c) 2006-2007, Ext JS, LLC.
5027 * Originally Released Under LGPL - original licence link has changed is not relivant.
5030 * <script type="text/javascript">
5035 * @class Roo.ColorPalette
5036 * @extends Roo.Component
5037 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
5038 * Here's an example of typical usage:
5040 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
5041 cp.render('my-div');
5043 cp.on('select', function(palette, selColor){
5044 // do something with selColor
5048 * Create a new ColorPalette
5049 * @param {Object} config The config object
5051 Roo.ColorPalette = function(config){
5052 Roo.ColorPalette.superclass.constructor.call(this, config);
5056 * Fires when a color is selected
5057 * @param {ColorPalette} this
5058 * @param {String} color The 6-digit color hex code (without the # symbol)
5064 this.on("select", this.handler, this.scope, true);
5067 Roo.extend(Roo.ColorPalette, Roo.Component, {
5069 * @cfg {String} itemCls
5070 * The CSS class to apply to the containing element (defaults to "x-color-palette")
5072 itemCls : "x-color-palette",
5074 * @cfg {String} value
5075 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
5076 * the hex codes are case-sensitive.
5081 ctype: "Roo.ColorPalette",
5084 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5086 allowReselect : false,
5089 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
5090 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
5091 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5092 * of colors with the width setting until the box is symmetrical.</p>
5093 * <p>You can override individual colors if needed:</p>
5095 var cp = new Roo.ColorPalette();
5096 cp.colors[0] = "FF0000"; // change the first box to red
5099 Or you can provide a custom array of your own for complete control:
5101 var cp = new Roo.ColorPalette();
5102 cp.colors = ["000000", "993300", "333300"];
5107 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5108 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5109 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5110 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5111 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5115 onRender : function(container, position){
5116 var t = new Roo.MasterTemplate(
5117 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
5119 var c = this.colors;
5120 for(var i = 0, len = c.length; i < len; i++){
5123 var el = document.createElement("div");
5124 el.className = this.itemCls;
5126 container.dom.insertBefore(el, position);
5127 this.el = Roo.get(el);
5128 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
5129 if(this.clickEvent != 'click'){
5130 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
5135 afterRender : function(){
5136 Roo.ColorPalette.superclass.afterRender.call(this);
5145 handleClick : function(e, t){
5148 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5149 this.select(c.toUpperCase());
5154 * Selects the specified color in the palette (fires the select event)
5155 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5157 select : function(color){
5158 color = color.replace("#", "");
5159 if(color != this.value || this.allowReselect){
5162 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5164 el.child("a.color-"+color).addClass("x-color-palette-sel");
5166 this.fireEvent("select", this, color);
5171 * Ext JS Library 1.1.1
5172 * Copyright(c) 2006-2007, Ext JS, LLC.
5174 * Originally Released Under LGPL - original licence link has changed is not relivant.
5177 * <script type="text/javascript">
5181 * @class Roo.DatePicker
5182 * @extends Roo.Component
5183 * Simple date picker class.
5185 * Create a new DatePicker
5186 * @param {Object} config The config object
5188 Roo.DatePicker = function(config){
5189 Roo.DatePicker.superclass.constructor.call(this, config);
5191 this.value = config && config.value ?
5192 config.value.clearTime() : new Date().clearTime();
5197 * Fires when a date is selected
5198 * @param {DatePicker} this
5199 * @param {Date} date The selected date
5203 * @event monthchange
5204 * Fires when the displayed month changes
5205 * @param {DatePicker} this
5206 * @param {Date} date The selected month
5212 this.on("select", this.handler, this.scope || this);
5214 // build the disabledDatesRE
5215 if(!this.disabledDatesRE && this.disabledDates){
5216 var dd = this.disabledDates;
5218 for(var i = 0; i < dd.length; i++){
5220 if(i != dd.length-1) {
5224 this.disabledDatesRE = new RegExp(re + ")");
5228 Roo.extend(Roo.DatePicker, Roo.Component, {
5230 * @cfg {String} todayText
5231 * The text to display on the button that selects the current date (defaults to "Today")
5233 todayText : "Today",
5235 * @cfg {String} okText
5236 * The text to display on the ok button
5238 okText : " OK ", //   to give the user extra clicking room
5240 * @cfg {String} cancelText
5241 * The text to display on the cancel button
5243 cancelText : "Cancel",
5245 * @cfg {String} todayTip
5246 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5248 todayTip : "{0} (Spacebar)",
5250 * @cfg {Date} minDate
5251 * Minimum allowable date (JavaScript date object, defaults to null)
5255 * @cfg {Date} maxDate
5256 * Maximum allowable date (JavaScript date object, defaults to null)
5260 * @cfg {String} minText
5261 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5263 minText : "This date is before the minimum date",
5265 * @cfg {String} maxText
5266 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5268 maxText : "This date is after the maximum date",
5270 * @cfg {String} format
5271 * The default date format string which can be overriden for localization support. The format must be
5272 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5276 * @cfg {Array} disabledDays
5277 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5279 disabledDays : null,
5281 * @cfg {String} disabledDaysText
5282 * The tooltip to display when the date falls on a disabled day (defaults to "")
5284 disabledDaysText : "",
5286 * @cfg {RegExp} disabledDatesRE
5287 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5289 disabledDatesRE : null,
5291 * @cfg {String} disabledDatesText
5292 * The tooltip text to display when the date falls on a disabled date (defaults to "")
5294 disabledDatesText : "",
5296 * @cfg {Boolean} constrainToViewport
5297 * True to constrain the date picker to the viewport (defaults to true)
5299 constrainToViewport : true,
5301 * @cfg {Array} monthNames
5302 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5304 monthNames : Date.monthNames,
5306 * @cfg {Array} dayNames
5307 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5309 dayNames : Date.dayNames,
5311 * @cfg {String} nextText
5312 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5314 nextText: 'Next Month (Control+Right)',
5316 * @cfg {String} prevText
5317 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5319 prevText: 'Previous Month (Control+Left)',
5321 * @cfg {String} monthYearText
5322 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5324 monthYearText: 'Choose a month (Control+Up/Down to move years)',
5326 * @cfg {Number} startDay
5327 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5331 * @cfg {Bool} showClear
5332 * Show a clear button (usefull for date form elements that can be blank.)
5338 * Sets the value of the date field
5339 * @param {Date} value The date to set
5341 setValue : function(value){
5342 var old = this.value;
5344 if (typeof(value) == 'string') {
5346 value = Date.parseDate(value, this.format);
5352 this.value = value.clearTime(true);
5354 this.update(this.value);
5359 * Gets the current selected value of the date field
5360 * @return {Date} The selected date
5362 getValue : function(){
5369 this.update(this.activeDate);
5374 onRender : function(container, position){
5377 '<table cellspacing="0">',
5378 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'"> </a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'"> </a></td></tr>',
5379 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5380 var dn = this.dayNames;
5381 for(var i = 0; i < 7; i++){
5382 var d = this.startDay+i;
5386 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5388 m[m.length] = "</tr></thead><tbody><tr>";
5389 for(var i = 0; i < 42; i++) {
5390 if(i % 7 == 0 && i != 0){
5391 m[m.length] = "</tr><tr>";
5393 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5395 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5396 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5398 var el = document.createElement("div");
5399 el.className = "x-date-picker";
5400 el.innerHTML = m.join("");
5402 container.dom.insertBefore(el, position);
5404 this.el = Roo.get(el);
5405 this.eventEl = Roo.get(el.firstChild);
5407 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5408 handler: this.showPrevMonth,
5410 preventDefault:true,
5414 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5415 handler: this.showNextMonth,
5417 preventDefault:true,
5421 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5423 this.monthPicker = this.el.down('div.x-date-mp');
5424 this.monthPicker.enableDisplayMode('block');
5426 var kn = new Roo.KeyNav(this.eventEl, {
5427 "left" : function(e){
5429 this.showPrevMonth() :
5430 this.update(this.activeDate.add("d", -1));
5433 "right" : function(e){
5435 this.showNextMonth() :
5436 this.update(this.activeDate.add("d", 1));
5441 this.showNextYear() :
5442 this.update(this.activeDate.add("d", -7));
5445 "down" : function(e){
5447 this.showPrevYear() :
5448 this.update(this.activeDate.add("d", 7));
5451 "pageUp" : function(e){
5452 this.showNextMonth();
5455 "pageDown" : function(e){
5456 this.showPrevMonth();
5459 "enter" : function(e){
5460 e.stopPropagation();
5467 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5469 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5471 this.el.unselectable();
5473 this.cells = this.el.select("table.x-date-inner tbody td");
5474 this.textNodes = this.el.query("table.x-date-inner tbody span");
5476 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5478 tooltip: this.monthYearText
5481 this.mbtn.on('click', this.showMonthPicker, this);
5482 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5485 var today = (new Date()).dateFormat(this.format);
5487 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5488 if (this.showClear) {
5489 baseTb.add( new Roo.Toolbar.Fill());
5492 text: String.format(this.todayText, today),
5493 tooltip: String.format(this.todayTip, today),
5494 handler: this.selectToday,
5498 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5501 if (this.showClear) {
5503 baseTb.add( new Roo.Toolbar.Fill());
5506 cls: 'x-btn-icon x-btn-clear',
5507 handler: function() {
5509 this.fireEvent("select", this, '');
5519 this.update(this.value);
5522 createMonthPicker : function(){
5523 if(!this.monthPicker.dom.firstChild){
5524 var buf = ['<table border="0" cellspacing="0">'];
5525 for(var i = 0; i < 6; i++){
5527 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5528 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5530 '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5531 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5535 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5537 '</button><button type="button" class="x-date-mp-cancel">',
5539 '</button></td></tr>',
5542 this.monthPicker.update(buf.join(''));
5543 this.monthPicker.on('click', this.onMonthClick, this);
5544 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5546 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5547 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5549 this.mpMonths.each(function(m, a, i){
5552 m.dom.xmonth = 5 + Math.round(i * .5);
5554 m.dom.xmonth = Math.round((i-1) * .5);
5560 showMonthPicker : function(){
5561 this.createMonthPicker();
5562 var size = this.el.getSize();
5563 this.monthPicker.setSize(size);
5564 this.monthPicker.child('table').setSize(size);
5566 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5567 this.updateMPMonth(this.mpSelMonth);
5568 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5569 this.updateMPYear(this.mpSelYear);
5571 this.monthPicker.slideIn('t', {duration:.2});
5574 updateMPYear : function(y){
5576 var ys = this.mpYears.elements;
5577 for(var i = 1; i <= 10; i++){
5578 var td = ys[i-1], y2;
5580 y2 = y + Math.round(i * .5);
5581 td.firstChild.innerHTML = y2;
5584 y2 = y - (5-Math.round(i * .5));
5585 td.firstChild.innerHTML = y2;
5588 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5592 updateMPMonth : function(sm){
5593 this.mpMonths.each(function(m, a, i){
5594 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5598 selectMPMonth: function(m){
5602 onMonthClick : function(e, t){
5604 var el = new Roo.Element(t), pn;
5605 if(el.is('button.x-date-mp-cancel')){
5606 this.hideMonthPicker();
5608 else if(el.is('button.x-date-mp-ok')){
5609 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5610 this.hideMonthPicker();
5612 else if(pn = el.up('td.x-date-mp-month', 2)){
5613 this.mpMonths.removeClass('x-date-mp-sel');
5614 pn.addClass('x-date-mp-sel');
5615 this.mpSelMonth = pn.dom.xmonth;
5617 else if(pn = el.up('td.x-date-mp-year', 2)){
5618 this.mpYears.removeClass('x-date-mp-sel');
5619 pn.addClass('x-date-mp-sel');
5620 this.mpSelYear = pn.dom.xyear;
5622 else if(el.is('a.x-date-mp-prev')){
5623 this.updateMPYear(this.mpyear-10);
5625 else if(el.is('a.x-date-mp-next')){
5626 this.updateMPYear(this.mpyear+10);
5630 onMonthDblClick : function(e, t){
5632 var el = new Roo.Element(t), pn;
5633 if(pn = el.up('td.x-date-mp-month', 2)){
5634 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5635 this.hideMonthPicker();
5637 else if(pn = el.up('td.x-date-mp-year', 2)){
5638 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5639 this.hideMonthPicker();
5643 hideMonthPicker : function(disableAnim){
5644 if(this.monthPicker){
5645 if(disableAnim === true){
5646 this.monthPicker.hide();
5648 this.monthPicker.slideOut('t', {duration:.2});
5654 showPrevMonth : function(e){
5655 this.update(this.activeDate.add("mo", -1));
5659 showNextMonth : function(e){
5660 this.update(this.activeDate.add("mo", 1));
5664 showPrevYear : function(){
5665 this.update(this.activeDate.add("y", -1));
5669 showNextYear : function(){
5670 this.update(this.activeDate.add("y", 1));
5674 handleMouseWheel : function(e){
5675 var delta = e.getWheelDelta();
5677 this.showPrevMonth();
5679 } else if(delta < 0){
5680 this.showNextMonth();
5686 handleDateClick : function(e, t){
5688 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5689 this.setValue(new Date(t.dateValue));
5690 this.fireEvent("select", this, this.value);
5695 selectToday : function(){
5696 this.setValue(new Date().clearTime());
5697 this.fireEvent("select", this, this.value);
5701 update : function(date)
5703 var vd = this.activeDate;
5704 this.activeDate = date;
5706 var t = date.getTime();
5707 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5708 this.cells.removeClass("x-date-selected");
5709 this.cells.each(function(c){
5710 if(c.dom.firstChild.dateValue == t){
5711 c.addClass("x-date-selected");
5712 setTimeout(function(){
5713 try{c.dom.firstChild.focus();}catch(e){}
5722 var days = date.getDaysInMonth();
5723 var firstOfMonth = date.getFirstDateOfMonth();
5724 var startingPos = firstOfMonth.getDay()-this.startDay;
5726 if(startingPos <= this.startDay){
5730 var pm = date.add("mo", -1);
5731 var prevStart = pm.getDaysInMonth()-startingPos;
5733 var cells = this.cells.elements;
5734 var textEls = this.textNodes;
5735 days += startingPos;
5737 // convert everything to numbers so it's fast
5739 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5740 var today = new Date().clearTime().getTime();
5741 var sel = date.clearTime().getTime();
5742 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5743 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5744 var ddMatch = this.disabledDatesRE;
5745 var ddText = this.disabledDatesText;
5746 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5747 var ddaysText = this.disabledDaysText;
5748 var format = this.format;
5750 var setCellClass = function(cal, cell){
5752 var t = d.getTime();
5753 cell.firstChild.dateValue = t;
5755 cell.className += " x-date-today";
5756 cell.title = cal.todayText;
5759 cell.className += " x-date-selected";
5760 setTimeout(function(){
5761 try{cell.firstChild.focus();}catch(e){}
5766 cell.className = " x-date-disabled";
5767 cell.title = cal.minText;
5771 cell.className = " x-date-disabled";
5772 cell.title = cal.maxText;
5776 if(ddays.indexOf(d.getDay()) != -1){
5777 cell.title = ddaysText;
5778 cell.className = " x-date-disabled";
5781 if(ddMatch && format){
5782 var fvalue = d.dateFormat(format);
5783 if(ddMatch.test(fvalue)){
5784 cell.title = ddText.replace("%0", fvalue);
5785 cell.className = " x-date-disabled";
5791 for(; i < startingPos; i++) {
5792 textEls[i].innerHTML = (++prevStart);
5793 d.setDate(d.getDate()+1);
5794 cells[i].className = "x-date-prevday";
5795 setCellClass(this, cells[i]);
5797 for(; i < days; i++){
5798 intDay = i - startingPos + 1;
5799 textEls[i].innerHTML = (intDay);
5800 d.setDate(d.getDate()+1);
5801 cells[i].className = "x-date-active";
5802 setCellClass(this, cells[i]);
5805 for(; i < 42; i++) {
5806 textEls[i].innerHTML = (++extraDays);
5807 d.setDate(d.getDate()+1);
5808 cells[i].className = "x-date-nextday";
5809 setCellClass(this, cells[i]);
5812 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5813 this.fireEvent('monthchange', this, date);
5815 if(!this.internalRender){
5816 var main = this.el.dom.firstChild;
5817 var w = main.offsetWidth;
5818 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5819 Roo.fly(main).setWidth(w);
5820 this.internalRender = true;
5821 // opera does not respect the auto grow header center column
5822 // then, after it gets a width opera refuses to recalculate
5823 // without a second pass
5824 if(Roo.isOpera && !this.secondPass){
5825 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5826 this.secondPass = true;
5827 this.update.defer(10, this, [date]);
5835 * Ext JS Library 1.1.1
5836 * Copyright(c) 2006-2007, Ext JS, LLC.
5838 * Originally Released Under LGPL - original licence link has changed is not relivant.
5841 * <script type="text/javascript">
5844 * @class Roo.TabPanel
5845 * @extends Roo.util.Observable
5846 * A lightweight tab container.
5850 // basic tabs 1, built from existing content
5851 var tabs = new Roo.TabPanel("tabs1");
5852 tabs.addTab("script", "View Script");
5853 tabs.addTab("markup", "View Markup");
5854 tabs.activate("script");
5856 // more advanced tabs, built from javascript
5857 var jtabs = new Roo.TabPanel("jtabs");
5858 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5860 // set up the UpdateManager
5861 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5862 var updater = tab2.getUpdateManager();
5863 updater.setDefaultUrl("ajax1.htm");
5864 tab2.on('activate', updater.refresh, updater, true);
5866 // Use setUrl for Ajax loading
5867 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5868 tab3.setUrl("ajax2.htm", null, true);
5871 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5874 jtabs.activate("jtabs-1");
5877 * Create a new TabPanel.
5878 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5879 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5881 Roo.TabPanel = function(container, config){
5883 * The container element for this TabPanel.
5886 this.el = Roo.get(container, true);
5888 if(typeof config == "boolean"){
5889 this.tabPosition = config ? "bottom" : "top";
5891 Roo.apply(this, config);
5894 if(this.tabPosition == "bottom"){
5895 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5896 this.el.addClass("x-tabs-bottom");
5898 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5899 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5900 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5902 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5904 if(this.tabPosition != "bottom"){
5905 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5908 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5909 this.el.addClass("x-tabs-top");
5913 this.bodyEl.setStyle("position", "relative");
5916 this.activateDelegate = this.activate.createDelegate(this);
5921 * Fires when the active tab changes
5922 * @param {Roo.TabPanel} this
5923 * @param {Roo.TabPanelItem} activePanel The new active tab
5927 * @event beforetabchange
5928 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5929 * @param {Roo.TabPanel} this
5930 * @param {Object} e Set cancel to true on this object to cancel the tab change
5931 * @param {Roo.TabPanelItem} tab The tab being changed to
5933 "beforetabchange" : true
5936 Roo.EventManager.onWindowResize(this.onResize, this);
5937 this.cpad = this.el.getPadding("lr");
5938 this.hiddenCount = 0;
5941 // toolbar on the tabbar support...
5943 var tcfg = this.toolbar;
5944 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5945 this.toolbar = new Roo.Toolbar(tcfg);
5947 var tbl = tcfg.container.child('table', true);
5948 tbl.setAttribute('width', '100%');
5955 Roo.TabPanel.superclass.constructor.call(this);
5958 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5960 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5962 tabPosition : "top",
5964 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5966 currentTabWidth : 0,
5968 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5972 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5976 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5978 preferredTabWidth : 175,
5980 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5984 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5986 monitorResize : true,
5988 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
5993 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5994 * @param {String} id The id of the div to use <b>or create</b>
5995 * @param {String} text The text for the tab
5996 * @param {String} content (optional) Content to put in the TabPanelItem body
5997 * @param {Boolean} closable (optional) True to create a close icon on the tab
5998 * @return {Roo.TabPanelItem} The created TabPanelItem
6000 addTab : function(id, text, content, closable){
6001 var item = new Roo.TabPanelItem(this, id, text, closable);
6002 this.addTabItem(item);
6004 item.setContent(content);
6010 * Returns the {@link Roo.TabPanelItem} with the specified id/index
6011 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6012 * @return {Roo.TabPanelItem}
6014 getTab : function(id){
6015 return this.items[id];
6019 * Hides the {@link Roo.TabPanelItem} with the specified id/index
6020 * @param {String/Number} id The id or index of the TabPanelItem to hide.
6022 hideTab : function(id){
6023 var t = this.items[id];
6027 this.autoSizeTabs();
6032 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6033 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6035 unhideTab : function(id){
6036 var t = this.items[id];
6040 this.autoSizeTabs();
6045 * Adds an existing {@link Roo.TabPanelItem}.
6046 * @param {Roo.TabPanelItem} item The TabPanelItem to add
6048 addTabItem : function(item){
6049 this.items[item.id] = item;
6050 this.items.push(item);
6051 if(this.resizeTabs){
6052 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6053 this.autoSizeTabs();
6060 * Removes a {@link Roo.TabPanelItem}.
6061 * @param {String/Number} id The id or index of the TabPanelItem to remove.
6063 removeTab : function(id){
6064 var items = this.items;
6065 var tab = items[id];
6066 if(!tab) { return; }
6067 var index = items.indexOf(tab);
6068 if(this.active == tab && items.length > 1){
6069 var newTab = this.getNextAvailable(index);
6074 this.stripEl.dom.removeChild(tab.pnode.dom);
6075 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6076 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6078 items.splice(index, 1);
6079 delete this.items[tab.id];
6080 tab.fireEvent("close", tab);
6081 tab.purgeListeners();
6082 this.autoSizeTabs();
6085 getNextAvailable : function(start){
6086 var items = this.items;
6088 // look for a next tab that will slide over to
6089 // replace the one being removed
6090 while(index < items.length){
6091 var item = items[++index];
6092 if(item && !item.isHidden()){
6096 // if one isn't found select the previous tab (on the left)
6099 var item = items[--index];
6100 if(item && !item.isHidden()){
6108 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6109 * @param {String/Number} id The id or index of the TabPanelItem to disable.
6111 disableTab : function(id){
6112 var tab = this.items[id];
6113 if(tab && this.active != tab){
6119 * Enables a {@link Roo.TabPanelItem} that is disabled.
6120 * @param {String/Number} id The id or index of the TabPanelItem to enable.
6122 enableTab : function(id){
6123 var tab = this.items[id];
6128 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6129 * @param {String/Number} id The id or index of the TabPanelItem to activate.
6130 * @return {Roo.TabPanelItem} The TabPanelItem.
6132 activate : function(id){
6133 var tab = this.items[id];
6137 if(tab == this.active || tab.disabled){
6141 this.fireEvent("beforetabchange", this, e, tab);
6142 if(e.cancel !== true && !tab.disabled){
6146 this.active = this.items[id];
6148 this.fireEvent("tabchange", this, this.active);
6154 * Gets the active {@link Roo.TabPanelItem}.
6155 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6157 getActiveTab : function(){
6162 * Updates the tab body element to fit the height of the container element
6163 * for overflow scrolling
6164 * @param {Number} targetHeight (optional) Override the starting height from the elements height
6166 syncHeight : function(targetHeight){
6167 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6168 var bm = this.bodyEl.getMargins();
6169 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6170 this.bodyEl.setHeight(newHeight);
6174 onResize : function(){
6175 if(this.monitorResize){
6176 this.autoSizeTabs();
6181 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6183 beginUpdate : function(){
6184 this.updating = true;
6188 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6190 endUpdate : function(){
6191 this.updating = false;
6192 this.autoSizeTabs();
6196 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6198 autoSizeTabs : function(){
6199 var count = this.items.length;
6200 var vcount = count - this.hiddenCount;
6201 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6204 var w = Math.max(this.el.getWidth() - this.cpad, 10);
6205 var availWidth = Math.floor(w / vcount);
6206 var b = this.stripBody;
6207 if(b.getWidth() > w){
6208 var tabs = this.items;
6209 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6210 if(availWidth < this.minTabWidth){
6211 /*if(!this.sleft){ // incomplete scrolling code
6212 this.createScrollButtons();
6215 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6218 if(this.currentTabWidth < this.preferredTabWidth){
6219 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6225 * Returns the number of tabs in this TabPanel.
6228 getCount : function(){
6229 return this.items.length;
6233 * Resizes all the tabs to the passed width
6234 * @param {Number} The new width
6236 setTabWidth : function(width){
6237 this.currentTabWidth = width;
6238 for(var i = 0, len = this.items.length; i < len; i++) {
6239 if(!this.items[i].isHidden()) {
6240 this.items[i].setWidth(width);
6246 * Destroys this TabPanel
6247 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6249 destroy : function(removeEl){
6250 Roo.EventManager.removeResizeListener(this.onResize, this);
6251 for(var i = 0, len = this.items.length; i < len; i++){
6252 this.items[i].purgeListeners();
6254 if(removeEl === true){
6262 * @class Roo.TabPanelItem
6263 * @extends Roo.util.Observable
6264 * Represents an individual item (tab plus body) in a TabPanel.
6265 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6266 * @param {String} id The id of this TabPanelItem
6267 * @param {String} text The text for the tab of this TabPanelItem
6268 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6270 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6272 * The {@link Roo.TabPanel} this TabPanelItem belongs to
6273 * @type Roo.TabPanel
6275 this.tabPanel = tabPanel;
6277 * The id for this TabPanelItem
6282 this.disabled = false;
6286 this.loaded = false;
6287 this.closable = closable;
6290 * The body element for this TabPanelItem.
6293 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6294 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6295 this.bodyEl.setStyle("display", "block");
6296 this.bodyEl.setStyle("zoom", "1");
6299 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6301 this.el = Roo.get(els.el, true);
6302 this.inner = Roo.get(els.inner, true);
6303 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6304 this.pnode = Roo.get(els.el.parentNode, true);
6305 this.el.on("mousedown", this.onTabMouseDown, this);
6306 this.el.on("click", this.onTabClick, this);
6309 var c = Roo.get(els.close, true);
6310 c.dom.title = this.closeText;
6311 c.addClassOnOver("close-over");
6312 c.on("click", this.closeClick, this);
6318 * Fires when this tab becomes the active tab.
6319 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6320 * @param {Roo.TabPanelItem} this
6324 * @event beforeclose
6325 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6326 * @param {Roo.TabPanelItem} this
6327 * @param {Object} e Set cancel to true on this object to cancel the close.
6329 "beforeclose": true,
6332 * Fires when this tab is closed.
6333 * @param {Roo.TabPanelItem} this
6338 * Fires when this tab is no longer the active tab.
6339 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6340 * @param {Roo.TabPanelItem} this
6344 this.hidden = false;
6346 Roo.TabPanelItem.superclass.constructor.call(this);
6349 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6350 purgeListeners : function(){
6351 Roo.util.Observable.prototype.purgeListeners.call(this);
6352 this.el.removeAllListeners();
6355 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6358 this.pnode.addClass("on");
6361 this.tabPanel.stripWrap.repaint();
6363 this.fireEvent("activate", this.tabPanel, this);
6367 * Returns true if this tab is the active tab.
6370 isActive : function(){
6371 return this.tabPanel.getActiveTab() == this;
6375 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6378 this.pnode.removeClass("on");
6380 this.fireEvent("deactivate", this.tabPanel, this);
6383 hideAction : function(){
6385 this.bodyEl.setStyle("position", "absolute");
6386 this.bodyEl.setLeft("-20000px");
6387 this.bodyEl.setTop("-20000px");
6390 showAction : function(){
6391 this.bodyEl.setStyle("position", "relative");
6392 this.bodyEl.setTop("");
6393 this.bodyEl.setLeft("");
6398 * Set the tooltip for the tab.
6399 * @param {String} tooltip The tab's tooltip
6401 setTooltip : function(text){
6402 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6403 this.textEl.dom.qtip = text;
6404 this.textEl.dom.removeAttribute('title');
6406 this.textEl.dom.title = text;
6410 onTabClick : function(e){
6412 this.tabPanel.activate(this.id);
6415 onTabMouseDown : function(e){
6417 this.tabPanel.activate(this.id);
6420 getWidth : function(){
6421 return this.inner.getWidth();
6424 setWidth : function(width){
6425 var iwidth = width - this.pnode.getPadding("lr");
6426 this.inner.setWidth(iwidth);
6427 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6428 this.pnode.setWidth(width);
6432 * Show or hide the tab
6433 * @param {Boolean} hidden True to hide or false to show.
6435 setHidden : function(hidden){
6436 this.hidden = hidden;
6437 this.pnode.setStyle("display", hidden ? "none" : "");
6441 * Returns true if this tab is "hidden"
6444 isHidden : function(){
6449 * Returns the text for this tab
6452 getText : function(){
6456 autoSize : function(){
6457 //this.el.beginMeasure();
6458 this.textEl.setWidth(1);
6460 * #2804 [new] Tabs in Roojs
6461 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6463 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6464 //this.el.endMeasure();
6468 * Sets the text for the tab (Note: this also sets the tooltip text)
6469 * @param {String} text The tab's text and tooltip
6471 setText : function(text){
6473 this.textEl.update(text);
6474 this.setTooltip(text);
6475 if(!this.tabPanel.resizeTabs){
6480 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6482 activate : function(){
6483 this.tabPanel.activate(this.id);
6487 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6489 disable : function(){
6490 if(this.tabPanel.active != this){
6491 this.disabled = true;
6492 this.pnode.addClass("disabled");
6497 * Enables this TabPanelItem if it was previously disabled.
6499 enable : function(){
6500 this.disabled = false;
6501 this.pnode.removeClass("disabled");
6505 * Sets the content for this TabPanelItem.
6506 * @param {String} content The content
6507 * @param {Boolean} loadScripts true to look for and load scripts
6509 setContent : function(content, loadScripts){
6510 this.bodyEl.update(content, loadScripts);
6514 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6515 * @return {Roo.UpdateManager} The UpdateManager
6517 getUpdateManager : function(){
6518 return this.bodyEl.getUpdateManager();
6522 * Set a URL to be used to load the content for this TabPanelItem.
6523 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6524 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6525 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6526 * @return {Roo.UpdateManager} The UpdateManager
6528 setUrl : function(url, params, loadOnce){
6529 if(this.refreshDelegate){
6530 this.un('activate', this.refreshDelegate);
6532 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6533 this.on("activate", this.refreshDelegate);
6534 return this.bodyEl.getUpdateManager();
6538 _handleRefresh : function(url, params, loadOnce){
6539 if(!loadOnce || !this.loaded){
6540 var updater = this.bodyEl.getUpdateManager();
6541 updater.update(url, params, this._setLoaded.createDelegate(this));
6546 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6547 * Will fail silently if the setUrl method has not been called.
6548 * This does not activate the panel, just updates its content.
6550 refresh : function(){
6551 if(this.refreshDelegate){
6552 this.loaded = false;
6553 this.refreshDelegate();
6558 _setLoaded : function(){
6563 closeClick : function(e){
6566 this.fireEvent("beforeclose", this, o);
6567 if(o.cancel !== true){
6568 this.tabPanel.removeTab(this.id);
6572 * The text displayed in the tooltip for the close icon.
6575 closeText : "Close this tab"
6579 Roo.TabPanel.prototype.createStrip = function(container){
6580 var strip = document.createElement("div");
6581 strip.className = "x-tabs-wrap";
6582 container.appendChild(strip);
6586 Roo.TabPanel.prototype.createStripList = function(strip){
6587 // div wrapper for retard IE
6588 // returns the "tr" element.
6589 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6590 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6591 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6592 return strip.firstChild.firstChild.firstChild.firstChild;
6595 Roo.TabPanel.prototype.createBody = function(container){
6596 var body = document.createElement("div");
6597 Roo.id(body, "tab-body");
6598 Roo.fly(body).addClass("x-tabs-body");
6599 container.appendChild(body);
6603 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6604 var body = Roo.getDom(id);
6606 body = document.createElement("div");
6609 Roo.fly(body).addClass("x-tabs-item-body");
6610 bodyEl.insertBefore(body, bodyEl.firstChild);
6614 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6615 var td = document.createElement("td");
6616 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6617 //stripEl.appendChild(td);
6619 td.className = "x-tabs-closable";
6621 this.closeTpl = new Roo.Template(
6622 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6623 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6624 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6627 var el = this.closeTpl.overwrite(td, {"text": text});
6628 var close = el.getElementsByTagName("div")[0];
6629 var inner = el.getElementsByTagName("em")[0];
6630 return {"el": el, "close": close, "inner": inner};
6633 this.tabTpl = new Roo.Template(
6634 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6635 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6638 var el = this.tabTpl.overwrite(td, {"text": text});
6639 var inner = el.getElementsByTagName("em")[0];
6640 return {"el": el, "inner": inner};
6644 * Ext JS Library 1.1.1
6645 * Copyright(c) 2006-2007, Ext JS, LLC.
6647 * Originally Released Under LGPL - original licence link has changed is not relivant.
6650 * <script type="text/javascript">
6655 * @extends Roo.util.Observable
6656 * Simple Button class
6657 * @cfg {String} text The button text
6658 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6659 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6660 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6661 * @cfg {Object} scope The scope of the handler
6662 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6663 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6664 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6665 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6666 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6667 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6668 applies if enableToggle = true)
6669 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6670 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6671 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6673 * Create a new button
6674 * @param {Object} config The config object
6676 Roo.Button = function(renderTo, config)
6680 renderTo = config.renderTo || false;
6683 Roo.apply(this, config);
6687 * Fires when this button is clicked
6688 * @param {Button} this
6689 * @param {EventObject} e The click event
6694 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6695 * @param {Button} this
6696 * @param {Boolean} pressed
6701 * Fires when the mouse hovers over the button
6702 * @param {Button} this
6703 * @param {Event} e The event object
6708 * Fires when the mouse exits the button
6709 * @param {Button} this
6710 * @param {Event} e The event object
6715 * Fires when the button is rendered
6716 * @param {Button} this
6721 this.menu = Roo.menu.MenuMgr.get(this.menu);
6723 // register listeners first!! - so render can be captured..
6724 Roo.util.Observable.call(this);
6726 this.render(renderTo);
6732 Roo.extend(Roo.Button, Roo.util.Observable, {
6738 * Read-only. True if this button is hidden
6743 * Read-only. True if this button is disabled
6748 * Read-only. True if this button is pressed (only if enableToggle = true)
6754 * @cfg {Number} tabIndex
6755 * The DOM tabIndex for this button (defaults to undefined)
6757 tabIndex : undefined,
6760 * @cfg {Boolean} enableToggle
6761 * True to enable pressed/not pressed toggling (defaults to false)
6763 enableToggle: false,
6766 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6770 * @cfg {String} menuAlign
6771 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6773 menuAlign : "tl-bl?",
6776 * @cfg {String} iconCls
6777 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6779 iconCls : undefined,
6781 * @cfg {String} type
6782 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6787 menuClassTarget: 'tr',
6790 * @cfg {String} clickEvent
6791 * The type of event to map to the button's event handler (defaults to 'click')
6793 clickEvent : 'click',
6796 * @cfg {Boolean} handleMouseEvents
6797 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6799 handleMouseEvents : true,
6802 * @cfg {String} tooltipType
6803 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6805 tooltipType : 'qtip',
6809 * A CSS class to apply to the button's main element.
6813 * @cfg {Roo.Template} template (Optional)
6814 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6815 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6816 * require code modifications if required elements (e.g. a button) aren't present.
6820 render : function(renderTo){
6822 if(this.hideParent){
6823 this.parentEl = Roo.get(renderTo);
6827 if(!Roo.Button.buttonTemplate){
6828 // hideous table template
6829 Roo.Button.buttonTemplate = new Roo.Template(
6830 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6831 '<td class="x-btn-left"><i> </i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i> </i></td>',
6832 "</tr></tbody></table>");
6834 this.template = Roo.Button.buttonTemplate;
6836 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6837 var btnEl = btn.child("button:first");
6838 btnEl.on('focus', this.onFocus, this);
6839 btnEl.on('blur', this.onBlur, this);
6841 btn.addClass(this.cls);
6844 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6847 btnEl.addClass(this.iconCls);
6849 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6852 if(this.tabIndex !== undefined){
6853 btnEl.dom.tabIndex = this.tabIndex;
6856 if(typeof this.tooltip == 'object'){
6857 Roo.QuickTips.tips(Roo.apply({
6861 btnEl.dom[this.tooltipType] = this.tooltip;
6865 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6869 this.el.dom.id = this.el.id = this.id;
6872 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6873 this.menu.on("show", this.onMenuShow, this);
6874 this.menu.on("hide", this.onMenuHide, this);
6876 btn.addClass("x-btn");
6877 if(Roo.isIE && !Roo.isIE7){
6878 this.autoWidth.defer(1, this);
6882 if(this.handleMouseEvents){
6883 btn.on("mouseover", this.onMouseOver, this);
6884 btn.on("mouseout", this.onMouseOut, this);
6885 btn.on("mousedown", this.onMouseDown, this);
6887 btn.on(this.clickEvent, this.onClick, this);
6888 //btn.on("mouseup", this.onMouseUp, this);
6895 Roo.ButtonToggleMgr.register(this);
6897 this.el.addClass("x-btn-pressed");
6900 var repeater = new Roo.util.ClickRepeater(btn,
6901 typeof this.repeat == "object" ? this.repeat : {}
6903 repeater.on("click", this.onClick, this);
6906 this.fireEvent('render', this);
6910 * Returns the button's underlying element
6911 * @return {Roo.Element} The element
6918 * Destroys this Button and removes any listeners.
6920 destroy : function(){
6921 Roo.ButtonToggleMgr.unregister(this);
6922 this.el.removeAllListeners();
6923 this.purgeListeners();
6928 autoWidth : function(){
6930 this.el.setWidth("auto");
6931 if(Roo.isIE7 && Roo.isStrict){
6932 var ib = this.el.child('button');
6933 if(ib && ib.getWidth() > 20){
6935 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6940 this.el.beginMeasure();
6942 if(this.el.getWidth() < this.minWidth){
6943 this.el.setWidth(this.minWidth);
6946 this.el.endMeasure();
6953 * Assigns this button's click handler
6954 * @param {Function} handler The function to call when the button is clicked
6955 * @param {Object} scope (optional) Scope for the function passed in
6957 setHandler : function(handler, scope){
6958 this.handler = handler;
6963 * Sets this button's text
6964 * @param {String} text The button text
6966 setText : function(text){
6969 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6975 * Gets the text for this button
6976 * @return {String} The button text
6978 getText : function(){
6986 this.hidden = false;
6988 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6998 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7003 * Convenience function for boolean show/hide
7004 * @param {Boolean} visible True to show, false to hide
7006 setVisible: function(visible){
7015 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7016 * @param {Boolean} state (optional) Force a particular state
7018 toggle : function(state){
7019 state = state === undefined ? !this.pressed : state;
7020 if(state != this.pressed){
7022 this.el.addClass("x-btn-pressed");
7023 this.pressed = true;
7024 this.fireEvent("toggle", this, true);
7026 this.el.removeClass("x-btn-pressed");
7027 this.pressed = false;
7028 this.fireEvent("toggle", this, false);
7030 if(this.toggleHandler){
7031 this.toggleHandler.call(this.scope || this, this, state);
7040 this.el.child('button:first').focus();
7044 * Disable this button
7046 disable : function(){
7048 this.el.addClass("x-btn-disabled");
7050 this.disabled = true;
7054 * Enable this button
7056 enable : function(){
7058 this.el.removeClass("x-btn-disabled");
7060 this.disabled = false;
7064 * Convenience function for boolean enable/disable
7065 * @param {Boolean} enabled True to enable, false to disable
7067 setDisabled : function(v){
7068 this[v !== true ? "enable" : "disable"]();
7072 onClick : function(e)
7081 if(this.enableToggle){
7084 if(this.menu && !this.menu.isVisible()){
7085 this.menu.show(this.el, this.menuAlign);
7087 this.fireEvent("click", this, e);
7089 this.el.removeClass("x-btn-over");
7090 this.handler.call(this.scope || this, this, e);
7095 onMouseOver : function(e){
7097 this.el.addClass("x-btn-over");
7098 this.fireEvent('mouseover', this, e);
7102 onMouseOut : function(e){
7103 if(!e.within(this.el, true)){
7104 this.el.removeClass("x-btn-over");
7105 this.fireEvent('mouseout', this, e);
7109 onFocus : function(e){
7111 this.el.addClass("x-btn-focus");
7115 onBlur : function(e){
7116 this.el.removeClass("x-btn-focus");
7119 onMouseDown : function(e){
7120 if(!this.disabled && e.button == 0){
7121 this.el.addClass("x-btn-click");
7122 Roo.get(document).on('mouseup', this.onMouseUp, this);
7126 onMouseUp : function(e){
7128 this.el.removeClass("x-btn-click");
7129 Roo.get(document).un('mouseup', this.onMouseUp, this);
7133 onMenuShow : function(e){
7134 this.el.addClass("x-btn-menu-active");
7137 onMenuHide : function(e){
7138 this.el.removeClass("x-btn-menu-active");
7142 // Private utility class used by Button
7143 Roo.ButtonToggleMgr = function(){
7146 function toggleGroup(btn, state){
7148 var g = groups[btn.toggleGroup];
7149 for(var i = 0, l = g.length; i < l; i++){
7158 register : function(btn){
7159 if(!btn.toggleGroup){
7162 var g = groups[btn.toggleGroup];
7164 g = groups[btn.toggleGroup] = [];
7167 btn.on("toggle", toggleGroup);
7170 unregister : function(btn){
7171 if(!btn.toggleGroup){
7174 var g = groups[btn.toggleGroup];
7177 btn.un("toggle", toggleGroup);
7183 * Ext JS Library 1.1.1
7184 * Copyright(c) 2006-2007, Ext JS, LLC.
7186 * Originally Released Under LGPL - original licence link has changed is not relivant.
7189 * <script type="text/javascript">
7193 * @class Roo.SplitButton
7194 * @extends Roo.Button
7195 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7196 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
7197 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7198 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7199 * @cfg {String} arrowTooltip The title attribute of the arrow
7201 * Create a new menu button
7202 * @param {String/HTMLElement/Element} renderTo The element to append the button to
7203 * @param {Object} config The config object
7205 Roo.SplitButton = function(renderTo, config){
7206 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7209 * Fires when this button's arrow is clicked
7210 * @param {SplitButton} this
7211 * @param {EventObject} e The click event
7213 this.addEvents({"arrowclick":true});
7216 Roo.extend(Roo.SplitButton, Roo.Button, {
7217 render : function(renderTo){
7218 // this is one sweet looking template!
7219 var tpl = new Roo.Template(
7220 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7221 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7222 '<tr><td class="x-btn-left"><i> </i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7223 "</tbody></table></td><td>",
7224 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7225 '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button"> </button></td><td class="x-btn-right"><i> </i></td></tr>',
7226 "</tbody></table></td></tr></table>"
7228 var btn = tpl.append(renderTo, [this.text, this.type], true);
7229 var btnEl = btn.child("button");
7231 btn.addClass(this.cls);
7234 btnEl.setStyle('background-image', 'url(' +this.icon +')');
7237 btnEl.addClass(this.iconCls);
7239 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7243 if(this.handleMouseEvents){
7244 btn.on("mouseover", this.onMouseOver, this);
7245 btn.on("mouseout", this.onMouseOut, this);
7246 btn.on("mousedown", this.onMouseDown, this);
7247 btn.on("mouseup", this.onMouseUp, this);
7249 btn.on(this.clickEvent, this.onClick, this);
7251 if(typeof this.tooltip == 'object'){
7252 Roo.QuickTips.tips(Roo.apply({
7256 btnEl.dom[this.tooltipType] = this.tooltip;
7259 if(this.arrowTooltip){
7260 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7269 this.el.addClass("x-btn-pressed");
7271 if(Roo.isIE && !Roo.isIE7){
7272 this.autoWidth.defer(1, this);
7277 this.menu.on("show", this.onMenuShow, this);
7278 this.menu.on("hide", this.onMenuHide, this);
7280 this.fireEvent('render', this);
7284 autoWidth : function(){
7286 var tbl = this.el.child("table:first");
7287 var tbl2 = this.el.child("table:last");
7288 this.el.setWidth("auto");
7289 tbl.setWidth("auto");
7290 if(Roo.isIE7 && Roo.isStrict){
7291 var ib = this.el.child('button:first');
7292 if(ib && ib.getWidth() > 20){
7294 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7299 this.el.beginMeasure();
7301 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7302 tbl.setWidth(this.minWidth-tbl2.getWidth());
7305 this.el.endMeasure();
7308 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7312 * Sets this button's click handler
7313 * @param {Function} handler The function to call when the button is clicked
7314 * @param {Object} scope (optional) Scope for the function passed above
7316 setHandler : function(handler, scope){
7317 this.handler = handler;
7322 * Sets this button's arrow click handler
7323 * @param {Function} handler The function to call when the arrow is clicked
7324 * @param {Object} scope (optional) Scope for the function passed above
7326 setArrowHandler : function(handler, scope){
7327 this.arrowHandler = handler;
7336 this.el.child("button:first").focus();
7341 onClick : function(e){
7344 if(e.getTarget(".x-btn-menu-arrow-wrap")){
7345 if(this.menu && !this.menu.isVisible()){
7346 this.menu.show(this.el, this.menuAlign);
7348 this.fireEvent("arrowclick", this, e);
7349 if(this.arrowHandler){
7350 this.arrowHandler.call(this.scope || this, this, e);
7353 this.fireEvent("click", this, e);
7355 this.handler.call(this.scope || this, this, e);
7361 onMouseDown : function(e){
7363 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7367 onMouseUp : function(e){
7368 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7374 Roo.MenuButton = Roo.SplitButton;/*
7376 * Ext JS Library 1.1.1
7377 * Copyright(c) 2006-2007, Ext JS, LLC.
7379 * Originally Released Under LGPL - original licence link has changed is not relivant.
7382 * <script type="text/javascript">
7386 * @class Roo.Toolbar
7387 * Basic Toolbar class.
7389 * Creates a new Toolbar
7390 * @param {Object} container The config object
7392 Roo.Toolbar = function(container, buttons, config)
7394 /// old consturctor format still supported..
7395 if(container instanceof Array){ // omit the container for later rendering
7396 buttons = container;
7400 if (typeof(container) == 'object' && container.xtype) {
7402 container = config.container;
7403 buttons = config.buttons || []; // not really - use items!!
7406 if (config && config.items) {
7407 xitems = config.items;
7408 delete config.items;
7410 Roo.apply(this, config);
7411 this.buttons = buttons;
7414 this.render(container);
7416 this.xitems = xitems;
7417 Roo.each(xitems, function(b) {
7423 Roo.Toolbar.prototype = {
7425 * @cfg {Array} items
7426 * array of button configs or elements to add (will be converted to a MixedCollection)
7430 * @cfg {String/HTMLElement/Element} container
7431 * The id or element that will contain the toolbar
7434 render : function(ct){
7435 this.el = Roo.get(ct);
7437 this.el.addClass(this.cls);
7439 // using a table allows for vertical alignment
7440 // 100% width is needed by Safari...
7441 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7442 this.tr = this.el.child("tr", true);
7444 this.items = new Roo.util.MixedCollection(false, function(o){
7445 return o.id || ("item" + (++autoId));
7448 this.add.apply(this, this.buttons);
7449 delete this.buttons;
7454 * Adds element(s) to the toolbar -- this function takes a variable number of
7455 * arguments of mixed type and adds them to the toolbar.
7456 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7458 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7459 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7460 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7461 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7462 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7463 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7464 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7465 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7466 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7468 * @param {Mixed} arg2
7469 * @param {Mixed} etc.
7472 var a = arguments, l = a.length;
7473 for(var i = 0; i < l; i++){
7478 _add : function(el) {
7481 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7484 if (el.applyTo){ // some kind of form field
7485 return this.addField(el);
7487 if (el.render){ // some kind of Toolbar.Item
7488 return this.addItem(el);
7490 if (typeof el == "string"){ // string
7491 if(el == "separator" || el == "-"){
7492 return this.addSeparator();
7495 return this.addSpacer();
7498 return this.addFill();
7500 return this.addText(el);
7503 if(el.tagName){ // element
7504 return this.addElement(el);
7506 if(typeof el == "object"){ // must be button config?
7507 return this.addButton(el);
7515 * Add an Xtype element
7516 * @param {Object} xtype Xtype Object
7517 * @return {Object} created Object
7519 addxtype : function(e){
7524 * Returns the Element for this toolbar.
7525 * @return {Roo.Element}
7533 * @return {Roo.Toolbar.Item} The separator item
7535 addSeparator : function(){
7536 return this.addItem(new Roo.Toolbar.Separator());
7540 * Adds a spacer element
7541 * @return {Roo.Toolbar.Spacer} The spacer item
7543 addSpacer : function(){
7544 return this.addItem(new Roo.Toolbar.Spacer());
7548 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7549 * @return {Roo.Toolbar.Fill} The fill item
7551 addFill : function(){
7552 return this.addItem(new Roo.Toolbar.Fill());
7556 * Adds any standard HTML element to the toolbar
7557 * @param {String/HTMLElement/Element} el The element or id of the element to add
7558 * @return {Roo.Toolbar.Item} The element's item
7560 addElement : function(el){
7561 return this.addItem(new Roo.Toolbar.Item(el));
7564 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7565 * @type Roo.util.MixedCollection
7570 * Adds any Toolbar.Item or subclass
7571 * @param {Roo.Toolbar.Item} item
7572 * @return {Roo.Toolbar.Item} The item
7574 addItem : function(item){
7575 var td = this.nextBlock();
7577 this.items.add(item);
7582 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7583 * @param {Object/Array} config A button config or array of configs
7584 * @return {Roo.Toolbar.Button/Array}
7586 addButton : function(config){
7587 if(config instanceof Array){
7589 for(var i = 0, len = config.length; i < len; i++) {
7590 buttons.push(this.addButton(config[i]));
7595 if(!(config instanceof Roo.Toolbar.Button)){
7597 new Roo.Toolbar.SplitButton(config) :
7598 new Roo.Toolbar.Button(config);
7600 var td = this.nextBlock();
7607 * Adds text to the toolbar
7608 * @param {String} text The text to add
7609 * @return {Roo.Toolbar.Item} The element's item
7611 addText : function(text){
7612 return this.addItem(new Roo.Toolbar.TextItem(text));
7616 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7617 * @param {Number} index The index where the item is to be inserted
7618 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7619 * @return {Roo.Toolbar.Button/Item}
7621 insertButton : function(index, item){
7622 if(item instanceof Array){
7624 for(var i = 0, len = item.length; i < len; i++) {
7625 buttons.push(this.insertButton(index + i, item[i]));
7629 if (!(item instanceof Roo.Toolbar.Button)){
7630 item = new Roo.Toolbar.Button(item);
7632 var td = document.createElement("td");
7633 this.tr.insertBefore(td, this.tr.childNodes[index]);
7635 this.items.insert(index, item);
7640 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7641 * @param {Object} config
7642 * @return {Roo.Toolbar.Item} The element's item
7644 addDom : function(config, returnEl){
7645 var td = this.nextBlock();
7646 Roo.DomHelper.overwrite(td, config);
7647 var ti = new Roo.Toolbar.Item(td.firstChild);
7654 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7655 * @type Roo.util.MixedCollection
7660 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7661 * Note: the field should not have been rendered yet. For a field that has already been
7662 * rendered, use {@link #addElement}.
7663 * @param {Roo.form.Field} field
7664 * @return {Roo.ToolbarItem}
7668 addField : function(field) {
7671 this.fields = new Roo.util.MixedCollection(false, function(o){
7672 return o.id || ("item" + (++autoId));
7677 var td = this.nextBlock();
7679 var ti = new Roo.Toolbar.Item(td.firstChild);
7682 this.fields.add(field);
7693 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7694 this.el.child('div').hide();
7702 this.el.child('div').show();
7706 nextBlock : function(){
7707 var td = document.createElement("td");
7708 this.tr.appendChild(td);
7713 destroy : function(){
7714 if(this.items){ // rendered?
7715 Roo.destroy.apply(Roo, this.items.items);
7717 if(this.fields){ // rendered?
7718 Roo.destroy.apply(Roo, this.fields.items);
7720 Roo.Element.uncache(this.el, this.tr);
7725 * @class Roo.Toolbar.Item
7726 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7728 * Creates a new Item
7729 * @param {HTMLElement} el
7731 Roo.Toolbar.Item = function(el){
7733 if (typeof (el.xtype) != 'undefined') {
7738 this.el = Roo.getDom(el);
7739 this.id = Roo.id(this.el);
7740 this.hidden = false;
7745 * Fires when the button is rendered
7746 * @param {Button} this
7750 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7752 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7753 //Roo.Toolbar.Item.prototype = {
7756 * Get this item's HTML Element
7757 * @return {HTMLElement}
7764 render : function(td){
7767 td.appendChild(this.el);
7769 this.fireEvent('render', this);
7773 * Removes and destroys this item.
7775 destroy : function(){
7776 this.td.parentNode.removeChild(this.td);
7783 this.hidden = false;
7784 this.td.style.display = "";
7792 this.td.style.display = "none";
7796 * Convenience function for boolean show/hide.
7797 * @param {Boolean} visible true to show/false to hide
7799 setVisible: function(visible){
7808 * Try to focus this item.
7811 Roo.fly(this.el).focus();
7815 * Disables this item.
7817 disable : function(){
7818 Roo.fly(this.td).addClass("x-item-disabled");
7819 this.disabled = true;
7820 this.el.disabled = true;
7824 * Enables this item.
7826 enable : function(){
7827 Roo.fly(this.td).removeClass("x-item-disabled");
7828 this.disabled = false;
7829 this.el.disabled = false;
7835 * @class Roo.Toolbar.Separator
7836 * @extends Roo.Toolbar.Item
7837 * A simple toolbar separator class
7839 * Creates a new Separator
7841 Roo.Toolbar.Separator = function(cfg){
7843 var s = document.createElement("span");
7844 s.className = "ytb-sep";
7849 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7851 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7853 disable:Roo.emptyFn,
7858 * @class Roo.Toolbar.Spacer
7859 * @extends Roo.Toolbar.Item
7860 * A simple element that adds extra horizontal space to a toolbar.
7862 * Creates a new Spacer
7864 Roo.Toolbar.Spacer = function(cfg){
7865 var s = document.createElement("div");
7866 s.className = "ytb-spacer";
7870 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7872 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7874 disable:Roo.emptyFn,
7879 * @class Roo.Toolbar.Fill
7880 * @extends Roo.Toolbar.Spacer
7881 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7883 * Creates a new Spacer
7885 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7887 render : function(td){
7888 td.style.width = '100%';
7889 Roo.Toolbar.Fill.superclass.render.call(this, td);
7894 * @class Roo.Toolbar.TextItem
7895 * @extends Roo.Toolbar.Item
7896 * A simple class that renders text directly into a toolbar.
7898 * Creates a new TextItem
7899 * @param {String} text
7901 Roo.Toolbar.TextItem = function(cfg){
7902 var text = cfg || "";
7903 if (typeof(cfg) == 'object') {
7904 text = cfg.text || "";
7908 var s = document.createElement("span");
7909 s.className = "ytb-text";
7915 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7917 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7921 disable:Roo.emptyFn,
7926 * @class Roo.Toolbar.Button
7927 * @extends Roo.Button
7928 * A button that renders into a toolbar.
7930 * Creates a new Button
7931 * @param {Object} config A standard {@link Roo.Button} config object
7933 Roo.Toolbar.Button = function(config){
7934 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7936 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7937 render : function(td){
7939 Roo.Toolbar.Button.superclass.render.call(this, td);
7943 * Removes and destroys this button
7945 destroy : function(){
7946 Roo.Toolbar.Button.superclass.destroy.call(this);
7947 this.td.parentNode.removeChild(this.td);
7954 this.hidden = false;
7955 this.td.style.display = "";
7963 this.td.style.display = "none";
7967 * Disables this item
7969 disable : function(){
7970 Roo.fly(this.td).addClass("x-item-disabled");
7971 this.disabled = true;
7977 enable : function(){
7978 Roo.fly(this.td).removeClass("x-item-disabled");
7979 this.disabled = false;
7983 Roo.ToolbarButton = Roo.Toolbar.Button;
7986 * @class Roo.Toolbar.SplitButton
7987 * @extends Roo.SplitButton
7988 * A menu button that renders into a toolbar.
7990 * Creates a new SplitButton
7991 * @param {Object} config A standard {@link Roo.SplitButton} config object
7993 Roo.Toolbar.SplitButton = function(config){
7994 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7996 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7997 render : function(td){
7999 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8003 * Removes and destroys this button
8005 destroy : function(){
8006 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8007 this.td.parentNode.removeChild(this.td);
8014 this.hidden = false;
8015 this.td.style.display = "";
8023 this.td.style.display = "none";
8028 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8030 * Ext JS Library 1.1.1
8031 * Copyright(c) 2006-2007, Ext JS, LLC.
8033 * Originally Released Under LGPL - original licence link has changed is not relivant.
8036 * <script type="text/javascript">
8040 * @class Roo.PagingToolbar
8041 * @extends Roo.Toolbar
8042 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8044 * Create a new PagingToolbar
8045 * @param {Object} config The config object
8047 Roo.PagingToolbar = function(el, ds, config)
8049 // old args format still supported... - xtype is prefered..
8050 if (typeof(el) == 'object' && el.xtype) {
8051 // created from xtype...
8054 el = config.container;
8058 items = config.items;
8062 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8065 this.renderButtons(this.el);
8068 // supprot items array.
8070 Roo.each(items, function(e) {
8071 this.add(Roo.factory(e));
8076 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8078 * @cfg {Roo.data.Store} dataSource
8079 * The underlying data store providing the paged data
8082 * @cfg {String/HTMLElement/Element} container
8083 * container The id or element that will contain the toolbar
8086 * @cfg {Boolean} displayInfo
8087 * True to display the displayMsg (defaults to false)
8090 * @cfg {Number} pageSize
8091 * The number of records to display per page (defaults to 20)
8095 * @cfg {String} displayMsg
8096 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8098 displayMsg : 'Displaying {0} - {1} of {2}',
8100 * @cfg {String} emptyMsg
8101 * The message to display when no records are found (defaults to "No data to display")
8103 emptyMsg : 'No data to display',
8105 * Customizable piece of the default paging text (defaults to "Page")
8108 beforePageText : "Page",
8110 * Customizable piece of the default paging text (defaults to "of %0")
8113 afterPageText : "of {0}",
8115 * Customizable piece of the default paging text (defaults to "First Page")
8118 firstText : "First Page",
8120 * Customizable piece of the default paging text (defaults to "Previous Page")
8123 prevText : "Previous Page",
8125 * Customizable piece of the default paging text (defaults to "Next Page")
8128 nextText : "Next Page",
8130 * Customizable piece of the default paging text (defaults to "Last Page")
8133 lastText : "Last Page",
8135 * Customizable piece of the default paging text (defaults to "Refresh")
8138 refreshText : "Refresh",
8141 renderButtons : function(el){
8142 Roo.PagingToolbar.superclass.render.call(this, el);
8143 this.first = this.addButton({
8144 tooltip: this.firstText,
8145 cls: "x-btn-icon x-grid-page-first",
8147 handler: this.onClick.createDelegate(this, ["first"])
8149 this.prev = this.addButton({
8150 tooltip: this.prevText,
8151 cls: "x-btn-icon x-grid-page-prev",
8153 handler: this.onClick.createDelegate(this, ["prev"])
8155 //this.addSeparator();
8156 this.add(this.beforePageText);
8157 this.field = Roo.get(this.addDom({
8162 cls: "x-grid-page-number"
8164 this.field.on("keydown", this.onPagingKeydown, this);
8165 this.field.on("focus", function(){this.dom.select();});
8166 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8167 this.field.setHeight(18);
8168 //this.addSeparator();
8169 this.next = this.addButton({
8170 tooltip: this.nextText,
8171 cls: "x-btn-icon x-grid-page-next",
8173 handler: this.onClick.createDelegate(this, ["next"])
8175 this.last = this.addButton({
8176 tooltip: this.lastText,
8177 cls: "x-btn-icon x-grid-page-last",
8179 handler: this.onClick.createDelegate(this, ["last"])
8181 //this.addSeparator();
8182 this.loading = this.addButton({
8183 tooltip: this.refreshText,
8184 cls: "x-btn-icon x-grid-loading",
8185 handler: this.onClick.createDelegate(this, ["refresh"])
8188 if(this.displayInfo){
8189 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8194 updateInfo : function(){
8196 var count = this.ds.getCount();
8197 var msg = count == 0 ?
8201 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
8203 this.displayEl.update(msg);
8208 onLoad : function(ds, r, o){
8209 this.cursor = o.params ? o.params.start : 0;
8210 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8212 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8213 this.field.dom.value = ap;
8214 this.first.setDisabled(ap == 1);
8215 this.prev.setDisabled(ap == 1);
8216 this.next.setDisabled(ap == ps);
8217 this.last.setDisabled(ap == ps);
8218 this.loading.enable();
8223 getPageData : function(){
8224 var total = this.ds.getTotalCount();
8227 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8228 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8233 onLoadError : function(){
8234 this.loading.enable();
8238 onPagingKeydown : function(e){
8240 var d = this.getPageData();
8242 var v = this.field.dom.value, pageNum;
8243 if(!v || isNaN(pageNum = parseInt(v, 10))){
8244 this.field.dom.value = d.activePage;
8247 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8248 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8251 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))
8253 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8254 this.field.dom.value = pageNum;
8255 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8258 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8260 var v = this.field.dom.value, pageNum;
8261 var increment = (e.shiftKey) ? 10 : 1;
8262 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8265 if(!v || isNaN(pageNum = parseInt(v, 10))) {
8266 this.field.dom.value = d.activePage;
8269 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8271 this.field.dom.value = parseInt(v, 10) + increment;
8272 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8273 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8280 beforeLoad : function(){
8282 this.loading.disable();
8287 onClick : function(which){
8291 ds.load({params:{start: 0, limit: this.pageSize}});
8294 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8297 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8300 var total = ds.getTotalCount();
8301 var extra = total % this.pageSize;
8302 var lastStart = extra ? (total - extra) : total-this.pageSize;
8303 ds.load({params:{start: lastStart, limit: this.pageSize}});
8306 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8312 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8313 * @param {Roo.data.Store} store The data store to unbind
8315 unbind : function(ds){
8316 ds.un("beforeload", this.beforeLoad, this);
8317 ds.un("load", this.onLoad, this);
8318 ds.un("loadexception", this.onLoadError, this);
8319 ds.un("remove", this.updateInfo, this);
8320 ds.un("add", this.updateInfo, this);
8321 this.ds = undefined;
8325 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8326 * @param {Roo.data.Store} store The data store to bind
8328 bind : function(ds){
8329 ds.on("beforeload", this.beforeLoad, this);
8330 ds.on("load", this.onLoad, this);
8331 ds.on("loadexception", this.onLoadError, this);
8332 ds.on("remove", this.updateInfo, this);
8333 ds.on("add", this.updateInfo, this);
8338 * Ext JS Library 1.1.1
8339 * Copyright(c) 2006-2007, Ext JS, LLC.
8341 * Originally Released Under LGPL - original licence link has changed is not relivant.
8344 * <script type="text/javascript">
8348 * @class Roo.Resizable
8349 * @extends Roo.util.Observable
8350 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8351 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8352 * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8353 * the element will be wrapped for you automatically.</p>
8354 * <p>Here is the list of valid resize handles:</p>
8357 ------ -------------------
8366 'hd' horizontal drag
8369 * <p>Here's an example showing the creation of a typical Resizable:</p>
8371 var resizer = new Roo.Resizable("element-id", {
8379 resizer.on("resize", myHandler);
8381 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8382 * resizer.east.setDisplayed(false);</p>
8383 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8384 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8385 * resize operation's new size (defaults to [0, 0])
8386 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8387 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8388 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8389 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8390 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8391 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8392 * @cfg {Number} width The width of the element in pixels (defaults to null)
8393 * @cfg {Number} height The height of the element in pixels (defaults to null)
8394 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8395 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8396 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8397 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8398 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8399 * in favor of the handles config option (defaults to false)
8400 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8401 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8402 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8403 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8404 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8405 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8406 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8407 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8408 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8409 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8410 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8412 * Create a new resizable component
8413 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8414 * @param {Object} config configuration options
8416 Roo.Resizable = function(el, config)
8418 this.el = Roo.get(el);
8420 if(config && config.wrap){
8421 config.resizeChild = this.el;
8422 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8423 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8424 this.el.setStyle("overflow", "hidden");
8425 this.el.setPositioning(config.resizeChild.getPositioning());
8426 config.resizeChild.clearPositioning();
8427 if(!config.width || !config.height){
8428 var csize = config.resizeChild.getSize();
8429 this.el.setSize(csize.width, csize.height);
8431 if(config.pinned && !config.adjustments){
8432 config.adjustments = "auto";
8436 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8437 this.proxy.unselectable();
8438 this.proxy.enableDisplayMode('block');
8440 Roo.apply(this, config);
8443 this.disableTrackOver = true;
8444 this.el.addClass("x-resizable-pinned");
8446 // if the element isn't positioned, make it relative
8447 var position = this.el.getStyle("position");
8448 if(position != "absolute" && position != "fixed"){
8449 this.el.setStyle("position", "relative");
8451 if(!this.handles){ // no handles passed, must be legacy style
8452 this.handles = 's,e,se';
8453 if(this.multiDirectional){
8454 this.handles += ',n,w';
8457 if(this.handles == "all"){
8458 this.handles = "n s e w ne nw se sw";
8460 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8461 var ps = Roo.Resizable.positions;
8462 for(var i = 0, len = hs.length; i < len; i++){
8463 if(hs[i] && ps[hs[i]]){
8464 var pos = ps[hs[i]];
8465 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8469 this.corner = this.southeast;
8471 // updateBox = the box can move..
8472 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8473 this.updateBox = true;
8476 this.activeHandle = null;
8478 if(this.resizeChild){
8479 if(typeof this.resizeChild == "boolean"){
8480 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8482 this.resizeChild = Roo.get(this.resizeChild, true);
8486 if(this.adjustments == "auto"){
8487 var rc = this.resizeChild;
8488 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8489 if(rc && (hw || hn)){
8490 rc.position("relative");
8491 rc.setLeft(hw ? hw.el.getWidth() : 0);
8492 rc.setTop(hn ? hn.el.getHeight() : 0);
8494 this.adjustments = [
8495 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8496 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8501 this.dd = this.dynamic ?
8502 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8503 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8509 * @event beforeresize
8510 * Fired before resize is allowed. Set enabled to false to cancel resize.
8511 * @param {Roo.Resizable} this
8512 * @param {Roo.EventObject} e The mousedown event
8514 "beforeresize" : true,
8518 * @param {Roo.Resizable} this
8519 * @param {Number} x The new x position
8520 * @param {Number} y The new y position
8521 * @param {Number} w The new w width
8522 * @param {Number} h The new h hight
8523 * @param {Roo.EventObject} e The mouseup event
8528 * Fired after a resize.
8529 * @param {Roo.Resizable} this
8530 * @param {Number} width The new width
8531 * @param {Number} height The new height
8532 * @param {Roo.EventObject} e The mouseup event
8537 if(this.width !== null && this.height !== null){
8538 this.resizeTo(this.width, this.height);
8540 this.updateChildSize();
8543 this.el.dom.style.zoom = 1;
8545 Roo.Resizable.superclass.constructor.call(this);
8548 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8549 resizeChild : false,
8550 adjustments : [0, 0],
8560 multiDirectional : false,
8561 disableTrackOver : false,
8562 easing : 'easeOutStrong',
8564 heightIncrement : 0,
8568 preserveRatio : false,
8575 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8577 constrainTo: undefined,
8579 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8581 resizeRegion: undefined,
8585 * Perform a manual resize
8586 * @param {Number} width
8587 * @param {Number} height
8589 resizeTo : function(width, height){
8590 this.el.setSize(width, height);
8591 this.updateChildSize();
8592 this.fireEvent("resize", this, width, height, null);
8596 startSizing : function(e, handle){
8597 this.fireEvent("beforeresize", this, e);
8598 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8601 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8602 this.overlay.unselectable();
8603 this.overlay.enableDisplayMode("block");
8604 this.overlay.on("mousemove", this.onMouseMove, this);
8605 this.overlay.on("mouseup", this.onMouseUp, this);
8607 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8609 this.resizing = true;
8610 this.startBox = this.el.getBox();
8611 this.startPoint = e.getXY();
8612 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8613 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8615 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8616 this.overlay.show();
8618 if(this.constrainTo) {
8619 var ct = Roo.get(this.constrainTo);
8620 this.resizeRegion = ct.getRegion().adjust(
8621 ct.getFrameWidth('t'),
8622 ct.getFrameWidth('l'),
8623 -ct.getFrameWidth('b'),
8624 -ct.getFrameWidth('r')
8628 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8630 this.proxy.setBox(this.startBox);
8632 this.proxy.setStyle('visibility', 'visible');
8638 onMouseDown : function(handle, e){
8641 this.activeHandle = handle;
8642 this.startSizing(e, handle);
8647 onMouseUp : function(e){
8648 var size = this.resizeElement();
8649 this.resizing = false;
8651 this.overlay.hide();
8653 this.fireEvent("resize", this, size.width, size.height, e);
8657 updateChildSize : function(){
8659 if(this.resizeChild){
8661 var child = this.resizeChild;
8662 var adj = this.adjustments;
8663 if(el.dom.offsetWidth){
8664 var b = el.getSize(true);
8665 child.setSize(b.width+adj[0], b.height+adj[1]);
8667 // Second call here for IE
8668 // The first call enables instant resizing and
8669 // the second call corrects scroll bars if they
8672 setTimeout(function(){
8673 if(el.dom.offsetWidth){
8674 var b = el.getSize(true);
8675 child.setSize(b.width+adj[0], b.height+adj[1]);
8683 snap : function(value, inc, min){
8684 if(!inc || !value) {
8687 var newValue = value;
8688 var m = value % inc;
8691 newValue = value + (inc-m);
8693 newValue = value - m;
8696 return Math.max(min, newValue);
8700 resizeElement : function(){
8701 var box = this.proxy.getBox();
8703 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8705 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8707 this.updateChildSize();
8715 constrain : function(v, diff, m, mx){
8718 }else if(v - diff > mx){
8725 onMouseMove : function(e){
8728 try{// try catch so if something goes wrong the user doesn't get hung
8730 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8734 //var curXY = this.startPoint;
8735 var curSize = this.curSize || this.startBox;
8736 var x = this.startBox.x, y = this.startBox.y;
8738 var w = curSize.width, h = curSize.height;
8740 var mw = this.minWidth, mh = this.minHeight;
8741 var mxw = this.maxWidth, mxh = this.maxHeight;
8742 var wi = this.widthIncrement;
8743 var hi = this.heightIncrement;
8745 var eventXY = e.getXY();
8746 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8747 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8749 var pos = this.activeHandle.position;
8754 w = Math.min(Math.max(mw, w), mxw);
8759 h = Math.min(Math.max(mh, h), mxh);
8764 w = Math.min(Math.max(mw, w), mxw);
8765 h = Math.min(Math.max(mh, h), mxh);
8768 diffY = this.constrain(h, diffY, mh, mxh);
8775 var adiffX = Math.abs(diffX);
8776 var sub = (adiffX % wi); // how much
8777 if (sub > (wi/2)) { // far enough to snap
8778 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8780 // remove difference..
8781 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8785 x = Math.max(this.minX, x);
8788 diffX = this.constrain(w, diffX, mw, mxw);
8794 w = Math.min(Math.max(mw, w), mxw);
8795 diffY = this.constrain(h, diffY, mh, mxh);
8800 diffX = this.constrain(w, diffX, mw, mxw);
8801 diffY = this.constrain(h, diffY, mh, mxh);
8808 diffX = this.constrain(w, diffX, mw, mxw);
8810 h = Math.min(Math.max(mh, h), mxh);
8816 var sw = this.snap(w, wi, mw);
8817 var sh = this.snap(h, hi, mh);
8818 if(sw != w || sh != h){
8841 if(this.preserveRatio){
8846 h = Math.min(Math.max(mh, h), mxh);
8851 w = Math.min(Math.max(mw, w), mxw);
8856 w = Math.min(Math.max(mw, w), mxw);
8862 w = Math.min(Math.max(mw, w), mxw);
8868 h = Math.min(Math.max(mh, h), mxh);
8876 h = Math.min(Math.max(mh, h), mxh);
8886 h = Math.min(Math.max(mh, h), mxh);
8894 if (pos == 'hdrag') {
8897 this.proxy.setBounds(x, y, w, h);
8899 this.resizeElement();
8903 this.fireEvent("resizing", this, x, y, w, h, e);
8907 handleOver : function(){
8909 this.el.addClass("x-resizable-over");
8914 handleOut : function(){
8916 this.el.removeClass("x-resizable-over");
8921 * Returns the element this component is bound to.
8922 * @return {Roo.Element}
8929 * Returns the resizeChild element (or null).
8930 * @return {Roo.Element}
8932 getResizeChild : function(){
8933 return this.resizeChild;
8935 groupHandler : function()
8940 * Destroys this resizable. If the element was wrapped and
8941 * removeEl is not true then the element remains.
8942 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8944 destroy : function(removeEl){
8945 this.proxy.remove();
8947 this.overlay.removeAllListeners();
8948 this.overlay.remove();
8950 var ps = Roo.Resizable.positions;
8952 if(typeof ps[k] != "function" && this[ps[k]]){
8953 var h = this[ps[k]];
8954 h.el.removeAllListeners();
8966 // hash to map config positions to true positions
8967 Roo.Resizable.positions = {
8968 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8973 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8975 // only initialize the template if resizable is used
8976 var tpl = Roo.DomHelper.createTemplate(
8977 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8980 Roo.Resizable.Handle.prototype.tpl = tpl;
8982 this.position = pos;
8984 // show north drag fro topdra
8985 var handlepos = pos == 'hdrag' ? 'north' : pos;
8987 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8988 if (pos == 'hdrag') {
8989 this.el.setStyle('cursor', 'pointer');
8991 this.el.unselectable();
8993 this.el.setOpacity(0);
8995 this.el.on("mousedown", this.onMouseDown, this);
8996 if(!disableTrackOver){
8997 this.el.on("mouseover", this.onMouseOver, this);
8998 this.el.on("mouseout", this.onMouseOut, this);
9003 Roo.Resizable.Handle.prototype = {
9004 afterResize : function(rz){
9009 onMouseDown : function(e){
9010 this.rz.onMouseDown(this, e);
9013 onMouseOver : function(e){
9014 this.rz.handleOver(this, e);
9017 onMouseOut : function(e){
9018 this.rz.handleOut(this, e);
9022 * Ext JS Library 1.1.1
9023 * Copyright(c) 2006-2007, Ext JS, LLC.
9025 * Originally Released Under LGPL - original licence link has changed is not relivant.
9028 * <script type="text/javascript">
9033 * @extends Roo.Component
9034 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9036 * Create a new Editor
9037 * @param {Roo.form.Field} field The Field object (or descendant)
9038 * @param {Object} config The config object
9040 Roo.Editor = function(field, config){
9041 Roo.Editor.superclass.constructor.call(this, config);
9045 * @event beforestartedit
9046 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
9047 * false from the handler of this event.
9048 * @param {Editor} this
9049 * @param {Roo.Element} boundEl The underlying element bound to this editor
9050 * @param {Mixed} value The field value being set
9052 "beforestartedit" : true,
9055 * Fires when this editor is displayed
9056 * @param {Roo.Element} boundEl The underlying element bound to this editor
9057 * @param {Mixed} value The starting field value
9061 * @event beforecomplete
9062 * Fires after a change has been made to the field, but before the change is reflected in the underlying
9063 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
9064 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9065 * event will not fire since no edit actually occurred.
9066 * @param {Editor} this
9067 * @param {Mixed} value The current field value
9068 * @param {Mixed} startValue The original field value
9070 "beforecomplete" : true,
9073 * Fires after editing is complete and any changed value has been written to the underlying field.
9074 * @param {Editor} this
9075 * @param {Mixed} value The current field value
9076 * @param {Mixed} startValue The original field value
9081 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9082 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9083 * @param {Roo.form.Field} this
9084 * @param {Roo.EventObject} e The event object
9090 Roo.extend(Roo.Editor, Roo.Component, {
9092 * @cfg {Boolean/String} autosize
9093 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9094 * or "height" to adopt the height only (defaults to false)
9097 * @cfg {Boolean} revertInvalid
9098 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9099 * validation fails (defaults to true)
9102 * @cfg {Boolean} ignoreNoChange
9103 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9104 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
9105 * will never be ignored.
9108 * @cfg {Boolean} hideEl
9109 * False to keep the bound element visible while the editor is displayed (defaults to true)
9112 * @cfg {Mixed} value
9113 * The data value of the underlying field (defaults to "")
9117 * @cfg {String} alignment
9118 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9122 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9123 * for bottom-right shadow (defaults to "frame")
9127 * @cfg {Boolean} constrain True to constrain the editor to the viewport
9131 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9133 completeOnEnter : false,
9135 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9137 cancelOnEsc : false,
9139 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9144 onRender : function(ct, position){
9145 this.el = new Roo.Layer({
9146 shadow: this.shadow,
9152 constrain: this.constrain
9154 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9155 if(this.field.msgTarget != 'title'){
9156 this.field.msgTarget = 'qtip';
9158 this.field.render(this.el);
9160 this.field.el.dom.setAttribute('autocomplete', 'off');
9162 this.field.on("specialkey", this.onSpecialKey, this);
9163 if(this.swallowKeys){
9164 this.field.el.swallowEvent(['keydown','keypress']);
9167 this.field.on("blur", this.onBlur, this);
9168 if(this.field.grow){
9169 this.field.on("autosize", this.el.sync, this.el, {delay:1});
9173 onSpecialKey : function(field, e)
9175 //Roo.log('editor onSpecialKey');
9176 if(this.completeOnEnter && e.getKey() == e.ENTER){
9178 this.completeEdit();
9181 // do not fire special key otherwise it might hide close the editor...
9182 if(e.getKey() == e.ENTER){
9185 if(this.cancelOnEsc && e.getKey() == e.ESC){
9189 this.fireEvent('specialkey', field, e);
9194 * Starts the editing process and shows the editor.
9195 * @param {String/HTMLElement/Element} el The element to edit
9196 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9197 * to the innerHTML of el.
9199 startEdit : function(el, value){
9201 this.completeEdit();
9203 this.boundEl = Roo.get(el);
9204 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9206 this.render(this.parentEl || document.body);
9208 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9211 this.startValue = v;
9212 this.field.setValue(v);
9214 var sz = this.boundEl.getSize();
9215 switch(this.autoSize){
9217 this.setSize(sz.width, "");
9220 this.setSize("", sz.height);
9223 this.setSize(sz.width, sz.height);
9226 this.el.alignTo(this.boundEl, this.alignment);
9227 this.editing = true;
9229 Roo.QuickTips.disable();
9235 * Sets the height and width of this editor.
9236 * @param {Number} width The new width
9237 * @param {Number} height The new height
9239 setSize : function(w, h){
9240 this.field.setSize(w, h);
9247 * Realigns the editor to the bound field based on the current alignment config value.
9249 realign : function(){
9250 this.el.alignTo(this.boundEl, this.alignment);
9254 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9255 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9257 completeEdit : function(remainVisible){
9261 var v = this.getValue();
9262 if(this.revertInvalid !== false && !this.field.isValid()){
9263 v = this.startValue;
9264 this.cancelEdit(true);
9266 if(String(v) === String(this.startValue) && this.ignoreNoChange){
9267 this.editing = false;
9271 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9272 this.editing = false;
9273 if(this.updateEl && this.boundEl){
9274 this.boundEl.update(v);
9276 if(remainVisible !== true){
9279 this.fireEvent("complete", this, v, this.startValue);
9284 onShow : function(){
9286 if(this.hideEl !== false){
9287 this.boundEl.hide();
9290 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9291 this.fixIEFocus = true;
9292 this.deferredFocus.defer(50, this);
9296 this.fireEvent("startedit", this.boundEl, this.startValue);
9299 deferredFocus : function(){
9306 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
9307 * reverted to the original starting value.
9308 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9309 * cancel (defaults to false)
9311 cancelEdit : function(remainVisible){
9313 this.setValue(this.startValue);
9314 if(remainVisible !== true){
9321 onBlur : function(){
9322 if(this.allowBlur !== true && this.editing){
9323 this.completeEdit();
9328 onHide : function(){
9330 this.completeEdit();
9334 if(this.field.collapse){
9335 this.field.collapse();
9338 if(this.hideEl !== false){
9339 this.boundEl.show();
9342 Roo.QuickTips.enable();
9347 * Sets the data value of the editor
9348 * @param {Mixed} value Any valid value supported by the underlying field
9350 setValue : function(v){
9351 this.field.setValue(v);
9355 * Gets the data value of the editor
9356 * @return {Mixed} The data value
9358 getValue : function(){
9359 return this.field.getValue();
9363 * Ext JS Library 1.1.1
9364 * Copyright(c) 2006-2007, Ext JS, LLC.
9366 * Originally Released Under LGPL - original licence link has changed is not relivant.
9369 * <script type="text/javascript">
9373 * @class Roo.BasicDialog
9374 * @extends Roo.util.Observable
9375 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9377 var dlg = new Roo.BasicDialog("my-dlg", {
9386 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9387 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9388 dlg.addButton('Cancel', dlg.hide, dlg);
9391 <b>A Dialog should always be a direct child of the body element.</b>
9392 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9393 * @cfg {String} title Default text to display in the title bar (defaults to null)
9394 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9395 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9396 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9397 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9398 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9399 * (defaults to null with no animation)
9400 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9401 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9402 * property for valid values (defaults to 'all')
9403 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9404 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9405 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9406 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9407 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9408 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9409 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9410 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9411 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9412 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9413 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9414 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9415 * draggable = true (defaults to false)
9416 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9417 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9418 * shadow (defaults to false)
9419 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9420 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9421 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9422 * @cfg {Array} buttons Array of buttons
9423 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9425 * Create a new BasicDialog.
9426 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9427 * @param {Object} config Configuration options
9429 Roo.BasicDialog = function(el, config){
9430 this.el = Roo.get(el);
9431 var dh = Roo.DomHelper;
9432 if(!this.el && config && config.autoCreate){
9433 if(typeof config.autoCreate == "object"){
9434 if(!config.autoCreate.id){
9435 config.autoCreate.id = el;
9437 this.el = dh.append(document.body,
9438 config.autoCreate, true);
9440 this.el = dh.append(document.body,
9441 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9445 el.setDisplayed(true);
9446 el.hide = this.hideAction;
9448 el.addClass("x-dlg");
9450 Roo.apply(this, config);
9452 this.proxy = el.createProxy("x-dlg-proxy");
9453 this.proxy.hide = this.hideAction;
9454 this.proxy.setOpacity(.5);
9458 el.setWidth(config.width);
9461 el.setHeight(config.height);
9463 this.size = el.getSize();
9464 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9465 this.xy = [config.x,config.y];
9467 this.xy = el.getCenterXY(true);
9469 /** The header element @type Roo.Element */
9470 this.header = el.child("> .x-dlg-hd");
9471 /** The body element @type Roo.Element */
9472 this.body = el.child("> .x-dlg-bd");
9473 /** The footer element @type Roo.Element */
9474 this.footer = el.child("> .x-dlg-ft");
9477 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9480 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9483 this.header.unselectable();
9485 this.header.update(this.title);
9487 // this element allows the dialog to be focused for keyboard event
9488 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9489 this.focusEl.swallowEvent("click", true);
9491 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9493 // wrap the body and footer for special rendering
9494 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9496 this.bwrap.dom.appendChild(this.footer.dom);
9499 this.bg = this.el.createChild({
9500 tag: "div", cls:"x-dlg-bg",
9501 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9503 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9506 if(this.autoScroll !== false && !this.autoTabs){
9507 this.body.setStyle("overflow", "auto");
9510 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9512 if(this.closable !== false){
9513 this.el.addClass("x-dlg-closable");
9514 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9515 this.close.on("click", this.closeClick, this);
9516 this.close.addClassOnOver("x-dlg-close-over");
9518 if(this.collapsible !== false){
9519 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9520 this.collapseBtn.on("click", this.collapseClick, this);
9521 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9522 this.header.on("dblclick", this.collapseClick, this);
9524 if(this.resizable !== false){
9525 this.el.addClass("x-dlg-resizable");
9526 this.resizer = new Roo.Resizable(el, {
9527 minWidth: this.minWidth || 80,
9528 minHeight:this.minHeight || 80,
9529 handles: this.resizeHandles || "all",
9532 this.resizer.on("beforeresize", this.beforeResize, this);
9533 this.resizer.on("resize", this.onResize, this);
9535 if(this.draggable !== false){
9536 el.addClass("x-dlg-draggable");
9537 if (!this.proxyDrag) {
9538 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9541 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9543 dd.setHandleElId(this.header.id);
9544 dd.endDrag = this.endMove.createDelegate(this);
9545 dd.startDrag = this.startMove.createDelegate(this);
9546 dd.onDrag = this.onDrag.createDelegate(this);
9551 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9552 this.mask.enableDisplayMode("block");
9554 this.el.addClass("x-dlg-modal");
9557 this.shadow = new Roo.Shadow({
9558 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9559 offset : this.shadowOffset
9562 this.shadowOffset = 0;
9564 if(Roo.useShims && this.shim !== false){
9565 this.shim = this.el.createShim();
9566 this.shim.hide = this.hideAction;
9575 var bts= this.buttons;
9577 Roo.each(bts, function(b) {
9586 * Fires when a key is pressed
9587 * @param {Roo.BasicDialog} this
9588 * @param {Roo.EventObject} e
9593 * Fires when this dialog is moved by the user.
9594 * @param {Roo.BasicDialog} this
9595 * @param {Number} x The new page X
9596 * @param {Number} y The new page Y
9601 * Fires when this dialog is resized by the user.
9602 * @param {Roo.BasicDialog} this
9603 * @param {Number} width The new width
9604 * @param {Number} height The new height
9609 * Fires before this dialog is hidden.
9610 * @param {Roo.BasicDialog} this
9612 "beforehide" : true,
9615 * Fires when this dialog is hidden.
9616 * @param {Roo.BasicDialog} this
9621 * Fires before this dialog is shown.
9622 * @param {Roo.BasicDialog} this
9624 "beforeshow" : true,
9627 * Fires when this dialog is shown.
9628 * @param {Roo.BasicDialog} this
9632 el.on("keydown", this.onKeyDown, this);
9633 el.on("mousedown", this.toFront, this);
9634 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9636 Roo.DialogManager.register(this);
9637 Roo.BasicDialog.superclass.constructor.call(this);
9640 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9641 shadowOffset: Roo.isIE ? 6 : 5,
9645 defaultButton: null,
9646 buttonAlign: "right",
9651 * Sets the dialog title text
9652 * @param {String} text The title text to display
9653 * @return {Roo.BasicDialog} this
9655 setTitle : function(text){
9656 this.header.update(text);
9661 closeClick : function(){
9666 collapseClick : function(){
9667 this[this.collapsed ? "expand" : "collapse"]();
9671 * Collapses the dialog to its minimized state (only the title bar is visible).
9672 * Equivalent to the user clicking the collapse dialog button.
9674 collapse : function(){
9675 if(!this.collapsed){
9676 this.collapsed = true;
9677 this.el.addClass("x-dlg-collapsed");
9678 this.restoreHeight = this.el.getHeight();
9679 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9684 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9685 * clicking the expand dialog button.
9687 expand : function(){
9689 this.collapsed = false;
9690 this.el.removeClass("x-dlg-collapsed");
9691 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9696 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9697 * @return {Roo.TabPanel} The tabs component
9699 initTabs : function(){
9700 var tabs = this.getTabs();
9701 while(tabs.getTab(0)){
9704 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9706 tabs.addTab(Roo.id(dom), dom.title);
9714 beforeResize : function(){
9715 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9719 onResize : function(){
9721 this.syncBodyHeight();
9722 this.adjustAssets();
9724 this.fireEvent("resize", this, this.size.width, this.size.height);
9728 onKeyDown : function(e){
9729 if(this.isVisible()){
9730 this.fireEvent("keydown", this, e);
9735 * Resizes the dialog.
9736 * @param {Number} width
9737 * @param {Number} height
9738 * @return {Roo.BasicDialog} this
9740 resizeTo : function(width, height){
9741 this.el.setSize(width, height);
9742 this.size = {width: width, height: height};
9743 this.syncBodyHeight();
9744 if(this.fixedcenter){
9747 if(this.isVisible()){
9749 this.adjustAssets();
9751 this.fireEvent("resize", this, width, height);
9757 * Resizes the dialog to fit the specified content size.
9758 * @param {Number} width
9759 * @param {Number} height
9760 * @return {Roo.BasicDialog} this
9762 setContentSize : function(w, h){
9763 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9764 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9765 //if(!this.el.isBorderBox()){
9766 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9767 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9770 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9771 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9773 this.resizeTo(w, h);
9778 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9779 * executed in response to a particular key being pressed while the dialog is active.
9780 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9781 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9782 * @param {Function} fn The function to call
9783 * @param {Object} scope (optional) The scope of the function
9784 * @return {Roo.BasicDialog} this
9786 addKeyListener : function(key, fn, scope){
9787 var keyCode, shift, ctrl, alt;
9788 if(typeof key == "object" && !(key instanceof Array)){
9789 keyCode = key["key"];
9790 shift = key["shift"];
9796 var handler = function(dlg, e){
9797 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9799 if(keyCode instanceof Array){
9800 for(var i = 0, len = keyCode.length; i < len; i++){
9801 if(keyCode[i] == k){
9802 fn.call(scope || window, dlg, k, e);
9808 fn.call(scope || window, dlg, k, e);
9813 this.on("keydown", handler);
9818 * Returns the TabPanel component (creates it if it doesn't exist).
9819 * Note: If you wish to simply check for the existence of tabs without creating them,
9820 * check for a null 'tabs' property.
9821 * @return {Roo.TabPanel} The tabs component
9823 getTabs : function(){
9825 this.el.addClass("x-dlg-auto-tabs");
9826 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9827 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9833 * Adds a button to the footer section of the dialog.
9834 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9835 * object or a valid Roo.DomHelper element config
9836 * @param {Function} handler The function called when the button is clicked
9837 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9838 * @return {Roo.Button} The new button
9840 addButton : function(config, handler, scope){
9841 var dh = Roo.DomHelper;
9843 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9845 if(!this.btnContainer){
9846 var tb = this.footer.createChild({
9848 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9849 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9851 this.btnContainer = tb.firstChild.firstChild.firstChild;
9856 minWidth: this.minButtonWidth,
9859 if(typeof config == "string"){
9860 bconfig.text = config;
9863 bconfig.dhconfig = config;
9865 Roo.apply(bconfig, config);
9869 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9870 bconfig.position = Math.max(0, bconfig.position);
9871 fc = this.btnContainer.childNodes[bconfig.position];
9874 var btn = new Roo.Button(
9876 this.btnContainer.insertBefore(document.createElement("td"),fc)
9877 : this.btnContainer.appendChild(document.createElement("td")),
9878 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9881 this.syncBodyHeight();
9884 * Array of all the buttons that have been added to this dialog via addButton
9889 this.buttons.push(btn);
9894 * Sets the default button to be focused when the dialog is displayed.
9895 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9896 * @return {Roo.BasicDialog} this
9898 setDefaultButton : function(btn){
9899 this.defaultButton = btn;
9904 getHeaderFooterHeight : function(safe){
9907 height += this.header.getHeight();
9910 var fm = this.footer.getMargins();
9911 height += (this.footer.getHeight()+fm.top+fm.bottom);
9913 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9914 height += this.centerBg.getPadding("tb");
9919 syncBodyHeight : function()
9921 var bd = this.body, // the text
9922 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9924 var height = this.size.height - this.getHeaderFooterHeight(false);
9925 bd.setHeight(height-bd.getMargins("tb"));
9926 var hh = this.header.getHeight();
9927 var h = this.size.height-hh;
9930 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9931 bw.setHeight(h-cb.getPadding("tb"));
9933 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9934 bd.setWidth(bw.getWidth(true));
9936 this.tabs.syncHeight();
9938 this.tabs.el.repaint();
9944 * Restores the previous state of the dialog if Roo.state is configured.
9945 * @return {Roo.BasicDialog} this
9947 restoreState : function(){
9948 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9949 if(box && box.width){
9950 this.xy = [box.x, box.y];
9951 this.resizeTo(box.width, box.height);
9957 beforeShow : function(){
9959 if(this.fixedcenter){
9960 this.xy = this.el.getCenterXY(true);
9963 Roo.get(document.body).addClass("x-body-masked");
9964 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9971 animShow : function(){
9972 var b = Roo.get(this.animateTarget).getBox();
9973 this.proxy.setSize(b.width, b.height);
9974 this.proxy.setLocation(b.x, b.y);
9976 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9977 true, .35, this.showEl.createDelegate(this));
9982 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9983 * @return {Roo.BasicDialog} this
9985 show : function(animateTarget){
9986 if (this.fireEvent("beforeshow", this) === false){
9989 if(this.syncHeightBeforeShow){
9990 this.syncBodyHeight();
9991 }else if(this.firstShow){
9992 this.firstShow = false;
9993 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9995 this.animateTarget = animateTarget || this.animateTarget;
9996 if(!this.el.isVisible()){
9998 if(this.animateTarget && Roo.get(this.animateTarget)){
10008 showEl : function(){
10010 this.el.setXY(this.xy);
10012 this.adjustAssets(true);
10015 // IE peekaboo bug - fix found by Dave Fenwick
10019 this.fireEvent("show", this);
10023 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
10024 * dialog itself will receive focus.
10026 focus : function(){
10027 if(this.defaultButton){
10028 this.defaultButton.focus();
10030 this.focusEl.focus();
10035 constrainXY : function(){
10036 if(this.constraintoviewport !== false){
10037 if(!this.viewSize){
10038 if(this.container){
10039 var s = this.container.getSize();
10040 this.viewSize = [s.width, s.height];
10042 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10045 var s = Roo.get(this.container||document).getScroll();
10047 var x = this.xy[0], y = this.xy[1];
10048 var w = this.size.width, h = this.size.height;
10049 var vw = this.viewSize[0], vh = this.viewSize[1];
10050 // only move it if it needs it
10052 // first validate right/bottom
10053 if(x + w > vw+s.left){
10057 if(y + h > vh+s.top){
10061 // then make sure top/left isn't negative
10073 if(this.isVisible()){
10074 this.el.setLocation(x, y);
10075 this.adjustAssets();
10082 onDrag : function(){
10083 if(!this.proxyDrag){
10084 this.xy = this.el.getXY();
10085 this.adjustAssets();
10090 adjustAssets : function(doShow){
10091 var x = this.xy[0], y = this.xy[1];
10092 var w = this.size.width, h = this.size.height;
10093 if(doShow === true){
10095 this.shadow.show(this.el);
10101 if(this.shadow && this.shadow.isVisible()){
10102 this.shadow.show(this.el);
10104 if(this.shim && this.shim.isVisible()){
10105 this.shim.setBounds(x, y, w, h);
10110 adjustViewport : function(w, h){
10112 w = Roo.lib.Dom.getViewWidth();
10113 h = Roo.lib.Dom.getViewHeight();
10116 this.viewSize = [w, h];
10117 if(this.modal && this.mask.isVisible()){
10118 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10119 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10121 if(this.isVisible()){
10122 this.constrainXY();
10127 * Destroys this dialog and all its supporting elements (including any tabs, shim,
10128 * shadow, proxy, mask, etc.) Also removes all event listeners.
10129 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10131 destroy : function(removeEl){
10132 if(this.isVisible()){
10133 this.animateTarget = null;
10136 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10138 this.tabs.destroy(removeEl);
10151 for(var i = 0, len = this.buttons.length; i < len; i++){
10152 this.buttons[i].destroy();
10155 this.el.removeAllListeners();
10156 if(removeEl === true){
10157 this.el.update("");
10160 Roo.DialogManager.unregister(this);
10164 startMove : function(){
10165 if(this.proxyDrag){
10168 if(this.constraintoviewport !== false){
10169 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10174 endMove : function(){
10175 if(!this.proxyDrag){
10176 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10178 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10181 this.refreshSize();
10182 this.adjustAssets();
10184 this.fireEvent("move", this, this.xy[0], this.xy[1]);
10188 * Brings this dialog to the front of any other visible dialogs
10189 * @return {Roo.BasicDialog} this
10191 toFront : function(){
10192 Roo.DialogManager.bringToFront(this);
10197 * Sends this dialog to the back (under) of any other visible dialogs
10198 * @return {Roo.BasicDialog} this
10200 toBack : function(){
10201 Roo.DialogManager.sendToBack(this);
10206 * Centers this dialog in the viewport
10207 * @return {Roo.BasicDialog} this
10209 center : function(){
10210 var xy = this.el.getCenterXY(true);
10211 this.moveTo(xy[0], xy[1]);
10216 * Moves the dialog's top-left corner to the specified point
10217 * @param {Number} x
10218 * @param {Number} y
10219 * @return {Roo.BasicDialog} this
10221 moveTo : function(x, y){
10223 if(this.isVisible()){
10224 this.el.setXY(this.xy);
10225 this.adjustAssets();
10231 * Aligns the dialog to the specified element
10232 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10233 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10234 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10235 * @return {Roo.BasicDialog} this
10237 alignTo : function(element, position, offsets){
10238 this.xy = this.el.getAlignToXY(element, position, offsets);
10239 if(this.isVisible()){
10240 this.el.setXY(this.xy);
10241 this.adjustAssets();
10247 * Anchors an element to another element and realigns it when the window is resized.
10248 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10249 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10250 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10251 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10252 * is a number, it is used as the buffer delay (defaults to 50ms).
10253 * @return {Roo.BasicDialog} this
10255 anchorTo : function(el, alignment, offsets, monitorScroll){
10256 var action = function(){
10257 this.alignTo(el, alignment, offsets);
10259 Roo.EventManager.onWindowResize(action, this);
10260 var tm = typeof monitorScroll;
10261 if(tm != 'undefined'){
10262 Roo.EventManager.on(window, 'scroll', action, this,
10263 {buffer: tm == 'number' ? monitorScroll : 50});
10270 * Returns true if the dialog is visible
10271 * @return {Boolean}
10273 isVisible : function(){
10274 return this.el.isVisible();
10278 animHide : function(callback){
10279 var b = Roo.get(this.animateTarget).getBox();
10281 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10283 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10284 this.hideEl.createDelegate(this, [callback]));
10288 * Hides the dialog.
10289 * @param {Function} callback (optional) Function to call when the dialog is hidden
10290 * @return {Roo.BasicDialog} this
10292 hide : function(callback){
10293 if (this.fireEvent("beforehide", this) === false){
10297 this.shadow.hide();
10302 // sometimes animateTarget seems to get set.. causing problems...
10303 // this just double checks..
10304 if(this.animateTarget && Roo.get(this.animateTarget)) {
10305 this.animHide(callback);
10308 this.hideEl(callback);
10314 hideEl : function(callback){
10318 Roo.get(document.body).removeClass("x-body-masked");
10320 this.fireEvent("hide", this);
10321 if(typeof callback == "function"){
10327 hideAction : function(){
10328 this.setLeft("-10000px");
10329 this.setTop("-10000px");
10330 this.setStyle("visibility", "hidden");
10334 refreshSize : function(){
10335 this.size = this.el.getSize();
10336 this.xy = this.el.getXY();
10337 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10341 // z-index is managed by the DialogManager and may be overwritten at any time
10342 setZIndex : function(index){
10344 this.mask.setStyle("z-index", index);
10347 this.shim.setStyle("z-index", ++index);
10350 this.shadow.setZIndex(++index);
10352 this.el.setStyle("z-index", ++index);
10354 this.proxy.setStyle("z-index", ++index);
10357 this.resizer.proxy.setStyle("z-index", ++index);
10360 this.lastZIndex = index;
10364 * Returns the element for this dialog
10365 * @return {Roo.Element} The underlying dialog Element
10367 getEl : function(){
10373 * @class Roo.DialogManager
10374 * Provides global access to BasicDialogs that have been created and
10375 * support for z-indexing (layering) multiple open dialogs.
10377 Roo.DialogManager = function(){
10379 var accessList = [];
10383 var sortDialogs = function(d1, d2){
10384 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10388 var orderDialogs = function(){
10389 accessList.sort(sortDialogs);
10390 var seed = Roo.DialogManager.zseed;
10391 for(var i = 0, len = accessList.length; i < len; i++){
10392 var dlg = accessList[i];
10394 dlg.setZIndex(seed + (i*10));
10401 * The starting z-index for BasicDialogs (defaults to 9000)
10402 * @type Number The z-index value
10407 register : function(dlg){
10408 list[dlg.id] = dlg;
10409 accessList.push(dlg);
10413 unregister : function(dlg){
10414 delete list[dlg.id];
10417 if(!accessList.indexOf){
10418 for( i = 0, len = accessList.length; i < len; i++){
10419 if(accessList[i] == dlg){
10420 accessList.splice(i, 1);
10425 i = accessList.indexOf(dlg);
10427 accessList.splice(i, 1);
10433 * Gets a registered dialog by id
10434 * @param {String/Object} id The id of the dialog or a dialog
10435 * @return {Roo.BasicDialog} this
10437 get : function(id){
10438 return typeof id == "object" ? id : list[id];
10442 * Brings the specified dialog to the front
10443 * @param {String/Object} dlg The id of the dialog or a dialog
10444 * @return {Roo.BasicDialog} this
10446 bringToFront : function(dlg){
10447 dlg = this.get(dlg);
10450 dlg._lastAccess = new Date().getTime();
10457 * Sends the specified dialog to the back
10458 * @param {String/Object} dlg The id of the dialog or a dialog
10459 * @return {Roo.BasicDialog} this
10461 sendToBack : function(dlg){
10462 dlg = this.get(dlg);
10463 dlg._lastAccess = -(new Date().getTime());
10469 * Hides all dialogs
10471 hideAll : function(){
10472 for(var id in list){
10473 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10482 * @class Roo.LayoutDialog
10483 * @extends Roo.BasicDialog
10484 * Dialog which provides adjustments for working with a layout in a Dialog.
10485 * Add your necessary layout config options to the dialog's config.<br>
10486 * Example usage (including a nested layout):
10489 dialog = new Roo.LayoutDialog("download-dlg", {
10498 // layout config merges with the dialog config
10500 tabPosition: "top",
10501 alwaysShowTabs: true
10504 dialog.addKeyListener(27, dialog.hide, dialog);
10505 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10506 dialog.addButton("Build It!", this.getDownload, this);
10508 // we can even add nested layouts
10509 var innerLayout = new Roo.BorderLayout("dl-inner", {
10519 innerLayout.beginUpdate();
10520 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10521 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10522 innerLayout.endUpdate(true);
10524 var layout = dialog.getLayout();
10525 layout.beginUpdate();
10526 layout.add("center", new Roo.ContentPanel("standard-panel",
10527 {title: "Download the Source", fitToFrame:true}));
10528 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10529 {title: "Build your own roo.js"}));
10530 layout.getRegion("center").showPanel(sp);
10531 layout.endUpdate();
10535 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10536 * @param {Object} config configuration options
10538 Roo.LayoutDialog = function(el, cfg){
10541 if (typeof(cfg) == 'undefined') {
10542 config = Roo.apply({}, el);
10543 // not sure why we use documentElement here.. - it should always be body.
10544 // IE7 borks horribly if we use documentElement.
10545 // webkit also does not like documentElement - it creates a body element...
10546 el = Roo.get( document.body || document.documentElement ).createChild();
10547 //config.autoCreate = true;
10551 config.autoTabs = false;
10552 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10553 this.body.setStyle({overflow:"hidden", position:"relative"});
10554 this.layout = new Roo.BorderLayout(this.body.dom, config);
10555 this.layout.monitorWindowResize = false;
10556 this.el.addClass("x-dlg-auto-layout");
10557 // fix case when center region overwrites center function
10558 this.center = Roo.BasicDialog.prototype.center;
10559 this.on("show", this.layout.layout, this.layout, true);
10560 if (config.items) {
10561 var xitems = config.items;
10562 delete config.items;
10563 Roo.each(xitems, this.addxtype, this);
10568 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10570 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10573 endUpdate : function(){
10574 this.layout.endUpdate();
10578 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10581 beginUpdate : function(){
10582 this.layout.beginUpdate();
10586 * Get the BorderLayout for this dialog
10587 * @return {Roo.BorderLayout}
10589 getLayout : function(){
10590 return this.layout;
10593 showEl : function(){
10594 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10596 this.layout.layout();
10601 // Use the syncHeightBeforeShow config option to control this automatically
10602 syncBodyHeight : function(){
10603 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10604 if(this.layout){this.layout.layout();}
10608 * Add an xtype element (actually adds to the layout.)
10609 * @return {Object} xdata xtype object data.
10612 addxtype : function(c) {
10613 return this.layout.addxtype(c);
10617 * Ext JS Library 1.1.1
10618 * Copyright(c) 2006-2007, Ext JS, LLC.
10620 * Originally Released Under LGPL - original licence link has changed is not relivant.
10623 * <script type="text/javascript">
10627 * @class Roo.MessageBox
10628 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10632 Roo.Msg.alert('Status', 'Changes saved successfully.');
10634 // Prompt for user data:
10635 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10637 // process text value...
10641 // Show a dialog using config options:
10643 title:'Save Changes?',
10644 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10645 buttons: Roo.Msg.YESNOCANCEL,
10652 Roo.MessageBox = function(){
10653 var dlg, opt, mask, waitTimer;
10654 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10655 var buttons, activeTextEl, bwidth;
10658 var handleButton = function(button){
10660 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10664 var handleHide = function(){
10665 if(opt && opt.cls){
10666 dlg.el.removeClass(opt.cls);
10669 Roo.TaskMgr.stop(waitTimer);
10675 var updateButtons = function(b){
10678 buttons["ok"].hide();
10679 buttons["cancel"].hide();
10680 buttons["yes"].hide();
10681 buttons["no"].hide();
10682 dlg.footer.dom.style.display = 'none';
10685 dlg.footer.dom.style.display = '';
10686 for(var k in buttons){
10687 if(typeof buttons[k] != "function"){
10690 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10691 width += buttons[k].el.getWidth()+15;
10701 var handleEsc = function(d, k, e){
10702 if(opt && opt.closable !== false){
10712 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10713 * @return {Roo.BasicDialog} The BasicDialog element
10715 getDialog : function(){
10717 dlg = new Roo.BasicDialog("x-msg-box", {
10722 constraintoviewport:false,
10724 collapsible : false,
10727 width:400, height:100,
10728 buttonAlign:"center",
10729 closeClick : function(){
10730 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10731 handleButton("no");
10733 handleButton("cancel");
10737 dlg.on("hide", handleHide);
10739 dlg.addKeyListener(27, handleEsc);
10741 var bt = this.buttonText;
10742 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10743 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10744 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10745 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10746 bodyEl = dlg.body.createChild({
10748 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
10750 msgEl = bodyEl.dom.firstChild;
10751 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10752 textboxEl.enableDisplayMode();
10753 textboxEl.addKeyListener([10,13], function(){
10754 if(dlg.isVisible() && opt && opt.buttons){
10755 if(opt.buttons.ok){
10756 handleButton("ok");
10757 }else if(opt.buttons.yes){
10758 handleButton("yes");
10762 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10763 textareaEl.enableDisplayMode();
10764 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10765 progressEl.enableDisplayMode();
10766 var pf = progressEl.dom.firstChild;
10768 pp = Roo.get(pf.firstChild);
10769 pp.setHeight(pf.offsetHeight);
10777 * Updates the message box body text
10778 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10779 * the XHTML-compliant non-breaking space character '&#160;')
10780 * @return {Roo.MessageBox} This message box
10782 updateText : function(text){
10783 if(!dlg.isVisible() && !opt.width){
10784 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10786 msgEl.innerHTML = text || ' ';
10788 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10789 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10791 Math.min(opt.width || cw , this.maxWidth),
10792 Math.max(opt.minWidth || this.minWidth, bwidth)
10795 activeTextEl.setWidth(w);
10797 if(dlg.isVisible()){
10798 dlg.fixedcenter = false;
10800 // to big, make it scroll. = But as usual stupid IE does not support
10803 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10804 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10805 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10807 bodyEl.dom.style.height = '';
10808 bodyEl.dom.style.overflowY = '';
10811 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10813 bodyEl.dom.style.overflowX = '';
10816 dlg.setContentSize(w, bodyEl.getHeight());
10817 if(dlg.isVisible()){
10818 dlg.fixedcenter = true;
10824 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10825 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10826 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10827 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10828 * @return {Roo.MessageBox} This message box
10830 updateProgress : function(value, text){
10832 this.updateText(text);
10834 if (pp) { // weird bug on my firefox - for some reason this is not defined
10835 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10841 * Returns true if the message box is currently displayed
10842 * @return {Boolean} True if the message box is visible, else false
10844 isVisible : function(){
10845 return dlg && dlg.isVisible();
10849 * Hides the message box if it is displayed
10852 if(this.isVisible()){
10858 * Displays a new message box, or reinitializes an existing message box, based on the config options
10859 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10860 * The following config object properties are supported:
10862 Property Type Description
10863 ---------- --------------- ------------------------------------------------------------------------------------
10864 animEl String/Element An id or Element from which the message box should animate as it opens and
10865 closes (defaults to undefined)
10866 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10867 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10868 closable Boolean False to hide the top-right close button (defaults to true). Note that
10869 progress and wait dialogs will ignore this property and always hide the
10870 close button as they can only be closed programmatically.
10871 cls String A custom CSS class to apply to the message box element
10872 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10873 displayed (defaults to 75)
10874 fn Function A callback function to execute after closing the dialog. The arguments to the
10875 function will be btn (the name of the button that was clicked, if applicable,
10876 e.g. "ok"), and text (the value of the active text field, if applicable).
10877 Progress and wait dialogs will ignore this option since they do not respond to
10878 user actions and can only be closed programmatically, so any required function
10879 should be called by the same code after it closes the dialog.
10880 icon String A CSS class that provides a background image to be used as an icon for
10881 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10882 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10883 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10884 modal Boolean False to allow user interaction with the page while the message box is
10885 displayed (defaults to true)
10886 msg String A string that will replace the existing message box body text (defaults
10887 to the XHTML-compliant non-breaking space character ' ')
10888 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10889 progress Boolean True to display a progress bar (defaults to false)
10890 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10891 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10892 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10893 title String The title text
10894 value String The string value to set into the active textbox element if displayed
10895 wait Boolean True to display a progress bar (defaults to false)
10896 width Number The width of the dialog in pixels
10903 msg: 'Please enter your address:',
10905 buttons: Roo.MessageBox.OKCANCEL,
10908 animEl: 'addAddressBtn'
10911 * @param {Object} config Configuration options
10912 * @return {Roo.MessageBox} This message box
10914 show : function(options)
10917 // this causes nightmares if you show one dialog after another
10918 // especially on callbacks..
10920 if(this.isVisible()){
10923 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10924 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10925 Roo.log("New Dialog Message:" + options.msg )
10926 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10927 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10930 var d = this.getDialog();
10932 d.setTitle(opt.title || " ");
10933 d.close.setDisplayed(opt.closable !== false);
10934 activeTextEl = textboxEl;
10935 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10940 textareaEl.setHeight(typeof opt.multiline == "number" ?
10941 opt.multiline : this.defaultTextHeight);
10942 activeTextEl = textareaEl;
10951 progressEl.setDisplayed(opt.progress === true);
10952 this.updateProgress(0);
10953 activeTextEl.dom.value = opt.value || "";
10955 dlg.setDefaultButton(activeTextEl);
10957 var bs = opt.buttons;
10960 db = buttons["ok"];
10961 }else if(bs && bs.yes){
10962 db = buttons["yes"];
10964 dlg.setDefaultButton(db);
10966 bwidth = updateButtons(opt.buttons);
10967 this.updateText(opt.msg);
10969 d.el.addClass(opt.cls);
10971 d.proxyDrag = opt.proxyDrag === true;
10972 d.modal = opt.modal !== false;
10973 d.mask = opt.modal !== false ? mask : false;
10974 if(!d.isVisible()){
10975 // force it to the end of the z-index stack so it gets a cursor in FF
10976 document.body.appendChild(dlg.el.dom);
10977 d.animateTarget = null;
10978 d.show(options.animEl);
10984 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
10985 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10986 * and closing the message box when the process is complete.
10987 * @param {String} title The title bar text
10988 * @param {String} msg The message box body text
10989 * @return {Roo.MessageBox} This message box
10991 progress : function(title, msg){
10998 minWidth: this.minProgressWidth,
11005 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11006 * If a callback function is passed it will be called after the user clicks the button, and the
11007 * id of the button that was clicked will be passed as the only parameter to the callback
11008 * (could also be the top-right close button).
11009 * @param {String} title The title bar text
11010 * @param {String} msg The message box body text
11011 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11012 * @param {Object} scope (optional) The scope of the callback function
11013 * @return {Roo.MessageBox} This message box
11015 alert : function(title, msg, fn, scope){
11028 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
11029 * interaction while waiting for a long-running process to complete that does not have defined intervals.
11030 * You are responsible for closing the message box when the process is complete.
11031 * @param {String} msg The message box body text
11032 * @param {String} title (optional) The title bar text
11033 * @return {Roo.MessageBox} This message box
11035 wait : function(msg, title){
11046 waitTimer = Roo.TaskMgr.start({
11048 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11056 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11057 * If a callback function is passed it will be called after the user clicks either button, and the id of the
11058 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11059 * @param {String} title The title bar text
11060 * @param {String} msg The message box body text
11061 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11062 * @param {Object} scope (optional) The scope of the callback function
11063 * @return {Roo.MessageBox} This message box
11065 confirm : function(title, msg, fn, scope){
11069 buttons: this.YESNO,
11078 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11079 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
11080 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11081 * (could also be the top-right close button) and the text that was entered will be passed as the two
11082 * parameters to the callback.
11083 * @param {String} title The title bar text
11084 * @param {String} msg The message box body text
11085 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11086 * @param {Object} scope (optional) The scope of the callback function
11087 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11088 * property, or the height in pixels to create the textbox (defaults to false / single-line)
11089 * @return {Roo.MessageBox} This message box
11091 prompt : function(title, msg, fn, scope, multiline){
11095 buttons: this.OKCANCEL,
11100 multiline: multiline,
11107 * Button config that displays a single OK button
11112 * Button config that displays Yes and No buttons
11115 YESNO : {yes:true, no:true},
11117 * Button config that displays OK and Cancel buttons
11120 OKCANCEL : {ok:true, cancel:true},
11122 * Button config that displays Yes, No and Cancel buttons
11125 YESNOCANCEL : {yes:true, no:true, cancel:true},
11128 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11131 defaultTextHeight : 75,
11133 * The maximum width in pixels of the message box (defaults to 600)
11138 * The minimum width in pixels of the message box (defaults to 100)
11143 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
11144 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11147 minProgressWidth : 250,
11149 * An object containing the default button text strings that can be overriden for localized language support.
11150 * Supported properties are: ok, cancel, yes and no.
11151 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11164 * Shorthand for {@link Roo.MessageBox}
11166 Roo.Msg = Roo.MessageBox;/*
11168 * Ext JS Library 1.1.1
11169 * Copyright(c) 2006-2007, Ext JS, LLC.
11171 * Originally Released Under LGPL - original licence link has changed is not relivant.
11174 * <script type="text/javascript">
11177 * @class Roo.QuickTips
11178 * Provides attractive and customizable tooltips for any element.
11181 Roo.QuickTips = function(){
11182 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11183 var ce, bd, xy, dd;
11184 var visible = false, disabled = true, inited = false;
11185 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11187 var onOver = function(e){
11191 var t = e.getTarget();
11192 if(!t || t.nodeType !== 1 || t == document || t == document.body){
11195 if(ce && t == ce.el){
11196 clearTimeout(hideProc);
11199 if(t && tagEls[t.id]){
11200 tagEls[t.id].el = t;
11201 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11204 var ttp, et = Roo.fly(t);
11205 var ns = cfg.namespace;
11206 if(tm.interceptTitles && t.title){
11209 t.removeAttribute("title");
11210 e.preventDefault();
11212 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11215 showProc = show.defer(tm.showDelay, tm, [{
11218 width: et.getAttributeNS(ns, cfg.width),
11219 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11220 title: et.getAttributeNS(ns, cfg.title),
11221 cls: et.getAttributeNS(ns, cfg.cls)
11226 var onOut = function(e){
11227 clearTimeout(showProc);
11228 var t = e.getTarget();
11229 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11230 hideProc = setTimeout(hide, tm.hideDelay);
11234 var onMove = function(e){
11240 if(tm.trackMouse && ce){
11245 var onDown = function(e){
11246 clearTimeout(showProc);
11247 clearTimeout(hideProc);
11249 if(tm.hideOnClick){
11252 tm.enable.defer(100, tm);
11257 var getPad = function(){
11258 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11261 var show = function(o){
11265 clearTimeout(dismissProc);
11267 if(removeCls){ // in case manually hidden
11268 el.removeClass(removeCls);
11272 el.addClass(ce.cls);
11273 removeCls = ce.cls;
11276 tipTitle.update(ce.title);
11279 tipTitle.update('');
11282 el.dom.style.width = tm.maxWidth+'px';
11283 //tipBody.dom.style.width = '';
11284 tipBodyText.update(o.text);
11285 var p = getPad(), w = ce.width;
11287 var td = tipBodyText.dom;
11288 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11289 if(aw > tm.maxWidth){
11291 }else if(aw < tm.minWidth){
11297 //tipBody.setWidth(w);
11298 el.setWidth(parseInt(w, 10) + p);
11299 if(ce.autoHide === false){
11300 close.setDisplayed(true);
11305 close.setDisplayed(false);
11311 el.avoidY = xy[1]-18;
11316 el.setStyle("visibility", "visible");
11317 el.fadeIn({callback: afterShow});
11323 var afterShow = function(){
11327 if(tm.autoDismiss && ce.autoHide !== false){
11328 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11333 var hide = function(noanim){
11334 clearTimeout(dismissProc);
11335 clearTimeout(hideProc);
11337 if(el.isVisible()){
11339 if(noanim !== true && tm.animate){
11340 el.fadeOut({callback: afterHide});
11347 var afterHide = function(){
11350 el.removeClass(removeCls);
11357 * @cfg {Number} minWidth
11358 * The minimum width of the quick tip (defaults to 40)
11362 * @cfg {Number} maxWidth
11363 * The maximum width of the quick tip (defaults to 300)
11367 * @cfg {Boolean} interceptTitles
11368 * True to automatically use the element's DOM title value if available (defaults to false)
11370 interceptTitles : false,
11372 * @cfg {Boolean} trackMouse
11373 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11375 trackMouse : false,
11377 * @cfg {Boolean} hideOnClick
11378 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11380 hideOnClick : true,
11382 * @cfg {Number} showDelay
11383 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11387 * @cfg {Number} hideDelay
11388 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11392 * @cfg {Boolean} autoHide
11393 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11394 * Used in conjunction with hideDelay.
11399 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11400 * (defaults to true). Used in conjunction with autoDismissDelay.
11402 autoDismiss : true,
11405 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11407 autoDismissDelay : 5000,
11409 * @cfg {Boolean} animate
11410 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11415 * @cfg {String} title
11416 * Title text to display (defaults to ''). This can be any valid HTML markup.
11420 * @cfg {String} text
11421 * Body text to display (defaults to ''). This can be any valid HTML markup.
11425 * @cfg {String} cls
11426 * A CSS class to apply to the base quick tip element (defaults to '').
11430 * @cfg {Number} width
11431 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11432 * minWidth or maxWidth.
11437 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11438 * or display QuickTips in a page.
11441 tm = Roo.QuickTips;
11442 cfg = tm.tagConfig;
11444 if(!Roo.isReady){ // allow calling of init() before onReady
11445 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11448 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11449 el.fxDefaults = {stopFx: true};
11450 // maximum custom styling
11451 //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11452 el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');
11453 tipTitle = el.child('h3');
11454 tipTitle.enableDisplayMode("block");
11455 tipBody = el.child('div.x-tip-bd');
11456 tipBodyText = el.child('div.x-tip-bd-inner');
11457 //bdLeft = el.child('div.x-tip-bd-left');
11458 //bdRight = el.child('div.x-tip-bd-right');
11459 close = el.child('div.x-tip-close');
11460 close.enableDisplayMode("block");
11461 close.on("click", hide);
11462 var d = Roo.get(document);
11463 d.on("mousedown", onDown);
11464 d.on("mouseover", onOver);
11465 d.on("mouseout", onOut);
11466 d.on("mousemove", onMove);
11467 esc = d.addKeyListener(27, hide);
11470 dd = el.initDD("default", null, {
11471 onDrag : function(){
11475 dd.setHandleElId(tipTitle.id);
11484 * Configures a new quick tip instance and assigns it to a target element. The following config options
11487 Property Type Description
11488 ---------- --------------------- ------------------------------------------------------------------------
11489 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11491 * @param {Object} config The config object
11493 register : function(config){
11494 var cs = config instanceof Array ? config : arguments;
11495 for(var i = 0, len = cs.length; i < len; i++) {
11497 var target = c.target;
11499 if(target instanceof Array){
11500 for(var j = 0, jlen = target.length; j < jlen; j++){
11501 tagEls[target[j]] = c;
11504 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11511 * Removes this quick tip from its element and destroys it.
11512 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11514 unregister : function(el){
11515 delete tagEls[Roo.id(el)];
11519 * Enable this quick tip.
11521 enable : function(){
11522 if(inited && disabled){
11524 if(locks.length < 1){
11531 * Disable this quick tip.
11533 disable : function(){
11535 clearTimeout(showProc);
11536 clearTimeout(hideProc);
11537 clearTimeout(dismissProc);
11545 * Returns true if the quick tip is enabled, else false.
11547 isEnabled : function(){
11553 namespace : "roo", // was ext?? this may break..
11554 alt_namespace : "ext",
11555 attribute : "qtip",
11565 // backwards compat
11566 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11568 * Ext JS Library 1.1.1
11569 * Copyright(c) 2006-2007, Ext JS, LLC.
11571 * Originally Released Under LGPL - original licence link has changed is not relivant.
11574 * <script type="text/javascript">
11579 * @class Roo.tree.TreePanel
11580 * @extends Roo.data.Tree
11582 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11583 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11584 * @cfg {Boolean} enableDD true to enable drag and drop
11585 * @cfg {Boolean} enableDrag true to enable just drag
11586 * @cfg {Boolean} enableDrop true to enable just drop
11587 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11588 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11589 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11590 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11591 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11592 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11593 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11594 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11595 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11596 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11597 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11598 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11599 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11600 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11601 * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11602 * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11605 * @param {String/HTMLElement/Element} el The container element
11606 * @param {Object} config
11608 Roo.tree.TreePanel = function(el, config){
11610 var loader = false;
11612 root = config.root;
11613 delete config.root;
11615 if (config.loader) {
11616 loader = config.loader;
11617 delete config.loader;
11620 Roo.apply(this, config);
11621 Roo.tree.TreePanel.superclass.constructor.call(this);
11622 this.el = Roo.get(el);
11623 this.el.addClass('x-tree');
11624 //console.log(root);
11626 this.setRootNode( Roo.factory(root, Roo.tree));
11629 this.loader = Roo.factory(loader, Roo.tree);
11632 * Read-only. The id of the container element becomes this TreePanel's id.
11634 this.id = this.el.id;
11637 * @event beforeload
11638 * Fires before a node is loaded, return false to cancel
11639 * @param {Node} node The node being loaded
11641 "beforeload" : true,
11644 * Fires when a node is loaded
11645 * @param {Node} node The node that was loaded
11649 * @event textchange
11650 * Fires when the text for a node is changed
11651 * @param {Node} node The node
11652 * @param {String} text The new text
11653 * @param {String} oldText The old text
11655 "textchange" : true,
11657 * @event beforeexpand
11658 * Fires before a node is expanded, return false to cancel.
11659 * @param {Node} node The node
11660 * @param {Boolean} deep
11661 * @param {Boolean} anim
11663 "beforeexpand" : true,
11665 * @event beforecollapse
11666 * Fires before a node is collapsed, return false to cancel.
11667 * @param {Node} node The node
11668 * @param {Boolean} deep
11669 * @param {Boolean} anim
11671 "beforecollapse" : true,
11674 * Fires when a node is expanded
11675 * @param {Node} node The node
11679 * @event disabledchange
11680 * Fires when the disabled status of a node changes
11681 * @param {Node} node The node
11682 * @param {Boolean} disabled
11684 "disabledchange" : true,
11687 * Fires when a node is collapsed
11688 * @param {Node} node The node
11692 * @event beforeclick
11693 * Fires before click processing on a node. Return false to cancel the default action.
11694 * @param {Node} node The node
11695 * @param {Roo.EventObject} e The event object
11697 "beforeclick":true,
11699 * @event checkchange
11700 * Fires when a node with a checkbox's checked property changes
11701 * @param {Node} this This node
11702 * @param {Boolean} checked
11704 "checkchange":true,
11707 * Fires when a node is clicked
11708 * @param {Node} node The node
11709 * @param {Roo.EventObject} e The event object
11714 * Fires when a node is double clicked
11715 * @param {Node} node The node
11716 * @param {Roo.EventObject} e The event object
11720 * @event contextmenu
11721 * Fires when a node is right clicked
11722 * @param {Node} node The node
11723 * @param {Roo.EventObject} e The event object
11725 "contextmenu":true,
11727 * @event beforechildrenrendered
11728 * Fires right before the child nodes for a node are rendered
11729 * @param {Node} node The node
11731 "beforechildrenrendered":true,
11734 * Fires when a node starts being dragged
11735 * @param {Roo.tree.TreePanel} this
11736 * @param {Roo.tree.TreeNode} node
11737 * @param {event} e The raw browser event
11739 "startdrag" : true,
11742 * Fires when a drag operation is complete
11743 * @param {Roo.tree.TreePanel} this
11744 * @param {Roo.tree.TreeNode} node
11745 * @param {event} e The raw browser event
11750 * Fires when a dragged node is dropped on a valid DD target
11751 * @param {Roo.tree.TreePanel} this
11752 * @param {Roo.tree.TreeNode} node
11753 * @param {DD} dd The dd it was dropped on
11754 * @param {event} e The raw browser event
11758 * @event beforenodedrop
11759 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11760 * passed to handlers has the following properties:<br />
11761 * <ul style="padding:5px;padding-left:16px;">
11762 * <li>tree - The TreePanel</li>
11763 * <li>target - The node being targeted for the drop</li>
11764 * <li>data - The drag data from the drag source</li>
11765 * <li>point - The point of the drop - append, above or below</li>
11766 * <li>source - The drag source</li>
11767 * <li>rawEvent - Raw mouse event</li>
11768 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11769 * to be inserted by setting them on this object.</li>
11770 * <li>cancel - Set this to true to cancel the drop.</li>
11772 * @param {Object} dropEvent
11774 "beforenodedrop" : true,
11777 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11778 * passed to handlers has the following properties:<br />
11779 * <ul style="padding:5px;padding-left:16px;">
11780 * <li>tree - The TreePanel</li>
11781 * <li>target - The node being targeted for the drop</li>
11782 * <li>data - The drag data from the drag source</li>
11783 * <li>point - The point of the drop - append, above or below</li>
11784 * <li>source - The drag source</li>
11785 * <li>rawEvent - Raw mouse event</li>
11786 * <li>dropNode - Dropped node(s).</li>
11788 * @param {Object} dropEvent
11792 * @event nodedragover
11793 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11794 * passed to handlers has the following properties:<br />
11795 * <ul style="padding:5px;padding-left:16px;">
11796 * <li>tree - The TreePanel</li>
11797 * <li>target - The node being targeted for the drop</li>
11798 * <li>data - The drag data from the drag source</li>
11799 * <li>point - The point of the drop - append, above or below</li>
11800 * <li>source - The drag source</li>
11801 * <li>rawEvent - Raw mouse event</li>
11802 * <li>dropNode - Drop node(s) provided by the source.</li>
11803 * <li>cancel - Set this to true to signal drop not allowed.</li>
11805 * @param {Object} dragOverEvent
11807 "nodedragover" : true
11810 if(this.singleExpand){
11811 this.on("beforeexpand", this.restrictExpand, this);
11814 this.editor.tree = this;
11815 this.editor = Roo.factory(this.editor, Roo.tree);
11818 if (this.selModel) {
11819 this.selModel = Roo.factory(this.selModel, Roo.tree);
11823 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11824 rootVisible : true,
11825 animate: Roo.enableFx,
11828 hlDrop : Roo.enableFx,
11832 rendererTip: false,
11834 restrictExpand : function(node){
11835 var p = node.parentNode;
11837 if(p.expandedChild && p.expandedChild.parentNode == p){
11838 p.expandedChild.collapse();
11840 p.expandedChild = node;
11844 // private override
11845 setRootNode : function(node){
11846 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11847 if(!this.rootVisible){
11848 node.ui = new Roo.tree.RootTreeNodeUI(node);
11854 * Returns the container element for this TreePanel
11856 getEl : function(){
11861 * Returns the default TreeLoader for this TreePanel
11863 getLoader : function(){
11864 return this.loader;
11870 expandAll : function(){
11871 this.root.expand(true);
11875 * Collapse all nodes
11877 collapseAll : function(){
11878 this.root.collapse(true);
11882 * Returns the selection model used by this TreePanel
11884 getSelectionModel : function(){
11885 if(!this.selModel){
11886 this.selModel = new Roo.tree.DefaultSelectionModel();
11888 return this.selModel;
11892 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11893 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11894 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11897 getChecked : function(a, startNode){
11898 startNode = startNode || this.root;
11900 var f = function(){
11901 if(this.attributes.checked){
11902 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11905 startNode.cascade(f);
11910 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11911 * @param {String} path
11912 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11913 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11914 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11916 expandPath : function(path, attr, callback){
11917 attr = attr || "id";
11918 var keys = path.split(this.pathSeparator);
11919 var curNode = this.root;
11920 if(curNode.attributes[attr] != keys[1]){ // invalid root
11922 callback(false, null);
11927 var f = function(){
11928 if(++index == keys.length){
11930 callback(true, curNode);
11934 var c = curNode.findChild(attr, keys[index]);
11937 callback(false, curNode);
11942 c.expand(false, false, f);
11944 curNode.expand(false, false, f);
11948 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11949 * @param {String} path
11950 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11951 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11952 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11954 selectPath : function(path, attr, callback){
11955 attr = attr || "id";
11956 var keys = path.split(this.pathSeparator);
11957 var v = keys.pop();
11958 if(keys.length > 0){
11959 var f = function(success, node){
11960 if(success && node){
11961 var n = node.findChild(attr, v);
11967 }else if(callback){
11968 callback(false, n);
11972 callback(false, n);
11976 this.expandPath(keys.join(this.pathSeparator), attr, f);
11978 this.root.select();
11980 callback(true, this.root);
11985 getTreeEl : function(){
11990 * Trigger rendering of this TreePanel
11992 render : function(){
11993 if (this.innerCt) {
11994 return this; // stop it rendering more than once!!
11997 this.innerCt = this.el.createChild({tag:"ul",
11998 cls:"x-tree-root-ct " +
11999 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12001 if(this.containerScroll){
12002 Roo.dd.ScrollManager.register(this.el);
12004 if((this.enableDD || this.enableDrop) && !this.dropZone){
12006 * The dropZone used by this tree if drop is enabled
12007 * @type Roo.tree.TreeDropZone
12009 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12010 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12013 if((this.enableDD || this.enableDrag) && !this.dragZone){
12015 * The dragZone used by this tree if drag is enabled
12016 * @type Roo.tree.TreeDragZone
12018 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12019 ddGroup: this.ddGroup || "TreeDD",
12020 scroll: this.ddScroll
12023 this.getSelectionModel().init(this);
12025 Roo.log("ROOT not set in tree");
12028 this.root.render();
12029 if(!this.rootVisible){
12030 this.root.renderChildren();
12036 * Ext JS Library 1.1.1
12037 * Copyright(c) 2006-2007, Ext JS, LLC.
12039 * Originally Released Under LGPL - original licence link has changed is not relivant.
12042 * <script type="text/javascript">
12047 * @class Roo.tree.DefaultSelectionModel
12048 * @extends Roo.util.Observable
12049 * The default single selection for a TreePanel.
12050 * @param {Object} cfg Configuration
12052 Roo.tree.DefaultSelectionModel = function(cfg){
12053 this.selNode = null;
12059 * @event selectionchange
12060 * Fires when the selected node changes
12061 * @param {DefaultSelectionModel} this
12062 * @param {TreeNode} node the new selection
12064 "selectionchange" : true,
12067 * @event beforeselect
12068 * Fires before the selected node changes, return false to cancel the change
12069 * @param {DefaultSelectionModel} this
12070 * @param {TreeNode} node the new selection
12071 * @param {TreeNode} node the old selection
12073 "beforeselect" : true
12076 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12079 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12080 init : function(tree){
12082 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12083 tree.on("click", this.onNodeClick, this);
12086 onNodeClick : function(node, e){
12087 if (e.ctrlKey && this.selNode == node) {
12088 this.unselect(node);
12096 * @param {TreeNode} node The node to select
12097 * @return {TreeNode} The selected node
12099 select : function(node){
12100 var last = this.selNode;
12101 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12103 last.ui.onSelectedChange(false);
12105 this.selNode = node;
12106 node.ui.onSelectedChange(true);
12107 this.fireEvent("selectionchange", this, node, last);
12114 * @param {TreeNode} node The node to unselect
12116 unselect : function(node){
12117 if(this.selNode == node){
12118 this.clearSelections();
12123 * Clear all selections
12125 clearSelections : function(){
12126 var n = this.selNode;
12128 n.ui.onSelectedChange(false);
12129 this.selNode = null;
12130 this.fireEvent("selectionchange", this, null);
12136 * Get the selected node
12137 * @return {TreeNode} The selected node
12139 getSelectedNode : function(){
12140 return this.selNode;
12144 * Returns true if the node is selected
12145 * @param {TreeNode} node The node to check
12146 * @return {Boolean}
12148 isSelected : function(node){
12149 return this.selNode == node;
12153 * Selects the node above the selected node in the tree, intelligently walking the nodes
12154 * @return TreeNode The new selection
12156 selectPrevious : function(){
12157 var s = this.selNode || this.lastSelNode;
12161 var ps = s.previousSibling;
12163 if(!ps.isExpanded() || ps.childNodes.length < 1){
12164 return this.select(ps);
12166 var lc = ps.lastChild;
12167 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12170 return this.select(lc);
12172 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12173 return this.select(s.parentNode);
12179 * Selects the node above the selected node in the tree, intelligently walking the nodes
12180 * @return TreeNode The new selection
12182 selectNext : function(){
12183 var s = this.selNode || this.lastSelNode;
12187 if(s.firstChild && s.isExpanded()){
12188 return this.select(s.firstChild);
12189 }else if(s.nextSibling){
12190 return this.select(s.nextSibling);
12191 }else if(s.parentNode){
12193 s.parentNode.bubble(function(){
12194 if(this.nextSibling){
12195 newS = this.getOwnerTree().selModel.select(this.nextSibling);
12204 onKeyDown : function(e){
12205 var s = this.selNode || this.lastSelNode;
12206 // undesirable, but required
12211 var k = e.getKey();
12219 this.selectPrevious();
12222 e.preventDefault();
12223 if(s.hasChildNodes()){
12224 if(!s.isExpanded()){
12226 }else if(s.firstChild){
12227 this.select(s.firstChild, e);
12232 e.preventDefault();
12233 if(s.hasChildNodes() && s.isExpanded()){
12235 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12236 this.select(s.parentNode, e);
12244 * @class Roo.tree.MultiSelectionModel
12245 * @extends Roo.util.Observable
12246 * Multi selection for a TreePanel.
12247 * @param {Object} cfg Configuration
12249 Roo.tree.MultiSelectionModel = function(){
12250 this.selNodes = [];
12254 * @event selectionchange
12255 * Fires when the selected nodes change
12256 * @param {MultiSelectionModel} this
12257 * @param {Array} nodes Array of the selected nodes
12259 "selectionchange" : true
12261 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12265 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12266 init : function(tree){
12268 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12269 tree.on("click", this.onNodeClick, this);
12272 onNodeClick : function(node, e){
12273 this.select(node, e, e.ctrlKey);
12278 * @param {TreeNode} node The node to select
12279 * @param {EventObject} e (optional) An event associated with the selection
12280 * @param {Boolean} keepExisting True to retain existing selections
12281 * @return {TreeNode} The selected node
12283 select : function(node, e, keepExisting){
12284 if(keepExisting !== true){
12285 this.clearSelections(true);
12287 if(this.isSelected(node)){
12288 this.lastSelNode = node;
12291 this.selNodes.push(node);
12292 this.selMap[node.id] = node;
12293 this.lastSelNode = node;
12294 node.ui.onSelectedChange(true);
12295 this.fireEvent("selectionchange", this, this.selNodes);
12301 * @param {TreeNode} node The node to unselect
12303 unselect : function(node){
12304 if(this.selMap[node.id]){
12305 node.ui.onSelectedChange(false);
12306 var sn = this.selNodes;
12309 index = sn.indexOf(node);
12311 for(var i = 0, len = sn.length; i < len; i++){
12319 this.selNodes.splice(index, 1);
12321 delete this.selMap[node.id];
12322 this.fireEvent("selectionchange", this, this.selNodes);
12327 * Clear all selections
12329 clearSelections : function(suppressEvent){
12330 var sn = this.selNodes;
12332 for(var i = 0, len = sn.length; i < len; i++){
12333 sn[i].ui.onSelectedChange(false);
12335 this.selNodes = [];
12337 if(suppressEvent !== true){
12338 this.fireEvent("selectionchange", this, this.selNodes);
12344 * Returns true if the node is selected
12345 * @param {TreeNode} node The node to check
12346 * @return {Boolean}
12348 isSelected : function(node){
12349 return this.selMap[node.id] ? true : false;
12353 * Returns an array of the selected nodes
12356 getSelectedNodes : function(){
12357 return this.selNodes;
12360 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12362 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12364 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12367 * Ext JS Library 1.1.1
12368 * Copyright(c) 2006-2007, Ext JS, LLC.
12370 * Originally Released Under LGPL - original licence link has changed is not relivant.
12373 * <script type="text/javascript">
12377 * @class Roo.tree.TreeNode
12378 * @extends Roo.data.Node
12379 * @cfg {String} text The text for this node
12380 * @cfg {Boolean} expanded true to start the node expanded
12381 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12382 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12383 * @cfg {Boolean} disabled true to start the node disabled
12384 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12385 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12386 * @cfg {String} cls A css class to be added to the node
12387 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12388 * @cfg {String} href URL of the link used for the node (defaults to #)
12389 * @cfg {String} hrefTarget target frame for the link
12390 * @cfg {String} qtip An Ext QuickTip for the node
12391 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12392 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12393 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12394 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12395 * (defaults to undefined with no checkbox rendered)
12397 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12399 Roo.tree.TreeNode = function(attributes){
12400 attributes = attributes || {};
12401 if(typeof attributes == "string"){
12402 attributes = {text: attributes};
12404 this.childrenRendered = false;
12405 this.rendered = false;
12406 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12407 this.expanded = attributes.expanded === true;
12408 this.isTarget = attributes.isTarget !== false;
12409 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12410 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12413 * Read-only. The text for this node. To change it use setText().
12416 this.text = attributes.text;
12418 * True if this node is disabled.
12421 this.disabled = attributes.disabled === true;
12425 * @event textchange
12426 * Fires when the text for this node is changed
12427 * @param {Node} this This node
12428 * @param {String} text The new text
12429 * @param {String} oldText The old text
12431 "textchange" : true,
12433 * @event beforeexpand
12434 * Fires before this node is expanded, return false to cancel.
12435 * @param {Node} this This node
12436 * @param {Boolean} deep
12437 * @param {Boolean} anim
12439 "beforeexpand" : true,
12441 * @event beforecollapse
12442 * Fires before this node is collapsed, return false to cancel.
12443 * @param {Node} this This node
12444 * @param {Boolean} deep
12445 * @param {Boolean} anim
12447 "beforecollapse" : true,
12450 * Fires when this node is expanded
12451 * @param {Node} this This node
12455 * @event disabledchange
12456 * Fires when the disabled status of this node changes
12457 * @param {Node} this This node
12458 * @param {Boolean} disabled
12460 "disabledchange" : true,
12463 * Fires when this node is collapsed
12464 * @param {Node} this This node
12468 * @event beforeclick
12469 * Fires before click processing. Return false to cancel the default action.
12470 * @param {Node} this This node
12471 * @param {Roo.EventObject} e The event object
12473 "beforeclick":true,
12475 * @event checkchange
12476 * Fires when a node with a checkbox's checked property changes
12477 * @param {Node} this This node
12478 * @param {Boolean} checked
12480 "checkchange":true,
12483 * Fires when this node is clicked
12484 * @param {Node} this This node
12485 * @param {Roo.EventObject} e The event object
12490 * Fires when this node is double clicked
12491 * @param {Node} this This node
12492 * @param {Roo.EventObject} e The event object
12496 * @event contextmenu
12497 * Fires when this node is right clicked
12498 * @param {Node} this This node
12499 * @param {Roo.EventObject} e The event object
12501 "contextmenu":true,
12503 * @event beforechildrenrendered
12504 * Fires right before the child nodes for this node are rendered
12505 * @param {Node} this This node
12507 "beforechildrenrendered":true
12510 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12513 * Read-only. The UI for this node
12516 this.ui = new uiClass(this);
12518 // finally support items[]
12519 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12524 Roo.each(this.attributes.items, function(c) {
12525 this.appendChild(Roo.factory(c,Roo.Tree));
12527 delete this.attributes.items;
12532 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12533 preventHScroll: true,
12535 * Returns true if this node is expanded
12536 * @return {Boolean}
12538 isExpanded : function(){
12539 return this.expanded;
12543 * Returns the UI object for this node
12544 * @return {TreeNodeUI}
12546 getUI : function(){
12550 // private override
12551 setFirstChild : function(node){
12552 var of = this.firstChild;
12553 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12554 if(this.childrenRendered && of && node != of){
12555 of.renderIndent(true, true);
12558 this.renderIndent(true, true);
12562 // private override
12563 setLastChild : function(node){
12564 var ol = this.lastChild;
12565 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12566 if(this.childrenRendered && ol && node != ol){
12567 ol.renderIndent(true, true);
12570 this.renderIndent(true, true);
12574 // these methods are overridden to provide lazy rendering support
12575 // private override
12576 appendChild : function()
12578 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12579 if(node && this.childrenRendered){
12582 this.ui.updateExpandIcon();
12586 // private override
12587 removeChild : function(node){
12588 this.ownerTree.getSelectionModel().unselect(node);
12589 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12590 // if it's been rendered remove dom node
12591 if(this.childrenRendered){
12594 if(this.childNodes.length < 1){
12595 this.collapse(false, false);
12597 this.ui.updateExpandIcon();
12599 if(!this.firstChild) {
12600 this.childrenRendered = false;
12605 // private override
12606 insertBefore : function(node, refNode){
12607 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12608 if(newNode && refNode && this.childrenRendered){
12611 this.ui.updateExpandIcon();
12616 * Sets the text for this node
12617 * @param {String} text
12619 setText : function(text){
12620 var oldText = this.text;
12622 this.attributes.text = text;
12623 if(this.rendered){ // event without subscribing
12624 this.ui.onTextChange(this, text, oldText);
12626 this.fireEvent("textchange", this, text, oldText);
12630 * Triggers selection of this node
12632 select : function(){
12633 this.getOwnerTree().getSelectionModel().select(this);
12637 * Triggers deselection of this node
12639 unselect : function(){
12640 this.getOwnerTree().getSelectionModel().unselect(this);
12644 * Returns true if this node is selected
12645 * @return {Boolean}
12647 isSelected : function(){
12648 return this.getOwnerTree().getSelectionModel().isSelected(this);
12652 * Expand this node.
12653 * @param {Boolean} deep (optional) True to expand all children as well
12654 * @param {Boolean} anim (optional) false to cancel the default animation
12655 * @param {Function} callback (optional) A callback to be called when
12656 * expanding this node completes (does not wait for deep expand to complete).
12657 * Called with 1 parameter, this node.
12659 expand : function(deep, anim, callback){
12660 if(!this.expanded){
12661 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12664 if(!this.childrenRendered){
12665 this.renderChildren();
12667 this.expanded = true;
12668 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12669 this.ui.animExpand(function(){
12670 this.fireEvent("expand", this);
12671 if(typeof callback == "function"){
12675 this.expandChildNodes(true);
12677 }.createDelegate(this));
12681 this.fireEvent("expand", this);
12682 if(typeof callback == "function"){
12687 if(typeof callback == "function"){
12692 this.expandChildNodes(true);
12696 isHiddenRoot : function(){
12697 return this.isRoot && !this.getOwnerTree().rootVisible;
12701 * Collapse this node.
12702 * @param {Boolean} deep (optional) True to collapse all children as well
12703 * @param {Boolean} anim (optional) false to cancel the default animation
12705 collapse : function(deep, anim){
12706 if(this.expanded && !this.isHiddenRoot()){
12707 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12710 this.expanded = false;
12711 if((this.getOwnerTree().animate && anim !== false) || anim){
12712 this.ui.animCollapse(function(){
12713 this.fireEvent("collapse", this);
12715 this.collapseChildNodes(true);
12717 }.createDelegate(this));
12720 this.ui.collapse();
12721 this.fireEvent("collapse", this);
12725 var cs = this.childNodes;
12726 for(var i = 0, len = cs.length; i < len; i++) {
12727 cs[i].collapse(true, false);
12733 delayedExpand : function(delay){
12734 if(!this.expandProcId){
12735 this.expandProcId = this.expand.defer(delay, this);
12740 cancelExpand : function(){
12741 if(this.expandProcId){
12742 clearTimeout(this.expandProcId);
12744 this.expandProcId = false;
12748 * Toggles expanded/collapsed state of the node
12750 toggle : function(){
12759 * Ensures all parent nodes are expanded
12761 ensureVisible : function(callback){
12762 var tree = this.getOwnerTree();
12763 tree.expandPath(this.parentNode.getPath(), false, function(){
12764 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12765 Roo.callback(callback);
12766 }.createDelegate(this));
12770 * Expand all child nodes
12771 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12773 expandChildNodes : function(deep){
12774 var cs = this.childNodes;
12775 for(var i = 0, len = cs.length; i < len; i++) {
12776 cs[i].expand(deep);
12781 * Collapse all child nodes
12782 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12784 collapseChildNodes : function(deep){
12785 var cs = this.childNodes;
12786 for(var i = 0, len = cs.length; i < len; i++) {
12787 cs[i].collapse(deep);
12792 * Disables this node
12794 disable : function(){
12795 this.disabled = true;
12797 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12798 this.ui.onDisableChange(this, true);
12800 this.fireEvent("disabledchange", this, true);
12804 * Enables this node
12806 enable : function(){
12807 this.disabled = false;
12808 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12809 this.ui.onDisableChange(this, false);
12811 this.fireEvent("disabledchange", this, false);
12815 renderChildren : function(suppressEvent){
12816 if(suppressEvent !== false){
12817 this.fireEvent("beforechildrenrendered", this);
12819 var cs = this.childNodes;
12820 for(var i = 0, len = cs.length; i < len; i++){
12821 cs[i].render(true);
12823 this.childrenRendered = true;
12827 sort : function(fn, scope){
12828 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12829 if(this.childrenRendered){
12830 var cs = this.childNodes;
12831 for(var i = 0, len = cs.length; i < len; i++){
12832 cs[i].render(true);
12838 render : function(bulkRender){
12839 this.ui.render(bulkRender);
12840 if(!this.rendered){
12841 this.rendered = true;
12843 this.expanded = false;
12844 this.expand(false, false);
12850 renderIndent : function(deep, refresh){
12852 this.ui.childIndent = null;
12854 this.ui.renderIndent();
12855 if(deep === true && this.childrenRendered){
12856 var cs = this.childNodes;
12857 for(var i = 0, len = cs.length; i < len; i++){
12858 cs[i].renderIndent(true, refresh);
12864 * Ext JS Library 1.1.1
12865 * Copyright(c) 2006-2007, Ext JS, LLC.
12867 * Originally Released Under LGPL - original licence link has changed is not relivant.
12870 * <script type="text/javascript">
12874 * @class Roo.tree.AsyncTreeNode
12875 * @extends Roo.tree.TreeNode
12876 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12878 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12880 Roo.tree.AsyncTreeNode = function(config){
12881 this.loaded = false;
12882 this.loading = false;
12883 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12885 * @event beforeload
12886 * Fires before this node is loaded, return false to cancel
12887 * @param {Node} this This node
12889 this.addEvents({'beforeload':true, 'load': true});
12892 * Fires when this node is loaded
12893 * @param {Node} this This node
12896 * The loader used by this node (defaults to using the tree's defined loader)
12901 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12902 expand : function(deep, anim, callback){
12903 if(this.loading){ // if an async load is already running, waiting til it's done
12905 var f = function(){
12906 if(!this.loading){ // done loading
12907 clearInterval(timer);
12908 this.expand(deep, anim, callback);
12910 }.createDelegate(this);
12911 timer = setInterval(f, 200);
12915 if(this.fireEvent("beforeload", this) === false){
12918 this.loading = true;
12919 this.ui.beforeLoad(this);
12920 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12922 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12926 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12930 * Returns true if this node is currently loading
12931 * @return {Boolean}
12933 isLoading : function(){
12934 return this.loading;
12937 loadComplete : function(deep, anim, callback){
12938 this.loading = false;
12939 this.loaded = true;
12940 this.ui.afterLoad(this);
12941 this.fireEvent("load", this);
12942 this.expand(deep, anim, callback);
12946 * Returns true if this node has been loaded
12947 * @return {Boolean}
12949 isLoaded : function(){
12950 return this.loaded;
12953 hasChildNodes : function(){
12954 if(!this.isLeaf() && !this.loaded){
12957 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12962 * Trigger a reload for this node
12963 * @param {Function} callback
12965 reload : function(callback){
12966 this.collapse(false, false);
12967 while(this.firstChild){
12968 this.removeChild(this.firstChild);
12970 this.childrenRendered = false;
12971 this.loaded = false;
12972 if(this.isHiddenRoot()){
12973 this.expanded = false;
12975 this.expand(false, false, callback);
12979 * Ext JS Library 1.1.1
12980 * Copyright(c) 2006-2007, Ext JS, LLC.
12982 * Originally Released Under LGPL - original licence link has changed is not relivant.
12985 * <script type="text/javascript">
12989 * @class Roo.tree.TreeNodeUI
12991 * @param {Object} node The node to render
12992 * The TreeNode UI implementation is separate from the
12993 * tree implementation. Unless you are customizing the tree UI,
12994 * you should never have to use this directly.
12996 Roo.tree.TreeNodeUI = function(node){
12998 this.rendered = false;
12999 this.animating = false;
13000 this.emptyIcon = Roo.BLANK_IMAGE_URL;
13003 Roo.tree.TreeNodeUI.prototype = {
13004 removeChild : function(node){
13006 this.ctNode.removeChild(node.ui.getEl());
13010 beforeLoad : function(){
13011 this.addClass("x-tree-node-loading");
13014 afterLoad : function(){
13015 this.removeClass("x-tree-node-loading");
13018 onTextChange : function(node, text, oldText){
13020 this.textNode.innerHTML = text;
13024 onDisableChange : function(node, state){
13025 this.disabled = state;
13027 this.addClass("x-tree-node-disabled");
13029 this.removeClass("x-tree-node-disabled");
13033 onSelectedChange : function(state){
13036 this.addClass("x-tree-selected");
13039 this.removeClass("x-tree-selected");
13043 onMove : function(tree, node, oldParent, newParent, index, refNode){
13044 this.childIndent = null;
13046 var targetNode = newParent.ui.getContainer();
13047 if(!targetNode){//target not rendered
13048 this.holder = document.createElement("div");
13049 this.holder.appendChild(this.wrap);
13052 var insertBefore = refNode ? refNode.ui.getEl() : null;
13054 targetNode.insertBefore(this.wrap, insertBefore);
13056 targetNode.appendChild(this.wrap);
13058 this.node.renderIndent(true);
13062 addClass : function(cls){
13064 Roo.fly(this.elNode).addClass(cls);
13068 removeClass : function(cls){
13070 Roo.fly(this.elNode).removeClass(cls);
13074 remove : function(){
13076 this.holder = document.createElement("div");
13077 this.holder.appendChild(this.wrap);
13081 fireEvent : function(){
13082 return this.node.fireEvent.apply(this.node, arguments);
13085 initEvents : function(){
13086 this.node.on("move", this.onMove, this);
13087 var E = Roo.EventManager;
13088 var a = this.anchor;
13090 var el = Roo.fly(a, '_treeui');
13092 if(Roo.isOpera){ // opera render bug ignores the CSS
13093 el.setStyle("text-decoration", "none");
13096 el.on("click", this.onClick, this);
13097 el.on("dblclick", this.onDblClick, this);
13100 Roo.EventManager.on(this.checkbox,
13101 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13104 el.on("contextmenu", this.onContextMenu, this);
13106 var icon = Roo.fly(this.iconNode);
13107 icon.on("click", this.onClick, this);
13108 icon.on("dblclick", this.onDblClick, this);
13109 icon.on("contextmenu", this.onContextMenu, this);
13110 E.on(this.ecNode, "click", this.ecClick, this, true);
13112 if(this.node.disabled){
13113 this.addClass("x-tree-node-disabled");
13115 if(this.node.hidden){
13116 this.addClass("x-tree-node-disabled");
13118 var ot = this.node.getOwnerTree();
13119 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13120 if(dd && (!this.node.isRoot || ot.rootVisible)){
13121 Roo.dd.Registry.register(this.elNode, {
13123 handles: this.getDDHandles(),
13129 getDDHandles : function(){
13130 return [this.iconNode, this.textNode];
13135 this.wrap.style.display = "none";
13141 this.wrap.style.display = "";
13145 onContextMenu : function(e){
13146 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13147 e.preventDefault();
13149 this.fireEvent("contextmenu", this.node, e);
13153 onClick : function(e){
13158 if(this.fireEvent("beforeclick", this.node, e) !== false){
13159 if(!this.disabled && this.node.attributes.href){
13160 this.fireEvent("click", this.node, e);
13163 e.preventDefault();
13168 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13169 this.node.toggle();
13172 this.fireEvent("click", this.node, e);
13178 onDblClick : function(e){
13179 e.preventDefault();
13184 this.toggleCheck();
13186 if(!this.animating && this.node.hasChildNodes()){
13187 this.node.toggle();
13189 this.fireEvent("dblclick", this.node, e);
13192 onCheckChange : function(){
13193 var checked = this.checkbox.checked;
13194 this.node.attributes.checked = checked;
13195 this.fireEvent('checkchange', this.node, checked);
13198 ecClick : function(e){
13199 if(!this.animating && this.node.hasChildNodes()){
13200 this.node.toggle();
13204 startDrop : function(){
13205 this.dropping = true;
13208 // delayed drop so the click event doesn't get fired on a drop
13209 endDrop : function(){
13210 setTimeout(function(){
13211 this.dropping = false;
13212 }.createDelegate(this), 50);
13215 expand : function(){
13216 this.updateExpandIcon();
13217 this.ctNode.style.display = "";
13220 focus : function(){
13221 if(!this.node.preventHScroll){
13222 try{this.anchor.focus();
13224 }else if(!Roo.isIE){
13226 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13227 var l = noscroll.scrollLeft;
13228 this.anchor.focus();
13229 noscroll.scrollLeft = l;
13234 toggleCheck : function(value){
13235 var cb = this.checkbox;
13237 cb.checked = (value === undefined ? !cb.checked : value);
13243 this.anchor.blur();
13247 animExpand : function(callback){
13248 var ct = Roo.get(this.ctNode);
13250 if(!this.node.hasChildNodes()){
13251 this.updateExpandIcon();
13252 this.ctNode.style.display = "";
13253 Roo.callback(callback);
13256 this.animating = true;
13257 this.updateExpandIcon();
13260 callback : function(){
13261 this.animating = false;
13262 Roo.callback(callback);
13265 duration: this.node.ownerTree.duration || .25
13269 highlight : function(){
13270 var tree = this.node.getOwnerTree();
13271 Roo.fly(this.wrap).highlight(
13272 tree.hlColor || "C3DAF9",
13273 {endColor: tree.hlBaseColor}
13277 collapse : function(){
13278 this.updateExpandIcon();
13279 this.ctNode.style.display = "none";
13282 animCollapse : function(callback){
13283 var ct = Roo.get(this.ctNode);
13284 ct.enableDisplayMode('block');
13287 this.animating = true;
13288 this.updateExpandIcon();
13291 callback : function(){
13292 this.animating = false;
13293 Roo.callback(callback);
13296 duration: this.node.ownerTree.duration || .25
13300 getContainer : function(){
13301 return this.ctNode;
13304 getEl : function(){
13308 appendDDGhost : function(ghostNode){
13309 ghostNode.appendChild(this.elNode.cloneNode(true));
13312 getDDRepairXY : function(){
13313 return Roo.lib.Dom.getXY(this.iconNode);
13316 onRender : function(){
13320 render : function(bulkRender){
13321 var n = this.node, a = n.attributes;
13322 var targetNode = n.parentNode ?
13323 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13325 if(!this.rendered){
13326 this.rendered = true;
13328 this.renderElements(n, a, targetNode, bulkRender);
13331 if(this.textNode.setAttributeNS){
13332 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13334 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13337 this.textNode.setAttribute("ext:qtip", a.qtip);
13339 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13342 }else if(a.qtipCfg){
13343 a.qtipCfg.target = Roo.id(this.textNode);
13344 Roo.QuickTips.register(a.qtipCfg);
13347 if(!this.node.expanded){
13348 this.updateExpandIcon();
13351 if(bulkRender === true) {
13352 targetNode.appendChild(this.wrap);
13357 renderElements : function(n, a, targetNode, bulkRender)
13359 // add some indent caching, this helps performance when rendering a large tree
13360 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13361 var t = n.getOwnerTree();
13362 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13363 if (typeof(n.attributes.html) != 'undefined') {
13364 txt = n.attributes.html;
13366 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13367 var cb = typeof a.checked == 'boolean';
13368 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13369 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13370 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13371 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13372 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13373 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13374 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13375 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13376 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13377 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13380 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13381 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13382 n.nextSibling.ui.getEl(), buf.join(""));
13384 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13387 this.elNode = this.wrap.childNodes[0];
13388 this.ctNode = this.wrap.childNodes[1];
13389 var cs = this.elNode.childNodes;
13390 this.indentNode = cs[0];
13391 this.ecNode = cs[1];
13392 this.iconNode = cs[2];
13395 this.checkbox = cs[3];
13398 this.anchor = cs[index];
13399 this.textNode = cs[index].firstChild;
13402 getAnchor : function(){
13403 return this.anchor;
13406 getTextEl : function(){
13407 return this.textNode;
13410 getIconEl : function(){
13411 return this.iconNode;
13414 isChecked : function(){
13415 return this.checkbox ? this.checkbox.checked : false;
13418 updateExpandIcon : function(){
13420 var n = this.node, c1, c2;
13421 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13422 var hasChild = n.hasChildNodes();
13426 c1 = "x-tree-node-collapsed";
13427 c2 = "x-tree-node-expanded";
13430 c1 = "x-tree-node-expanded";
13431 c2 = "x-tree-node-collapsed";
13434 this.removeClass("x-tree-node-leaf");
13435 this.wasLeaf = false;
13437 if(this.c1 != c1 || this.c2 != c2){
13438 Roo.fly(this.elNode).replaceClass(c1, c2);
13439 this.c1 = c1; this.c2 = c2;
13442 // this changes non-leafs into leafs if they have no children.
13443 // it's not very rational behaviour..
13445 if(!this.wasLeaf && this.node.leaf){
13446 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13449 this.wasLeaf = true;
13452 var ecc = "x-tree-ec-icon "+cls;
13453 if(this.ecc != ecc){
13454 this.ecNode.className = ecc;
13460 getChildIndent : function(){
13461 if(!this.childIndent){
13465 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13467 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13469 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13474 this.childIndent = buf.join("");
13476 return this.childIndent;
13479 renderIndent : function(){
13482 var p = this.node.parentNode;
13484 indent = p.ui.getChildIndent();
13486 if(this.indentMarkup != indent){ // don't rerender if not required
13487 this.indentNode.innerHTML = indent;
13488 this.indentMarkup = indent;
13490 this.updateExpandIcon();
13495 Roo.tree.RootTreeNodeUI = function(){
13496 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13498 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13499 render : function(){
13500 if(!this.rendered){
13501 var targetNode = this.node.ownerTree.innerCt.dom;
13502 this.node.expanded = true;
13503 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13504 this.wrap = this.ctNode = targetNode.firstChild;
13507 collapse : function(){
13509 expand : function(){
13513 * Ext JS Library 1.1.1
13514 * Copyright(c) 2006-2007, Ext JS, LLC.
13516 * Originally Released Under LGPL - original licence link has changed is not relivant.
13519 * <script type="text/javascript">
13522 * @class Roo.tree.TreeLoader
13523 * @extends Roo.util.Observable
13524 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13525 * nodes from a specified URL. The response must be a javascript Array definition
13526 * who's elements are node definition objects. eg:
13531 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13532 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13539 * The old style respose with just an array is still supported, but not recommended.
13542 * A server request is sent, and child nodes are loaded only when a node is expanded.
13543 * The loading node's id is passed to the server under the parameter name "node" to
13544 * enable the server to produce the correct child nodes.
13546 * To pass extra parameters, an event handler may be attached to the "beforeload"
13547 * event, and the parameters specified in the TreeLoader's baseParams property:
13549 myTreeLoader.on("beforeload", function(treeLoader, node) {
13550 this.baseParams.category = node.attributes.category;
13553 * This would pass an HTTP parameter called "category" to the server containing
13554 * the value of the Node's "category" attribute.
13556 * Creates a new Treeloader.
13557 * @param {Object} config A config object containing config properties.
13559 Roo.tree.TreeLoader = function(config){
13560 this.baseParams = {};
13561 this.requestMethod = "POST";
13562 Roo.apply(this, config);
13567 * @event beforeload
13568 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13569 * @param {Object} This TreeLoader object.
13570 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13571 * @param {Object} callback The callback function specified in the {@link #load} call.
13576 * Fires when the node has been successfuly loaded.
13577 * @param {Object} This TreeLoader object.
13578 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13579 * @param {Object} response The response object containing the data from the server.
13583 * @event loadexception
13584 * Fires if the network request failed.
13585 * @param {Object} This TreeLoader object.
13586 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13587 * @param {Object} response The response object containing the data from the server.
13589 loadexception : true,
13592 * Fires before a node is created, enabling you to return custom Node types
13593 * @param {Object} This TreeLoader object.
13594 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13599 Roo.tree.TreeLoader.superclass.constructor.call(this);
13602 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13604 * @cfg {String} dataUrl The URL from which to request a Json string which
13605 * specifies an array of node definition object representing the child nodes
13609 * @cfg {String} requestMethod either GET or POST
13610 * defaults to POST (due to BC)
13614 * @cfg {Object} baseParams (optional) An object containing properties which
13615 * specify HTTP parameters to be passed to each request for child nodes.
13618 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13619 * created by this loader. If the attributes sent by the server have an attribute in this object,
13620 * they take priority.
13623 * @cfg {Object} uiProviders (optional) An object containing properties which
13625 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13626 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13627 * <i>uiProvider</i> attribute of a returned child node is a string rather
13628 * than a reference to a TreeNodeUI implementation, this that string value
13629 * is used as a property name in the uiProviders object. You can define the provider named
13630 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13635 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13636 * child nodes before loading.
13638 clearOnLoad : true,
13641 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13642 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13643 * Grid query { data : [ .....] }
13648 * @cfg {String} queryParam (optional)
13649 * Name of the query as it will be passed on the querystring (defaults to 'node')
13650 * eg. the request will be ?node=[id]
13657 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13658 * This is called automatically when a node is expanded, but may be used to reload
13659 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13660 * @param {Roo.tree.TreeNode} node
13661 * @param {Function} callback
13663 load : function(node, callback){
13664 if(this.clearOnLoad){
13665 while(node.firstChild){
13666 node.removeChild(node.firstChild);
13669 if(node.attributes.children){ // preloaded json children
13670 var cs = node.attributes.children;
13671 for(var i = 0, len = cs.length; i < len; i++){
13672 node.appendChild(this.createNode(cs[i]));
13674 if(typeof callback == "function"){
13677 }else if(this.dataUrl){
13678 this.requestData(node, callback);
13682 getParams: function(node){
13683 var buf = [], bp = this.baseParams;
13684 for(var key in bp){
13685 if(typeof bp[key] != "function"){
13686 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13689 var n = this.queryParam === false ? 'node' : this.queryParam;
13690 buf.push(n + "=", encodeURIComponent(node.id));
13691 return buf.join("");
13694 requestData : function(node, callback){
13695 if(this.fireEvent("beforeload", this, node, callback) !== false){
13696 this.transId = Roo.Ajax.request({
13697 method:this.requestMethod,
13698 url: this.dataUrl||this.url,
13699 success: this.handleResponse,
13700 failure: this.handleFailure,
13702 argument: {callback: callback, node: node},
13703 params: this.getParams(node)
13706 // if the load is cancelled, make sure we notify
13707 // the node that we are done
13708 if(typeof callback == "function"){
13714 isLoading : function(){
13715 return this.transId ? true : false;
13718 abort : function(){
13719 if(this.isLoading()){
13720 Roo.Ajax.abort(this.transId);
13725 createNode : function(attr)
13727 // apply baseAttrs, nice idea Corey!
13728 if(this.baseAttrs){
13729 Roo.applyIf(attr, this.baseAttrs);
13731 if(this.applyLoader !== false){
13732 attr.loader = this;
13734 // uiProvider = depreciated..
13736 if(typeof(attr.uiProvider) == 'string'){
13737 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13738 /** eval:var:attr */ eval(attr.uiProvider);
13740 if(typeof(this.uiProviders['default']) != 'undefined') {
13741 attr.uiProvider = this.uiProviders['default'];
13744 this.fireEvent('create', this, attr);
13746 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13748 new Roo.tree.TreeNode(attr) :
13749 new Roo.tree.AsyncTreeNode(attr));
13752 processResponse : function(response, node, callback)
13754 var json = response.responseText;
13757 var o = Roo.decode(json);
13759 if (this.root === false && typeof(o.success) != undefined) {
13760 this.root = 'data'; // the default behaviour for list like data..
13763 if (this.root !== false && !o.success) {
13764 // it's a failure condition.
13765 var a = response.argument;
13766 this.fireEvent("loadexception", this, a.node, response);
13767 Roo.log("Load failed - should have a handler really");
13773 if (this.root !== false) {
13777 for(var i = 0, len = o.length; i < len; i++){
13778 var n = this.createNode(o[i]);
13780 node.appendChild(n);
13783 if(typeof callback == "function"){
13784 callback(this, node);
13787 this.handleFailure(response);
13791 handleResponse : function(response){
13792 this.transId = false;
13793 var a = response.argument;
13794 this.processResponse(response, a.node, a.callback);
13795 this.fireEvent("load", this, a.node, response);
13798 handleFailure : function(response)
13800 // should handle failure better..
13801 this.transId = false;
13802 var a = response.argument;
13803 this.fireEvent("loadexception", this, a.node, response);
13804 if(typeof a.callback == "function"){
13805 a.callback(this, a.node);
13810 * Ext JS Library 1.1.1
13811 * Copyright(c) 2006-2007, Ext JS, LLC.
13813 * Originally Released Under LGPL - original licence link has changed is not relivant.
13816 * <script type="text/javascript">
13820 * @class Roo.tree.TreeFilter
13821 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13822 * @param {TreePanel} tree
13823 * @param {Object} config (optional)
13825 Roo.tree.TreeFilter = function(tree, config){
13827 this.filtered = {};
13828 Roo.apply(this, config);
13831 Roo.tree.TreeFilter.prototype = {
13838 * Filter the data by a specific attribute.
13839 * @param {String/RegExp} value Either string that the attribute value
13840 * should start with or a RegExp to test against the attribute
13841 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13842 * @param {TreeNode} startNode (optional) The node to start the filter at.
13844 filter : function(value, attr, startNode){
13845 attr = attr || "text";
13847 if(typeof value == "string"){
13848 var vlen = value.length;
13849 // auto clear empty filter
13850 if(vlen == 0 && this.clearBlank){
13854 value = value.toLowerCase();
13856 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13858 }else if(value.exec){ // regex?
13860 return value.test(n.attributes[attr]);
13863 throw 'Illegal filter type, must be string or regex';
13865 this.filterBy(f, null, startNode);
13869 * Filter by a function. The passed function will be called with each
13870 * node in the tree (or from the startNode). If the function returns true, the node is kept
13871 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13872 * @param {Function} fn The filter function
13873 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13875 filterBy : function(fn, scope, startNode){
13876 startNode = startNode || this.tree.root;
13877 if(this.autoClear){
13880 var af = this.filtered, rv = this.reverse;
13881 var f = function(n){
13882 if(n == startNode){
13888 var m = fn.call(scope || n, n);
13896 startNode.cascade(f);
13899 if(typeof id != "function"){
13901 if(n && n.parentNode){
13902 n.parentNode.removeChild(n);
13910 * Clears the current filter. Note: with the "remove" option
13911 * set a filter cannot be cleared.
13913 clear : function(){
13915 var af = this.filtered;
13917 if(typeof id != "function"){
13924 this.filtered = {};
13929 * Ext JS Library 1.1.1
13930 * Copyright(c) 2006-2007, Ext JS, LLC.
13932 * Originally Released Under LGPL - original licence link has changed is not relivant.
13935 * <script type="text/javascript">
13940 * @class Roo.tree.TreeSorter
13941 * Provides sorting of nodes in a TreePanel
13943 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13944 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13945 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13946 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13947 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13948 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13950 * @param {TreePanel} tree
13951 * @param {Object} config
13953 Roo.tree.TreeSorter = function(tree, config){
13954 Roo.apply(this, config);
13955 tree.on("beforechildrenrendered", this.doSort, this);
13956 tree.on("append", this.updateSort, this);
13957 tree.on("insert", this.updateSort, this);
13959 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13960 var p = this.property || "text";
13961 var sortType = this.sortType;
13962 var fs = this.folderSort;
13963 var cs = this.caseSensitive === true;
13964 var leafAttr = this.leafAttr || 'leaf';
13966 this.sortFn = function(n1, n2){
13968 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13971 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13975 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13976 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13978 return dsc ? +1 : -1;
13980 return dsc ? -1 : +1;
13987 Roo.tree.TreeSorter.prototype = {
13988 doSort : function(node){
13989 node.sort(this.sortFn);
13992 compareNodes : function(n1, n2){
13993 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13996 updateSort : function(tree, node){
13997 if(node.childrenRendered){
13998 this.doSort.defer(1, this, [node]);
14003 * Ext JS Library 1.1.1
14004 * Copyright(c) 2006-2007, Ext JS, LLC.
14006 * Originally Released Under LGPL - original licence link has changed is not relivant.
14009 * <script type="text/javascript">
14012 if(Roo.dd.DropZone){
14014 Roo.tree.TreeDropZone = function(tree, config){
14015 this.allowParentInsert = false;
14016 this.allowContainerDrop = false;
14017 this.appendOnly = false;
14018 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14020 this.lastInsertClass = "x-tree-no-status";
14021 this.dragOverData = {};
14024 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14025 ddGroup : "TreeDD",
14028 expandDelay : 1000,
14030 expandNode : function(node){
14031 if(node.hasChildNodes() && !node.isExpanded()){
14032 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14036 queueExpand : function(node){
14037 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14040 cancelExpand : function(){
14041 if(this.expandProcId){
14042 clearTimeout(this.expandProcId);
14043 this.expandProcId = false;
14047 isValidDropPoint : function(n, pt, dd, e, data){
14048 if(!n || !data){ return false; }
14049 var targetNode = n.node;
14050 var dropNode = data.node;
14051 // default drop rules
14052 if(!(targetNode && targetNode.isTarget && pt)){
14055 if(pt == "append" && targetNode.allowChildren === false){
14058 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14061 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14064 // reuse the object
14065 var overEvent = this.dragOverData;
14066 overEvent.tree = this.tree;
14067 overEvent.target = targetNode;
14068 overEvent.data = data;
14069 overEvent.point = pt;
14070 overEvent.source = dd;
14071 overEvent.rawEvent = e;
14072 overEvent.dropNode = dropNode;
14073 overEvent.cancel = false;
14074 var result = this.tree.fireEvent("nodedragover", overEvent);
14075 return overEvent.cancel === false && result !== false;
14078 getDropPoint : function(e, n, dd)
14082 return tn.allowChildren !== false ? "append" : false; // always append for root
14084 var dragEl = n.ddel;
14085 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14086 var y = Roo.lib.Event.getPageY(e);
14087 //var noAppend = tn.allowChildren === false || tn.isLeaf();
14089 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14090 var noAppend = tn.allowChildren === false;
14091 if(this.appendOnly || tn.parentNode.allowChildren === false){
14092 return noAppend ? false : "append";
14094 var noBelow = false;
14095 if(!this.allowParentInsert){
14096 noBelow = tn.hasChildNodes() && tn.isExpanded();
14098 var q = (b - t) / (noAppend ? 2 : 3);
14099 if(y >= t && y < (t + q)){
14101 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14108 onNodeEnter : function(n, dd, e, data)
14110 this.cancelExpand();
14113 onNodeOver : function(n, dd, e, data)
14116 var pt = this.getDropPoint(e, n, dd);
14119 // auto node expand check
14120 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14121 this.queueExpand(node);
14122 }else if(pt != "append"){
14123 this.cancelExpand();
14126 // set the insert point style on the target node
14127 var returnCls = this.dropNotAllowed;
14128 if(this.isValidDropPoint(n, pt, dd, e, data)){
14133 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14134 cls = "x-tree-drag-insert-above";
14135 }else if(pt == "below"){
14136 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14137 cls = "x-tree-drag-insert-below";
14139 returnCls = "x-tree-drop-ok-append";
14140 cls = "x-tree-drag-append";
14142 if(this.lastInsertClass != cls){
14143 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14144 this.lastInsertClass = cls;
14151 onNodeOut : function(n, dd, e, data){
14153 this.cancelExpand();
14154 this.removeDropIndicators(n);
14157 onNodeDrop : function(n, dd, e, data){
14158 var point = this.getDropPoint(e, n, dd);
14159 var targetNode = n.node;
14160 targetNode.ui.startDrop();
14161 if(!this.isValidDropPoint(n, point, dd, e, data)){
14162 targetNode.ui.endDrop();
14165 // first try to find the drop node
14166 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14169 target: targetNode,
14174 dropNode: dropNode,
14177 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14178 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14179 targetNode.ui.endDrop();
14182 // allow target changing
14183 targetNode = dropEvent.target;
14184 if(point == "append" && !targetNode.isExpanded()){
14185 targetNode.expand(false, null, function(){
14186 this.completeDrop(dropEvent);
14187 }.createDelegate(this));
14189 this.completeDrop(dropEvent);
14194 completeDrop : function(de){
14195 var ns = de.dropNode, p = de.point, t = de.target;
14196 if(!(ns instanceof Array)){
14200 for(var i = 0, len = ns.length; i < len; i++){
14203 t.parentNode.insertBefore(n, t);
14204 }else if(p == "below"){
14205 t.parentNode.insertBefore(n, t.nextSibling);
14211 if(this.tree.hlDrop){
14215 this.tree.fireEvent("nodedrop", de);
14218 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14219 if(this.tree.hlDrop){
14220 dropNode.ui.focus();
14221 dropNode.ui.highlight();
14223 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14226 getTree : function(){
14230 removeDropIndicators : function(n){
14233 Roo.fly(el).removeClass([
14234 "x-tree-drag-insert-above",
14235 "x-tree-drag-insert-below",
14236 "x-tree-drag-append"]);
14237 this.lastInsertClass = "_noclass";
14241 beforeDragDrop : function(target, e, id){
14242 this.cancelExpand();
14246 afterRepair : function(data){
14247 if(data && Roo.enableFx){
14248 data.node.ui.highlight();
14258 * Ext JS Library 1.1.1
14259 * Copyright(c) 2006-2007, Ext JS, LLC.
14261 * Originally Released Under LGPL - original licence link has changed is not relivant.
14264 * <script type="text/javascript">
14268 if(Roo.dd.DragZone){
14269 Roo.tree.TreeDragZone = function(tree, config){
14270 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14274 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14275 ddGroup : "TreeDD",
14277 onBeforeDrag : function(data, e){
14279 return n && n.draggable && !n.disabled;
14283 onInitDrag : function(e){
14284 var data = this.dragData;
14285 this.tree.getSelectionModel().select(data.node);
14286 this.proxy.update("");
14287 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14288 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14291 getRepairXY : function(e, data){
14292 return data.node.ui.getDDRepairXY();
14295 onEndDrag : function(data, e){
14296 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14301 onValidDrop : function(dd, e, id){
14302 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14306 beforeInvalidDrop : function(e, id){
14307 // this scrolls the original position back into view
14308 var sm = this.tree.getSelectionModel();
14309 sm.clearSelections();
14310 sm.select(this.dragData.node);
14315 * Ext JS Library 1.1.1
14316 * Copyright(c) 2006-2007, Ext JS, LLC.
14318 * Originally Released Under LGPL - original licence link has changed is not relivant.
14321 * <script type="text/javascript">
14324 * @class Roo.tree.TreeEditor
14325 * @extends Roo.Editor
14326 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14327 * as the editor field.
14329 * @param {Object} config (used to be the tree panel.)
14330 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14332 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14333 * @cfg {Roo.form.TextField|Object} field The field configuration
14337 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14340 if (oldconfig) { // old style..
14341 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14344 tree = config.tree;
14345 config.field = config.field || {};
14346 config.field.xtype = 'TextField';
14347 field = Roo.factory(config.field, Roo.form);
14349 config = config || {};
14354 * @event beforenodeedit
14355 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14356 * false from the handler of this event.
14357 * @param {Editor} this
14358 * @param {Roo.tree.Node} node
14360 "beforenodeedit" : true
14364 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14368 tree.on('beforeclick', this.beforeNodeClick, this);
14369 tree.getTreeEl().on('mousedown', this.hide, this);
14370 this.on('complete', this.updateNode, this);
14371 this.on('beforestartedit', this.fitToTree, this);
14372 this.on('startedit', this.bindScroll, this, {delay:10});
14373 this.on('specialkey', this.onSpecialKey, this);
14376 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14378 * @cfg {String} alignment
14379 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14385 * @cfg {Boolean} hideEl
14386 * True to hide the bound element while the editor is displayed (defaults to false)
14390 * @cfg {String} cls
14391 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14393 cls: "x-small-editor x-tree-editor",
14395 * @cfg {Boolean} shim
14396 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14402 * @cfg {Number} maxWidth
14403 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14404 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14405 * scroll and client offsets into account prior to each edit.
14412 fitToTree : function(ed, el){
14413 var td = this.tree.getTreeEl().dom, nd = el.dom;
14414 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14415 td.scrollLeft = nd.offsetLeft;
14419 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14420 this.setSize(w, '');
14422 return this.fireEvent('beforenodeedit', this, this.editNode);
14427 triggerEdit : function(node){
14428 this.completeEdit();
14429 this.editNode = node;
14430 this.startEdit(node.ui.textNode, node.text);
14434 bindScroll : function(){
14435 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14439 beforeNodeClick : function(node, e){
14440 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14441 this.lastClick = new Date();
14442 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14444 this.triggerEdit(node);
14451 updateNode : function(ed, value){
14452 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14453 this.editNode.setText(value);
14457 onHide : function(){
14458 Roo.tree.TreeEditor.superclass.onHide.call(this);
14460 this.editNode.ui.focus();
14465 onSpecialKey : function(field, e){
14466 var k = e.getKey();
14470 }else if(k == e.ENTER && !e.hasModifier()){
14472 this.completeEdit();
14475 });//<Script type="text/javascript">
14478 * Ext JS Library 1.1.1
14479 * Copyright(c) 2006-2007, Ext JS, LLC.
14481 * Originally Released Under LGPL - original licence link has changed is not relivant.
14484 * <script type="text/javascript">
14488 * Not documented??? - probably should be...
14491 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14492 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14494 renderElements : function(n, a, targetNode, bulkRender){
14495 //consel.log("renderElements?");
14496 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14498 var t = n.getOwnerTree();
14499 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14501 var cols = t.columns;
14502 var bw = t.borderWidth;
14504 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14505 var cb = typeof a.checked == "boolean";
14506 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14507 var colcls = 'x-t-' + tid + '-c0';
14509 '<li class="x-tree-node">',
14512 '<div class="x-tree-node-el ', a.cls,'">',
14514 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14517 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14518 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14519 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14520 (a.icon ? ' x-tree-node-inline-icon' : ''),
14521 (a.iconCls ? ' '+a.iconCls : ''),
14522 '" unselectable="on" />',
14523 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14524 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14526 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14527 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14528 '<span unselectable="on" qtip="' + tx + '">',
14532 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14533 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14535 for(var i = 1, len = cols.length; i < len; i++){
14537 colcls = 'x-t-' + tid + '-c' +i;
14538 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14539 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14540 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14546 '<div class="x-clear"></div></div>',
14547 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14550 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14551 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14552 n.nextSibling.ui.getEl(), buf.join(""));
14554 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14556 var el = this.wrap.firstChild;
14558 this.elNode = el.firstChild;
14559 this.ranchor = el.childNodes[1];
14560 this.ctNode = this.wrap.childNodes[1];
14561 var cs = el.firstChild.childNodes;
14562 this.indentNode = cs[0];
14563 this.ecNode = cs[1];
14564 this.iconNode = cs[2];
14567 this.checkbox = cs[3];
14570 this.anchor = cs[index];
14572 this.textNode = cs[index].firstChild;
14574 //el.on("click", this.onClick, this);
14575 //el.on("dblclick", this.onDblClick, this);
14578 // console.log(this);
14580 initEvents : function(){
14581 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14584 var a = this.ranchor;
14586 var el = Roo.get(a);
14588 if(Roo.isOpera){ // opera render bug ignores the CSS
14589 el.setStyle("text-decoration", "none");
14592 el.on("click", this.onClick, this);
14593 el.on("dblclick", this.onDblClick, this);
14594 el.on("contextmenu", this.onContextMenu, this);
14598 /*onSelectedChange : function(state){
14601 this.addClass("x-tree-selected");
14604 this.removeClass("x-tree-selected");
14607 addClass : function(cls){
14609 Roo.fly(this.elRow).addClass(cls);
14615 removeClass : function(cls){
14617 Roo.fly(this.elRow).removeClass(cls);
14623 });//<Script type="text/javascript">
14627 * Ext JS Library 1.1.1
14628 * Copyright(c) 2006-2007, Ext JS, LLC.
14630 * Originally Released Under LGPL - original licence link has changed is not relivant.
14633 * <script type="text/javascript">
14638 * @class Roo.tree.ColumnTree
14639 * @extends Roo.data.TreePanel
14640 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14641 * @cfg {int} borderWidth compined right/left border allowance
14643 * @param {String/HTMLElement/Element} el The container element
14644 * @param {Object} config
14646 Roo.tree.ColumnTree = function(el, config)
14648 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14652 * Fire this event on a container when it resizes
14653 * @param {int} w Width
14654 * @param {int} h Height
14658 this.on('resize', this.onResize, this);
14661 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14665 borderWidth: Roo.isBorderBox ? 0 : 2,
14668 render : function(){
14669 // add the header.....
14671 Roo.tree.ColumnTree.superclass.render.apply(this);
14673 this.el.addClass('x-column-tree');
14675 this.headers = this.el.createChild(
14676 {cls:'x-tree-headers'},this.innerCt.dom);
14678 var cols = this.columns, c;
14679 var totalWidth = 0;
14681 var len = cols.length;
14682 for(var i = 0; i < len; i++){
14684 totalWidth += c.width;
14685 this.headEls.push(this.headers.createChild({
14686 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14688 cls:'x-tree-hd-text',
14691 style:'width:'+(c.width-this.borderWidth)+'px;'
14694 this.headers.createChild({cls:'x-clear'});
14695 // prevent floats from wrapping when clipped
14696 this.headers.setWidth(totalWidth);
14697 //this.innerCt.setWidth(totalWidth);
14698 this.innerCt.setStyle({ overflow: 'auto' });
14699 this.onResize(this.width, this.height);
14703 onResize : function(w,h)
14708 this.innerCt.setWidth(this.width);
14709 this.innerCt.setHeight(this.height-20);
14712 var cols = this.columns, c;
14713 var totalWidth = 0;
14715 var len = cols.length;
14716 for(var i = 0; i < len; i++){
14718 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14719 // it's the expander..
14720 expEl = this.headEls[i];
14723 totalWidth += c.width;
14727 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14729 this.headers.setWidth(w-20);
14738 * Ext JS Library 1.1.1
14739 * Copyright(c) 2006-2007, Ext JS, LLC.
14741 * Originally Released Under LGPL - original licence link has changed is not relivant.
14744 * <script type="text/javascript">
14748 * @class Roo.menu.Menu
14749 * @extends Roo.util.Observable
14750 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14751 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14753 * Creates a new Menu
14754 * @param {Object} config Configuration options
14756 Roo.menu.Menu = function(config){
14757 Roo.apply(this, config);
14758 this.id = this.id || Roo.id();
14761 * @event beforeshow
14762 * Fires before this menu is displayed
14763 * @param {Roo.menu.Menu} this
14767 * @event beforehide
14768 * Fires before this menu is hidden
14769 * @param {Roo.menu.Menu} this
14774 * Fires after this menu is displayed
14775 * @param {Roo.menu.Menu} this
14780 * Fires after this menu is hidden
14781 * @param {Roo.menu.Menu} this
14786 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14787 * @param {Roo.menu.Menu} this
14788 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14789 * @param {Roo.EventObject} e
14794 * Fires when the mouse is hovering over this menu
14795 * @param {Roo.menu.Menu} this
14796 * @param {Roo.EventObject} e
14797 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14802 * Fires when the mouse exits this menu
14803 * @param {Roo.menu.Menu} this
14804 * @param {Roo.EventObject} e
14805 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810 * Fires when a menu item contained in this menu is clicked
14811 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14812 * @param {Roo.EventObject} e
14816 if (this.registerMenu) {
14817 Roo.menu.MenuMgr.register(this);
14820 var mis = this.items;
14821 this.items = new Roo.util.MixedCollection();
14823 this.add.apply(this, mis);
14827 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14829 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14833 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14834 * for bottom-right shadow (defaults to "sides")
14838 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14839 * this menu (defaults to "tl-tr?")
14841 subMenuAlign : "tl-tr?",
14843 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14844 * relative to its element of origin (defaults to "tl-bl?")
14846 defaultAlign : "tl-bl?",
14848 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14850 allowOtherMenus : false,
14852 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14854 registerMenu : true,
14859 render : function(){
14863 var el = this.el = new Roo.Layer({
14865 shadow:this.shadow,
14867 parentEl: this.parentEl || document.body,
14871 this.keyNav = new Roo.menu.MenuNav(this);
14874 el.addClass("x-menu-plain");
14877 el.addClass(this.cls);
14879 // generic focus element
14880 this.focusEl = el.createChild({
14881 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14883 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14884 //disabling touch- as it's causing issues ..
14885 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14886 ul.on('click' , this.onClick, this);
14889 ul.on("mouseover", this.onMouseOver, this);
14890 ul.on("mouseout", this.onMouseOut, this);
14891 this.items.each(function(item){
14896 var li = document.createElement("li");
14897 li.className = "x-menu-list-item";
14898 ul.dom.appendChild(li);
14899 item.render(li, this);
14906 autoWidth : function(){
14907 var el = this.el, ul = this.ul;
14911 var w = this.width;
14914 }else if(Roo.isIE){
14915 el.setWidth(this.minWidth);
14916 var t = el.dom.offsetWidth; // force recalc
14917 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14922 delayAutoWidth : function(){
14925 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14927 this.awTask.delay(20);
14932 findTargetItem : function(e){
14933 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14934 if(t && t.menuItemId){
14935 return this.items.get(t.menuItemId);
14940 onClick : function(e){
14941 Roo.log("menu.onClick");
14942 var t = this.findTargetItem(e);
14947 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14948 if(t == this.activeItem && t.shouldDeactivate(e)){
14949 this.activeItem.deactivate();
14950 delete this.activeItem;
14954 this.setActiveItem(t, true);
14962 this.fireEvent("click", this, t, e);
14966 setActiveItem : function(item, autoExpand){
14967 if(item != this.activeItem){
14968 if(this.activeItem){
14969 this.activeItem.deactivate();
14971 this.activeItem = item;
14972 item.activate(autoExpand);
14973 }else if(autoExpand){
14979 tryActivate : function(start, step){
14980 var items = this.items;
14981 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14982 var item = items.get(i);
14983 if(!item.disabled && item.canActivate){
14984 this.setActiveItem(item, false);
14992 onMouseOver : function(e){
14994 if(t = this.findTargetItem(e)){
14995 if(t.canActivate && !t.disabled){
14996 this.setActiveItem(t, true);
14999 this.fireEvent("mouseover", this, e, t);
15003 onMouseOut : function(e){
15005 if(t = this.findTargetItem(e)){
15006 if(t == this.activeItem && t.shouldDeactivate(e)){
15007 this.activeItem.deactivate();
15008 delete this.activeItem;
15011 this.fireEvent("mouseout", this, e, t);
15015 * Read-only. Returns true if the menu is currently displayed, else false.
15018 isVisible : function(){
15019 return this.el && !this.hidden;
15023 * Displays this menu relative to another element
15024 * @param {String/HTMLElement/Roo.Element} element The element to align to
15025 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15026 * the element (defaults to this.defaultAlign)
15027 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15029 show : function(el, pos, parentMenu){
15030 this.parentMenu = parentMenu;
15034 this.fireEvent("beforeshow", this);
15035 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15039 * Displays this menu at a specific xy position
15040 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15041 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15043 showAt : function(xy, parentMenu, /* private: */_e){
15044 this.parentMenu = parentMenu;
15049 this.fireEvent("beforeshow", this);
15050 xy = this.el.adjustForConstraints(xy);
15054 this.hidden = false;
15056 this.fireEvent("show", this);
15059 focus : function(){
15061 this.doFocus.defer(50, this);
15065 doFocus : function(){
15067 this.focusEl.focus();
15072 * Hides this menu and optionally all parent menus
15073 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15075 hide : function(deep){
15076 if(this.el && this.isVisible()){
15077 this.fireEvent("beforehide", this);
15078 if(this.activeItem){
15079 this.activeItem.deactivate();
15080 this.activeItem = null;
15083 this.hidden = true;
15084 this.fireEvent("hide", this);
15086 if(deep === true && this.parentMenu){
15087 this.parentMenu.hide(true);
15092 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15093 * Any of the following are valid:
15095 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15096 * <li>An HTMLElement object which will be converted to a menu item</li>
15097 * <li>A menu item config object that will be created as a new menu item</li>
15098 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15099 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15104 var menu = new Roo.menu.Menu();
15106 // Create a menu item to add by reference
15107 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15109 // Add a bunch of items at once using different methods.
15110 // Only the last item added will be returned.
15111 var item = menu.add(
15112 menuItem, // add existing item by ref
15113 'Dynamic Item', // new TextItem
15114 '-', // new separator
15115 { text: 'Config Item' } // new item by config
15118 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15119 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15122 var a = arguments, l = a.length, item;
15123 for(var i = 0; i < l; i++){
15125 if ((typeof(el) == "object") && el.xtype && el.xns) {
15126 el = Roo.factory(el, Roo.menu);
15129 if(el.render){ // some kind of Item
15130 item = this.addItem(el);
15131 }else if(typeof el == "string"){ // string
15132 if(el == "separator" || el == "-"){
15133 item = this.addSeparator();
15135 item = this.addText(el);
15137 }else if(el.tagName || el.el){ // element
15138 item = this.addElement(el);
15139 }else if(typeof el == "object"){ // must be menu item config?
15140 item = this.addMenuItem(el);
15147 * Returns this menu's underlying {@link Roo.Element} object
15148 * @return {Roo.Element} The element
15150 getEl : function(){
15158 * Adds a separator bar to the menu
15159 * @return {Roo.menu.Item} The menu item that was added
15161 addSeparator : function(){
15162 return this.addItem(new Roo.menu.Separator());
15166 * Adds an {@link Roo.Element} object to the menu
15167 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15168 * @return {Roo.menu.Item} The menu item that was added
15170 addElement : function(el){
15171 return this.addItem(new Roo.menu.BaseItem(el));
15175 * Adds an existing object based on {@link Roo.menu.Item} to the menu
15176 * @param {Roo.menu.Item} item The menu item to add
15177 * @return {Roo.menu.Item} The menu item that was added
15179 addItem : function(item){
15180 this.items.add(item);
15182 var li = document.createElement("li");
15183 li.className = "x-menu-list-item";
15184 this.ul.dom.appendChild(li);
15185 item.render(li, this);
15186 this.delayAutoWidth();
15192 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15193 * @param {Object} config A MenuItem config object
15194 * @return {Roo.menu.Item} The menu item that was added
15196 addMenuItem : function(config){
15197 if(!(config instanceof Roo.menu.Item)){
15198 if(typeof config.checked == "boolean"){ // must be check menu item config?
15199 config = new Roo.menu.CheckItem(config);
15201 config = new Roo.menu.Item(config);
15204 return this.addItem(config);
15208 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15209 * @param {String} text The text to display in the menu item
15210 * @return {Roo.menu.Item} The menu item that was added
15212 addText : function(text){
15213 return this.addItem(new Roo.menu.TextItem({ text : text }));
15217 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15218 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15219 * @param {Roo.menu.Item} item The menu item to add
15220 * @return {Roo.menu.Item} The menu item that was added
15222 insert : function(index, item){
15223 this.items.insert(index, item);
15225 var li = document.createElement("li");
15226 li.className = "x-menu-list-item";
15227 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15228 item.render(li, this);
15229 this.delayAutoWidth();
15235 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15236 * @param {Roo.menu.Item} item The menu item to remove
15238 remove : function(item){
15239 this.items.removeKey(item.id);
15244 * Removes and destroys all items in the menu
15246 removeAll : function(){
15248 while(f = this.items.first()){
15254 // MenuNav is a private utility class used internally by the Menu
15255 Roo.menu.MenuNav = function(menu){
15256 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15257 this.scope = this.menu = menu;
15260 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15261 doRelay : function(e, h){
15262 var k = e.getKey();
15263 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15264 this.menu.tryActivate(0, 1);
15267 return h.call(this.scope || this, e, this.menu);
15270 up : function(e, m){
15271 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15272 m.tryActivate(m.items.length-1, -1);
15276 down : function(e, m){
15277 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15278 m.tryActivate(0, 1);
15282 right : function(e, m){
15284 m.activeItem.expandMenu(true);
15288 left : function(e, m){
15290 if(m.parentMenu && m.parentMenu.activeItem){
15291 m.parentMenu.activeItem.activate();
15295 enter : function(e, m){
15297 e.stopPropagation();
15298 m.activeItem.onClick(e);
15299 m.fireEvent("click", this, m.activeItem);
15305 * Ext JS Library 1.1.1
15306 * Copyright(c) 2006-2007, Ext JS, LLC.
15308 * Originally Released Under LGPL - original licence link has changed is not relivant.
15311 * <script type="text/javascript">
15315 * @class Roo.menu.MenuMgr
15316 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15319 Roo.menu.MenuMgr = function(){
15320 var menus, active, groups = {}, attached = false, lastShow = new Date();
15322 // private - called when first menu is created
15325 active = new Roo.util.MixedCollection();
15326 Roo.get(document).addKeyListener(27, function(){
15327 if(active.length > 0){
15334 function hideAll(){
15335 if(active && active.length > 0){
15336 var c = active.clone();
15337 c.each(function(m){
15344 function onHide(m){
15346 if(active.length < 1){
15347 Roo.get(document).un("mousedown", onMouseDown);
15353 function onShow(m){
15354 var last = active.last();
15355 lastShow = new Date();
15358 Roo.get(document).on("mousedown", onMouseDown);
15362 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15363 m.parentMenu.activeChild = m;
15364 }else if(last && last.isVisible()){
15365 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15370 function onBeforeHide(m){
15372 m.activeChild.hide();
15374 if(m.autoHideTimer){
15375 clearTimeout(m.autoHideTimer);
15376 delete m.autoHideTimer;
15381 function onBeforeShow(m){
15382 var pm = m.parentMenu;
15383 if(!pm && !m.allowOtherMenus){
15385 }else if(pm && pm.activeChild && active != m){
15386 pm.activeChild.hide();
15391 function onMouseDown(e){
15392 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15398 function onBeforeCheck(mi, state){
15400 var g = groups[mi.group];
15401 for(var i = 0, l = g.length; i < l; i++){
15403 g[i].setChecked(false);
15412 * Hides all menus that are currently visible
15414 hideAll : function(){
15419 register : function(menu){
15423 menus[menu.id] = menu;
15424 menu.on("beforehide", onBeforeHide);
15425 menu.on("hide", onHide);
15426 menu.on("beforeshow", onBeforeShow);
15427 menu.on("show", onShow);
15428 var g = menu.group;
15429 if(g && menu.events["checkchange"]){
15433 groups[g].push(menu);
15434 menu.on("checkchange", onCheck);
15439 * Returns a {@link Roo.menu.Menu} object
15440 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15441 * be used to generate and return a new Menu instance.
15443 get : function(menu){
15444 if(typeof menu == "string"){ // menu id
15445 return menus[menu];
15446 }else if(menu.events){ // menu instance
15448 }else if(typeof menu.length == 'number'){ // array of menu items?
15449 return new Roo.menu.Menu({items:menu});
15450 }else{ // otherwise, must be a config
15451 return new Roo.menu.Menu(menu);
15456 unregister : function(menu){
15457 delete menus[menu.id];
15458 menu.un("beforehide", onBeforeHide);
15459 menu.un("hide", onHide);
15460 menu.un("beforeshow", onBeforeShow);
15461 menu.un("show", onShow);
15462 var g = menu.group;
15463 if(g && menu.events["checkchange"]){
15464 groups[g].remove(menu);
15465 menu.un("checkchange", onCheck);
15470 registerCheckable : function(menuItem){
15471 var g = menuItem.group;
15476 groups[g].push(menuItem);
15477 menuItem.on("beforecheckchange", onBeforeCheck);
15482 unregisterCheckable : function(menuItem){
15483 var g = menuItem.group;
15485 groups[g].remove(menuItem);
15486 menuItem.un("beforecheckchange", onBeforeCheck);
15492 * Ext JS Library 1.1.1
15493 * Copyright(c) 2006-2007, Ext JS, LLC.
15495 * Originally Released Under LGPL - original licence link has changed is not relivant.
15498 * <script type="text/javascript">
15503 * @class Roo.menu.BaseItem
15504 * @extends Roo.Component
15505 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15506 * management and base configuration options shared by all menu components.
15508 * Creates a new BaseItem
15509 * @param {Object} config Configuration options
15511 Roo.menu.BaseItem = function(config){
15512 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15517 * Fires when this item is clicked
15518 * @param {Roo.menu.BaseItem} this
15519 * @param {Roo.EventObject} e
15524 * Fires when this item is activated
15525 * @param {Roo.menu.BaseItem} this
15529 * @event deactivate
15530 * Fires when this item is deactivated
15531 * @param {Roo.menu.BaseItem} this
15537 this.on("click", this.handler, this.scope, true);
15541 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15543 * @cfg {Function} handler
15544 * A function that will handle the click event of this menu item (defaults to undefined)
15547 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15549 canActivate : false,
15552 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15557 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15559 activeClass : "x-menu-item-active",
15561 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15563 hideOnClick : true,
15565 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15570 ctype: "Roo.menu.BaseItem",
15573 actionMode : "container",
15576 render : function(container, parentMenu){
15577 this.parentMenu = parentMenu;
15578 Roo.menu.BaseItem.superclass.render.call(this, container);
15579 this.container.menuItemId = this.id;
15583 onRender : function(container, position){
15584 this.el = Roo.get(this.el);
15585 container.dom.appendChild(this.el.dom);
15589 onClick : function(e){
15590 if(!this.disabled && this.fireEvent("click", this, e) !== false
15591 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15592 this.handleClick(e);
15599 activate : function(){
15603 var li = this.container;
15604 li.addClass(this.activeClass);
15605 this.region = li.getRegion().adjust(2, 2, -2, -2);
15606 this.fireEvent("activate", this);
15611 deactivate : function(){
15612 this.container.removeClass(this.activeClass);
15613 this.fireEvent("deactivate", this);
15617 shouldDeactivate : function(e){
15618 return !this.region || !this.region.contains(e.getPoint());
15622 handleClick : function(e){
15623 if(this.hideOnClick){
15624 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15629 expandMenu : function(autoActivate){
15634 hideMenu : function(){
15639 * Ext JS Library 1.1.1
15640 * Copyright(c) 2006-2007, Ext JS, LLC.
15642 * Originally Released Under LGPL - original licence link has changed is not relivant.
15645 * <script type="text/javascript">
15649 * @class Roo.menu.Adapter
15650 * @extends Roo.menu.BaseItem
15651 * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15652 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15654 * Creates a new Adapter
15655 * @param {Object} config Configuration options
15657 Roo.menu.Adapter = function(component, config){
15658 Roo.menu.Adapter.superclass.constructor.call(this, config);
15659 this.component = component;
15661 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15663 canActivate : true,
15666 onRender : function(container, position){
15667 this.component.render(container);
15668 this.el = this.component.getEl();
15672 activate : function(){
15676 this.component.focus();
15677 this.fireEvent("activate", this);
15682 deactivate : function(){
15683 this.fireEvent("deactivate", this);
15687 disable : function(){
15688 this.component.disable();
15689 Roo.menu.Adapter.superclass.disable.call(this);
15693 enable : function(){
15694 this.component.enable();
15695 Roo.menu.Adapter.superclass.enable.call(this);
15699 * Ext JS Library 1.1.1
15700 * Copyright(c) 2006-2007, Ext JS, LLC.
15702 * Originally Released Under LGPL - original licence link has changed is not relivant.
15705 * <script type="text/javascript">
15709 * @class Roo.menu.TextItem
15710 * @extends Roo.menu.BaseItem
15711 * Adds a static text string to a menu, usually used as either a heading or group separator.
15712 * Note: old style constructor with text is still supported.
15715 * Creates a new TextItem
15716 * @param {Object} cfg Configuration
15718 Roo.menu.TextItem = function(cfg){
15719 if (typeof(cfg) == 'string') {
15722 Roo.apply(this,cfg);
15725 Roo.menu.TextItem.superclass.constructor.call(this);
15728 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15730 * @cfg {Boolean} text Text to show on item.
15735 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15737 hideOnClick : false,
15739 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15741 itemCls : "x-menu-text",
15744 onRender : function(){
15745 var s = document.createElement("span");
15746 s.className = this.itemCls;
15747 s.innerHTML = this.text;
15749 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15753 * Ext JS Library 1.1.1
15754 * Copyright(c) 2006-2007, Ext JS, LLC.
15756 * Originally Released Under LGPL - original licence link has changed is not relivant.
15759 * <script type="text/javascript">
15763 * @class Roo.menu.Separator
15764 * @extends Roo.menu.BaseItem
15765 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15766 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15768 * @param {Object} config Configuration options
15770 Roo.menu.Separator = function(config){
15771 Roo.menu.Separator.superclass.constructor.call(this, config);
15774 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15776 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15778 itemCls : "x-menu-sep",
15780 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15782 hideOnClick : false,
15785 onRender : function(li){
15786 var s = document.createElement("span");
15787 s.className = this.itemCls;
15788 s.innerHTML = " ";
15790 li.addClass("x-menu-sep-li");
15791 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15795 * Ext JS Library 1.1.1
15796 * Copyright(c) 2006-2007, Ext JS, LLC.
15798 * Originally Released Under LGPL - original licence link has changed is not relivant.
15801 * <script type="text/javascript">
15804 * @class Roo.menu.Item
15805 * @extends Roo.menu.BaseItem
15806 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15807 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15808 * activation and click handling.
15810 * Creates a new Item
15811 * @param {Object} config Configuration options
15813 Roo.menu.Item = function(config){
15814 Roo.menu.Item.superclass.constructor.call(this, config);
15816 this.menu = Roo.menu.MenuMgr.get(this.menu);
15819 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15822 * @cfg {String} text
15823 * The text to show on the menu item.
15827 * @cfg {String} HTML to render in menu
15828 * The text to show on the menu item (HTML version).
15832 * @cfg {String} icon
15833 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15837 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15839 itemCls : "x-menu-item",
15841 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15843 canActivate : true,
15845 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15848 // doc'd in BaseItem
15852 ctype: "Roo.menu.Item",
15855 onRender : function(container, position){
15856 var el = document.createElement("a");
15857 el.hideFocus = true;
15858 el.unselectable = "on";
15859 el.href = this.href || "#";
15860 if(this.hrefTarget){
15861 el.target = this.hrefTarget;
15863 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15865 var html = this.html.length ? this.html : String.format('{0}',this.text);
15867 el.innerHTML = String.format(
15868 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15869 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15871 Roo.menu.Item.superclass.onRender.call(this, container, position);
15875 * Sets the text to display in this menu item
15876 * @param {String} text The text to display
15877 * @param {Boolean} isHTML true to indicate text is pure html.
15879 setText : function(text, isHTML){
15887 var html = this.html.length ? this.html : String.format('{0}',this.text);
15889 this.el.update(String.format(
15890 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15891 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15892 this.parentMenu.autoWidth();
15897 handleClick : function(e){
15898 if(!this.href){ // if no link defined, stop the event automatically
15901 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15905 activate : function(autoExpand){
15906 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15916 shouldDeactivate : function(e){
15917 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15918 if(this.menu && this.menu.isVisible()){
15919 return !this.menu.getEl().getRegion().contains(e.getPoint());
15927 deactivate : function(){
15928 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15933 expandMenu : function(autoActivate){
15934 if(!this.disabled && this.menu){
15935 clearTimeout(this.hideTimer);
15936 delete this.hideTimer;
15937 if(!this.menu.isVisible() && !this.showTimer){
15938 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15939 }else if (this.menu.isVisible() && autoActivate){
15940 this.menu.tryActivate(0, 1);
15946 deferExpand : function(autoActivate){
15947 delete this.showTimer;
15948 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15950 this.menu.tryActivate(0, 1);
15955 hideMenu : function(){
15956 clearTimeout(this.showTimer);
15957 delete this.showTimer;
15958 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15959 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15964 deferHide : function(){
15965 delete this.hideTimer;
15970 * Ext JS Library 1.1.1
15971 * Copyright(c) 2006-2007, Ext JS, LLC.
15973 * Originally Released Under LGPL - original licence link has changed is not relivant.
15976 * <script type="text/javascript">
15980 * @class Roo.menu.CheckItem
15981 * @extends Roo.menu.Item
15982 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15984 * Creates a new CheckItem
15985 * @param {Object} config Configuration options
15987 Roo.menu.CheckItem = function(config){
15988 Roo.menu.CheckItem.superclass.constructor.call(this, config);
15991 * @event beforecheckchange
15992 * Fires before the checked value is set, providing an opportunity to cancel if needed
15993 * @param {Roo.menu.CheckItem} this
15994 * @param {Boolean} checked The new checked value that will be set
15996 "beforecheckchange" : true,
15998 * @event checkchange
15999 * Fires after the checked value has been set
16000 * @param {Roo.menu.CheckItem} this
16001 * @param {Boolean} checked The checked value that was set
16003 "checkchange" : true
16005 if(this.checkHandler){
16006 this.on('checkchange', this.checkHandler, this.scope);
16009 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16011 * @cfg {String} group
16012 * All check items with the same group name will automatically be grouped into a single-select
16013 * radio button group (defaults to '')
16016 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16018 itemCls : "x-menu-item x-menu-check-item",
16020 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16022 groupClass : "x-menu-group-item",
16025 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
16026 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16027 * initialized with checked = true will be rendered as checked.
16032 ctype: "Roo.menu.CheckItem",
16035 onRender : function(c){
16036 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16038 this.el.addClass(this.groupClass);
16040 Roo.menu.MenuMgr.registerCheckable(this);
16042 this.checked = false;
16043 this.setChecked(true, true);
16048 destroy : function(){
16050 Roo.menu.MenuMgr.unregisterCheckable(this);
16052 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16056 * Set the checked state of this item
16057 * @param {Boolean} checked The new checked value
16058 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16060 setChecked : function(state, suppressEvent){
16061 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16062 if(this.container){
16063 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16065 this.checked = state;
16066 if(suppressEvent !== true){
16067 this.fireEvent("checkchange", this, state);
16073 handleClick : function(e){
16074 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16075 this.setChecked(!this.checked);
16077 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16081 * Ext JS Library 1.1.1
16082 * Copyright(c) 2006-2007, Ext JS, LLC.
16084 * Originally Released Under LGPL - original licence link has changed is not relivant.
16087 * <script type="text/javascript">
16091 * @class Roo.menu.DateItem
16092 * @extends Roo.menu.Adapter
16093 * A menu item that wraps the {@link Roo.DatPicker} component.
16095 * Creates a new DateItem
16096 * @param {Object} config Configuration options
16098 Roo.menu.DateItem = function(config){
16099 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16100 /** The Roo.DatePicker object @type Roo.DatePicker */
16101 this.picker = this.component;
16102 this.addEvents({select: true});
16104 this.picker.on("render", function(picker){
16105 picker.getEl().swallowEvent("click");
16106 picker.container.addClass("x-menu-date-item");
16109 this.picker.on("select", this.onSelect, this);
16112 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16114 onSelect : function(picker, date){
16115 this.fireEvent("select", this, date, picker);
16116 Roo.menu.DateItem.superclass.handleClick.call(this);
16120 * Ext JS Library 1.1.1
16121 * Copyright(c) 2006-2007, Ext JS, LLC.
16123 * Originally Released Under LGPL - original licence link has changed is not relivant.
16126 * <script type="text/javascript">
16130 * @class Roo.menu.ColorItem
16131 * @extends Roo.menu.Adapter
16132 * A menu item that wraps the {@link Roo.ColorPalette} component.
16134 * Creates a new ColorItem
16135 * @param {Object} config Configuration options
16137 Roo.menu.ColorItem = function(config){
16138 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16139 /** The Roo.ColorPalette object @type Roo.ColorPalette */
16140 this.palette = this.component;
16141 this.relayEvents(this.palette, ["select"]);
16142 if(this.selectHandler){
16143 this.on('select', this.selectHandler, this.scope);
16146 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16148 * Ext JS Library 1.1.1
16149 * Copyright(c) 2006-2007, Ext JS, LLC.
16151 * Originally Released Under LGPL - original licence link has changed is not relivant.
16154 * <script type="text/javascript">
16159 * @class Roo.menu.DateMenu
16160 * @extends Roo.menu.Menu
16161 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16163 * Creates a new DateMenu
16164 * @param {Object} config Configuration options
16166 Roo.menu.DateMenu = function(config){
16167 Roo.menu.DateMenu.superclass.constructor.call(this, config);
16169 var di = new Roo.menu.DateItem(config);
16172 * The {@link Roo.DatePicker} instance for this DateMenu
16175 this.picker = di.picker;
16178 * @param {DatePicker} picker
16179 * @param {Date} date
16181 this.relayEvents(di, ["select"]);
16182 this.on('beforeshow', function(){
16184 this.picker.hideMonthPicker(false);
16188 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16192 * Ext JS Library 1.1.1
16193 * Copyright(c) 2006-2007, Ext JS, LLC.
16195 * Originally Released Under LGPL - original licence link has changed is not relivant.
16198 * <script type="text/javascript">
16203 * @class Roo.menu.ColorMenu
16204 * @extends Roo.menu.Menu
16205 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16207 * Creates a new ColorMenu
16208 * @param {Object} config Configuration options
16210 Roo.menu.ColorMenu = function(config){
16211 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16213 var ci = new Roo.menu.ColorItem(config);
16216 * The {@link Roo.ColorPalette} instance for this ColorMenu
16217 * @type ColorPalette
16219 this.palette = ci.palette;
16222 * @param {ColorPalette} palette
16223 * @param {String} color
16225 this.relayEvents(ci, ["select"]);
16227 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16229 * Ext JS Library 1.1.1
16230 * Copyright(c) 2006-2007, Ext JS, LLC.
16232 * Originally Released Under LGPL - original licence link has changed is not relivant.
16235 * <script type="text/javascript">
16239 * @class Roo.form.Field
16240 * @extends Roo.BoxComponent
16241 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16243 * Creates a new Field
16244 * @param {Object} config Configuration options
16246 Roo.form.Field = function(config){
16247 Roo.form.Field.superclass.constructor.call(this, config);
16250 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16252 * @cfg {String} fieldLabel Label to use when rendering a form.
16255 * @cfg {String} qtip Mouse over tip
16259 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16261 invalidClass : "x-form-invalid",
16263 * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
16265 invalidText : "The value in this field is invalid",
16267 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16269 focusClass : "x-form-focus",
16271 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16272 automatic validation (defaults to "keyup").
16274 validationEvent : "keyup",
16276 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16278 validateOnBlur : true,
16280 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16282 validationDelay : 250,
16284 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16285 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16287 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16289 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16291 fieldClass : "x-form-field",
16293 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16296 ----------- ----------------------------------------------------------------------
16297 qtip Display a quick tip when the user hovers over the field
16298 title Display a default browser title attribute popup
16299 under Add a block div beneath the field containing the error text
16300 side Add an error icon to the right of the field with a popup on hover
16301 [element id] Add the error text directly to the innerHTML of the specified element
16304 msgTarget : 'qtip',
16306 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16311 * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16316 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16321 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16323 inputType : undefined,
16326 * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16328 tabIndex : undefined,
16331 isFormField : true,
16336 * @property {Roo.Element} fieldEl
16337 * Element Containing the rendered Field (with label etc.)
16340 * @cfg {Mixed} value A value to initialize this field with.
16345 * @cfg {String} name The field's HTML name attribute.
16348 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16351 loadedValue : false,
16355 initComponent : function(){
16356 Roo.form.Field.superclass.initComponent.call(this);
16360 * Fires when this field receives input focus.
16361 * @param {Roo.form.Field} this
16366 * Fires when this field loses input focus.
16367 * @param {Roo.form.Field} this
16371 * @event specialkey
16372 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16373 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16374 * @param {Roo.form.Field} this
16375 * @param {Roo.EventObject} e The event object
16380 * Fires just before the field blurs if the field value has changed.
16381 * @param {Roo.form.Field} this
16382 * @param {Mixed} newValue The new value
16383 * @param {Mixed} oldValue The original value
16388 * Fires after the field has been marked as invalid.
16389 * @param {Roo.form.Field} this
16390 * @param {String} msg The validation message
16395 * Fires after the field has been validated with no errors.
16396 * @param {Roo.form.Field} this
16401 * Fires after the key up
16402 * @param {Roo.form.Field} this
16403 * @param {Roo.EventObject} e The event Object
16410 * Returns the name attribute of the field if available
16411 * @return {String} name The field name
16413 getName: function(){
16414 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16418 onRender : function(ct, position){
16419 Roo.form.Field.superclass.onRender.call(this, ct, position);
16421 var cfg = this.getAutoCreate();
16423 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16425 if (!cfg.name.length) {
16428 if(this.inputType){
16429 cfg.type = this.inputType;
16431 this.el = ct.createChild(cfg, position);
16433 var type = this.el.dom.type;
16435 if(type == 'password'){
16438 this.el.addClass('x-form-'+type);
16441 this.el.dom.readOnly = true;
16443 if(this.tabIndex !== undefined){
16444 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16447 this.el.addClass([this.fieldClass, this.cls]);
16452 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16453 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16454 * @return {Roo.form.Field} this
16456 applyTo : function(target){
16457 this.allowDomMove = false;
16458 this.el = Roo.get(target);
16459 this.render(this.el.dom.parentNode);
16464 initValue : function(){
16465 if(this.value !== undefined){
16466 this.setValue(this.value);
16467 }else if(this.el.dom.value.length > 0){
16468 this.setValue(this.el.dom.value);
16473 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16474 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16476 isDirty : function() {
16477 if(this.disabled) {
16480 return String(this.getValue()) !== String(this.originalValue);
16484 * stores the current value in loadedValue
16486 resetHasChanged : function()
16488 this.loadedValue = String(this.getValue());
16491 * checks the current value against the 'loaded' value.
16492 * Note - will return false if 'resetHasChanged' has not been called first.
16494 hasChanged : function()
16496 if(this.disabled || this.readOnly) {
16499 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16505 afterRender : function(){
16506 Roo.form.Field.superclass.afterRender.call(this);
16511 fireKey : function(e){
16512 //Roo.log('field ' + e.getKey());
16513 if(e.isNavKeyPress()){
16514 this.fireEvent("specialkey", this, e);
16519 * Resets the current field value to the originally loaded value and clears any validation messages
16521 reset : function(){
16522 this.setValue(this.resetValue);
16523 this.clearInvalid();
16527 initEvents : function(){
16528 // safari killled keypress - so keydown is now used..
16529 this.el.on("keydown" , this.fireKey, this);
16530 this.el.on("focus", this.onFocus, this);
16531 this.el.on("blur", this.onBlur, this);
16532 this.el.relayEvent('keyup', this);
16534 // reference to original value for reset
16535 this.originalValue = this.getValue();
16536 this.resetValue = this.getValue();
16540 onFocus : function(){
16541 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16542 this.el.addClass(this.focusClass);
16544 if(!this.hasFocus){
16545 this.hasFocus = true;
16546 this.startValue = this.getValue();
16547 this.fireEvent("focus", this);
16551 beforeBlur : Roo.emptyFn,
16554 onBlur : function(){
16556 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16557 this.el.removeClass(this.focusClass);
16559 this.hasFocus = false;
16560 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16563 var v = this.getValue();
16564 if(String(v) !== String(this.startValue)){
16565 this.fireEvent('change', this, v, this.startValue);
16567 this.fireEvent("blur", this);
16571 * Returns whether or not the field value is currently valid
16572 * @param {Boolean} preventMark True to disable marking the field invalid
16573 * @return {Boolean} True if the value is valid, else false
16575 isValid : function(preventMark){
16579 var restore = this.preventMark;
16580 this.preventMark = preventMark === true;
16581 var v = this.validateValue(this.processValue(this.getRawValue()));
16582 this.preventMark = restore;
16587 * Validates the field value
16588 * @return {Boolean} True if the value is valid, else false
16590 validate : function(){
16591 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16592 this.clearInvalid();
16598 processValue : function(value){
16603 // Subclasses should provide the validation implementation by overriding this
16604 validateValue : function(value){
16609 * Mark this field as invalid
16610 * @param {String} msg The validation message
16612 markInvalid : function(msg){
16613 if(!this.rendered || this.preventMark){ // not rendered
16617 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16619 obj.el.addClass(this.invalidClass);
16620 msg = msg || this.invalidText;
16621 switch(this.msgTarget){
16623 obj.el.dom.qtip = msg;
16624 obj.el.dom.qclass = 'x-form-invalid-tip';
16625 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16626 Roo.QuickTips.enable();
16630 this.el.dom.title = msg;
16634 var elp = this.el.findParent('.x-form-element', 5, true);
16635 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16636 this.errorEl.setWidth(elp.getWidth(true)-20);
16638 this.errorEl.update(msg);
16639 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16642 if(!this.errorIcon){
16643 var elp = this.el.findParent('.x-form-element', 5, true);
16644 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16646 this.alignErrorIcon();
16647 this.errorIcon.dom.qtip = msg;
16648 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16649 this.errorIcon.show();
16650 this.on('resize', this.alignErrorIcon, this);
16653 var t = Roo.getDom(this.msgTarget);
16655 t.style.display = this.msgDisplay;
16658 this.fireEvent('invalid', this, msg);
16662 alignErrorIcon : function(){
16663 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16667 * Clear any invalid styles/messages for this field
16669 clearInvalid : function(){
16670 if(!this.rendered || this.preventMark){ // not rendered
16673 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16675 obj.el.removeClass(this.invalidClass);
16676 switch(this.msgTarget){
16678 obj.el.dom.qtip = '';
16681 this.el.dom.title = '';
16685 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16689 if(this.errorIcon){
16690 this.errorIcon.dom.qtip = '';
16691 this.errorIcon.hide();
16692 this.un('resize', this.alignErrorIcon, this);
16696 var t = Roo.getDom(this.msgTarget);
16698 t.style.display = 'none';
16701 this.fireEvent('valid', this);
16705 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16706 * @return {Mixed} value The field value
16708 getRawValue : function(){
16709 var v = this.el.getValue();
16715 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16716 * @return {Mixed} value The field value
16718 getValue : function(){
16719 var v = this.el.getValue();
16725 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16726 * @param {Mixed} value The value to set
16728 setRawValue : function(v){
16729 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16733 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16734 * @param {Mixed} value The value to set
16736 setValue : function(v){
16739 this.el.dom.value = (v === null || v === undefined ? '' : v);
16744 adjustSize : function(w, h){
16745 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16746 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16750 adjustWidth : function(tag, w){
16751 tag = tag.toLowerCase();
16752 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16753 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16754 if(tag == 'input'){
16757 if(tag == 'textarea'){
16760 }else if(Roo.isOpera){
16761 if(tag == 'input'){
16764 if(tag == 'textarea'){
16774 // anything other than normal should be considered experimental
16775 Roo.form.Field.msgFx = {
16777 show: function(msgEl, f){
16778 msgEl.setDisplayed('block');
16781 hide : function(msgEl, f){
16782 msgEl.setDisplayed(false).update('');
16787 show: function(msgEl, f){
16788 msgEl.slideIn('t', {stopFx:true});
16791 hide : function(msgEl, f){
16792 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16797 show: function(msgEl, f){
16798 msgEl.fixDisplay();
16799 msgEl.alignTo(f.el, 'tl-tr');
16800 msgEl.slideIn('l', {stopFx:true});
16803 hide : function(msgEl, f){
16804 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16809 * Ext JS Library 1.1.1
16810 * Copyright(c) 2006-2007, Ext JS, LLC.
16812 * Originally Released Under LGPL - original licence link has changed is not relivant.
16815 * <script type="text/javascript">
16820 * @class Roo.form.TextField
16821 * @extends Roo.form.Field
16822 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16823 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16825 * Creates a new TextField
16826 * @param {Object} config Configuration options
16828 Roo.form.TextField = function(config){
16829 Roo.form.TextField.superclass.constructor.call(this, config);
16833 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16834 * according to the default logic, but this event provides a hook for the developer to apply additional
16835 * logic at runtime to resize the field if needed.
16836 * @param {Roo.form.Field} this This text field
16837 * @param {Number} width The new field width
16843 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16845 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16849 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16853 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16857 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16861 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16865 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16867 disableKeyFilter : false,
16869 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16873 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16877 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16879 maxLength : Number.MAX_VALUE,
16881 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16883 minLengthText : "The minimum length for this field is {0}",
16885 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16887 maxLengthText : "The maximum length for this field is {0}",
16889 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16891 selectOnFocus : false,
16893 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16895 blankText : "This field is required",
16897 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16898 * If available, this function will be called only after the basic validators all return true, and will be passed the
16899 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16903 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16904 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16905 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
16909 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16913 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16919 initEvents : function()
16921 if (this.emptyText) {
16922 this.el.attr('placeholder', this.emptyText);
16925 Roo.form.TextField.superclass.initEvents.call(this);
16926 if(this.validationEvent == 'keyup'){
16927 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16928 this.el.on('keyup', this.filterValidation, this);
16930 else if(this.validationEvent !== false){
16931 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16934 if(this.selectOnFocus){
16935 this.on("focus", this.preFocus, this);
16938 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16939 this.el.on("keypress", this.filterKeys, this);
16942 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
16943 this.el.on("click", this.autoSize, this);
16945 if(this.el.is('input[type=password]') && Roo.isSafari){
16946 this.el.on('keydown', this.SafariOnKeyDown, this);
16950 processValue : function(value){
16951 if(this.stripCharsRe){
16952 var newValue = value.replace(this.stripCharsRe, '');
16953 if(newValue !== value){
16954 this.setRawValue(newValue);
16961 filterValidation : function(e){
16962 if(!e.isNavKeyPress()){
16963 this.validationTask.delay(this.validationDelay);
16968 onKeyUp : function(e){
16969 if(!e.isNavKeyPress()){
16975 * Resets the current field value to the originally-loaded value and clears any validation messages.
16978 reset : function(){
16979 Roo.form.TextField.superclass.reset.call(this);
16985 preFocus : function(){
16987 if(this.selectOnFocus){
16988 this.el.dom.select();
16994 filterKeys : function(e){
16995 var k = e.getKey();
16996 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16999 var c = e.getCharCode(), cc = String.fromCharCode(c);
17000 if(Roo.isIE && (e.isSpecialKey() || !cc)){
17003 if(!this.maskRe.test(cc)){
17008 setValue : function(v){
17010 Roo.form.TextField.superclass.setValue.apply(this, arguments);
17016 * Validates a value according to the field's validation rules and marks the field as invalid
17017 * if the validation fails
17018 * @param {Mixed} value The value to validate
17019 * @return {Boolean} True if the value is valid, else false
17021 validateValue : function(value){
17022 if(value.length < 1) { // if it's blank
17023 if(this.allowBlank){
17024 this.clearInvalid();
17027 this.markInvalid(this.blankText);
17031 if(value.length < this.minLength){
17032 this.markInvalid(String.format(this.minLengthText, this.minLength));
17035 if(value.length > this.maxLength){
17036 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17040 var vt = Roo.form.VTypes;
17041 if(!vt[this.vtype](value, this)){
17042 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17046 if(typeof this.validator == "function"){
17047 var msg = this.validator(value);
17049 this.markInvalid(msg);
17053 if(this.regex && !this.regex.test(value)){
17054 this.markInvalid(this.regexText);
17061 * Selects text in this field
17062 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17063 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17065 selectText : function(start, end){
17066 var v = this.getRawValue();
17068 start = start === undefined ? 0 : start;
17069 end = end === undefined ? v.length : end;
17070 var d = this.el.dom;
17071 if(d.setSelectionRange){
17072 d.setSelectionRange(start, end);
17073 }else if(d.createTextRange){
17074 var range = d.createTextRange();
17075 range.moveStart("character", start);
17076 range.moveEnd("character", v.length-end);
17083 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17084 * This only takes effect if grow = true, and fires the autosize event.
17086 autoSize : function(){
17087 if(!this.grow || !this.rendered){
17091 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17094 var v = el.dom.value;
17095 var d = document.createElement('div');
17096 d.appendChild(document.createTextNode(v));
17100 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17101 this.el.setWidth(w);
17102 this.fireEvent("autosize", this, w);
17106 SafariOnKeyDown : function(event)
17108 // this is a workaround for a password hang bug on chrome/ webkit.
17110 var isSelectAll = false;
17112 if(this.el.dom.selectionEnd > 0){
17113 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17115 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17116 event.preventDefault();
17121 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17123 event.preventDefault();
17124 // this is very hacky as keydown always get's upper case.
17126 var cc = String.fromCharCode(event.getCharCode());
17129 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
17137 * Ext JS Library 1.1.1
17138 * Copyright(c) 2006-2007, Ext JS, LLC.
17140 * Originally Released Under LGPL - original licence link has changed is not relivant.
17143 * <script type="text/javascript">
17147 * @class Roo.form.Hidden
17148 * @extends Roo.form.TextField
17149 * Simple Hidden element used on forms
17151 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17154 * Creates a new Hidden form element.
17155 * @param {Object} config Configuration options
17160 // easy hidden field...
17161 Roo.form.Hidden = function(config){
17162 Roo.form.Hidden.superclass.constructor.call(this, config);
17165 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17167 inputType: 'hidden',
17170 labelSeparator: '',
17172 itemCls : 'x-form-item-display-none'
17180 * Ext JS Library 1.1.1
17181 * Copyright(c) 2006-2007, Ext JS, LLC.
17183 * Originally Released Under LGPL - original licence link has changed is not relivant.
17186 * <script type="text/javascript">
17190 * @class Roo.form.TriggerField
17191 * @extends Roo.form.TextField
17192 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17193 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17194 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17195 * for which you can provide a custom implementation. For example:
17197 var trigger = new Roo.form.TriggerField();
17198 trigger.onTriggerClick = myTriggerFn;
17199 trigger.applyTo('my-field');
17202 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17203 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17204 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17205 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17207 * Create a new TriggerField.
17208 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17209 * to the base TextField)
17211 Roo.form.TriggerField = function(config){
17212 this.mimicing = false;
17213 Roo.form.TriggerField.superclass.constructor.call(this, config);
17216 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17218 * @cfg {String} triggerClass A CSS class to apply to the trigger
17221 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17222 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17224 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17226 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17230 /** @cfg {Boolean} grow @hide */
17231 /** @cfg {Number} growMin @hide */
17232 /** @cfg {Number} growMax @hide */
17238 autoSize: Roo.emptyFn,
17242 deferHeight : true,
17245 actionMode : 'wrap',
17247 onResize : function(w, h){
17248 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17249 if(typeof w == 'number'){
17250 var x = w - this.trigger.getWidth();
17251 this.el.setWidth(this.adjustWidth('input', x));
17252 this.trigger.setStyle('left', x+'px');
17257 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17260 getResizeEl : function(){
17265 getPositionEl : function(){
17270 alignErrorIcon : function(){
17271 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17275 onRender : function(ct, position){
17276 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17277 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17278 this.trigger = this.wrap.createChild(this.triggerConfig ||
17279 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17280 if(this.hideTrigger){
17281 this.trigger.setDisplayed(false);
17283 this.initTrigger();
17285 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17290 initTrigger : function(){
17291 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17292 this.trigger.addClassOnOver('x-form-trigger-over');
17293 this.trigger.addClassOnClick('x-form-trigger-click');
17297 onDestroy : function(){
17299 this.trigger.removeAllListeners();
17300 this.trigger.remove();
17303 this.wrap.remove();
17305 Roo.form.TriggerField.superclass.onDestroy.call(this);
17309 onFocus : function(){
17310 Roo.form.TriggerField.superclass.onFocus.call(this);
17311 if(!this.mimicing){
17312 this.wrap.addClass('x-trigger-wrap-focus');
17313 this.mimicing = true;
17314 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17315 if(this.monitorTab){
17316 this.el.on("keydown", this.checkTab, this);
17322 checkTab : function(e){
17323 if(e.getKey() == e.TAB){
17324 this.triggerBlur();
17329 onBlur : function(){
17334 mimicBlur : function(e, t){
17335 if(!this.wrap.contains(t) && this.validateBlur()){
17336 this.triggerBlur();
17341 triggerBlur : function(){
17342 this.mimicing = false;
17343 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17344 if(this.monitorTab){
17345 this.el.un("keydown", this.checkTab, this);
17347 this.wrap.removeClass('x-trigger-wrap-focus');
17348 Roo.form.TriggerField.superclass.onBlur.call(this);
17352 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17353 validateBlur : function(e, t){
17358 onDisable : function(){
17359 Roo.form.TriggerField.superclass.onDisable.call(this);
17361 this.wrap.addClass('x-item-disabled');
17366 onEnable : function(){
17367 Roo.form.TriggerField.superclass.onEnable.call(this);
17369 this.wrap.removeClass('x-item-disabled');
17374 onShow : function(){
17375 var ae = this.getActionEl();
17378 ae.dom.style.display = '';
17379 ae.dom.style.visibility = 'visible';
17385 onHide : function(){
17386 var ae = this.getActionEl();
17387 ae.dom.style.display = 'none';
17391 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17392 * by an implementing function.
17394 * @param {EventObject} e
17396 onTriggerClick : Roo.emptyFn
17399 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17400 // to be extended by an implementing class. For an example of implementing this class, see the custom
17401 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17402 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17403 initComponent : function(){
17404 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17406 this.triggerConfig = {
17407 tag:'span', cls:'x-form-twin-triggers', cn:[
17408 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17409 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17413 getTrigger : function(index){
17414 return this.triggers[index];
17417 initTrigger : function(){
17418 var ts = this.trigger.select('.x-form-trigger', true);
17419 this.wrap.setStyle('overflow', 'hidden');
17420 var triggerField = this;
17421 ts.each(function(t, all, index){
17422 t.hide = function(){
17423 var w = triggerField.wrap.getWidth();
17424 this.dom.style.display = 'none';
17425 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17427 t.show = function(){
17428 var w = triggerField.wrap.getWidth();
17429 this.dom.style.display = '';
17430 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17432 var triggerIndex = 'Trigger'+(index+1);
17434 if(this['hide'+triggerIndex]){
17435 t.dom.style.display = 'none';
17437 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17438 t.addClassOnOver('x-form-trigger-over');
17439 t.addClassOnClick('x-form-trigger-click');
17441 this.triggers = ts.elements;
17444 onTrigger1Click : Roo.emptyFn,
17445 onTrigger2Click : Roo.emptyFn
17448 * Ext JS Library 1.1.1
17449 * Copyright(c) 2006-2007, Ext JS, LLC.
17451 * Originally Released Under LGPL - original licence link has changed is not relivant.
17454 * <script type="text/javascript">
17458 * @class Roo.form.TextArea
17459 * @extends Roo.form.TextField
17460 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17461 * support for auto-sizing.
17463 * Creates a new TextArea
17464 * @param {Object} config Configuration options
17466 Roo.form.TextArea = function(config){
17467 Roo.form.TextArea.superclass.constructor.call(this, config);
17468 // these are provided exchanges for backwards compat
17469 // minHeight/maxHeight were replaced by growMin/growMax to be
17470 // compatible with TextField growing config values
17471 if(this.minHeight !== undefined){
17472 this.growMin = this.minHeight;
17474 if(this.maxHeight !== undefined){
17475 this.growMax = this.maxHeight;
17479 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17481 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17485 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17489 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17490 * in the field (equivalent to setting overflow: hidden, defaults to false)
17492 preventScrollbars: false,
17494 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17495 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17499 onRender : function(ct, position){
17501 this.defaultAutoCreate = {
17503 style:"width:300px;height:60px;",
17504 autocomplete: "new-password"
17507 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17509 this.textSizeEl = Roo.DomHelper.append(document.body, {
17510 tag: "pre", cls: "x-form-grow-sizer"
17512 if(this.preventScrollbars){
17513 this.el.setStyle("overflow", "hidden");
17515 this.el.setHeight(this.growMin);
17519 onDestroy : function(){
17520 if(this.textSizeEl){
17521 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17523 Roo.form.TextArea.superclass.onDestroy.call(this);
17527 onKeyUp : function(e){
17528 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17534 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17535 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17537 autoSize : function(){
17538 if(!this.grow || !this.textSizeEl){
17542 var v = el.dom.value;
17543 var ts = this.textSizeEl;
17546 ts.appendChild(document.createTextNode(v));
17549 Roo.fly(ts).setWidth(this.el.getWidth());
17551 v = "  ";
17554 v = v.replace(/\n/g, '<p> </p>');
17556 v += " \n ";
17559 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17560 if(h != this.lastHeight){
17561 this.lastHeight = h;
17562 this.el.setHeight(h);
17563 this.fireEvent("autosize", this, h);
17568 * Ext JS Library 1.1.1
17569 * Copyright(c) 2006-2007, Ext JS, LLC.
17571 * Originally Released Under LGPL - original licence link has changed is not relivant.
17574 * <script type="text/javascript">
17579 * @class Roo.form.NumberField
17580 * @extends Roo.form.TextField
17581 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17583 * Creates a new NumberField
17584 * @param {Object} config Configuration options
17586 Roo.form.NumberField = function(config){
17587 Roo.form.NumberField.superclass.constructor.call(this, config);
17590 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17592 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17594 fieldClass: "x-form-field x-form-num-field",
17596 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17598 allowDecimals : true,
17600 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17602 decimalSeparator : ".",
17604 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17606 decimalPrecision : 2,
17608 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17610 allowNegative : true,
17612 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17614 minValue : Number.NEGATIVE_INFINITY,
17616 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17618 maxValue : Number.MAX_VALUE,
17620 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17622 minText : "The minimum value for this field is {0}",
17624 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17626 maxText : "The maximum value for this field is {0}",
17628 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17629 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17631 nanText : "{0} is not a valid number",
17634 initEvents : function(){
17635 Roo.form.NumberField.superclass.initEvents.call(this);
17636 var allowed = "0123456789";
17637 if(this.allowDecimals){
17638 allowed += this.decimalSeparator;
17640 if(this.allowNegative){
17643 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17644 var keyPress = function(e){
17645 var k = e.getKey();
17646 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17649 var c = e.getCharCode();
17650 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17654 this.el.on("keypress", keyPress, this);
17658 validateValue : function(value){
17659 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17662 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17665 var num = this.parseValue(value);
17667 this.markInvalid(String.format(this.nanText, value));
17670 if(num < this.minValue){
17671 this.markInvalid(String.format(this.minText, this.minValue));
17674 if(num > this.maxValue){
17675 this.markInvalid(String.format(this.maxText, this.maxValue));
17681 getValue : function(){
17682 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17686 parseValue : function(value){
17687 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17688 return isNaN(value) ? '' : value;
17692 fixPrecision : function(value){
17693 var nan = isNaN(value);
17694 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17695 return nan ? '' : value;
17697 return parseFloat(value).toFixed(this.decimalPrecision);
17700 setValue : function(v){
17701 v = this.fixPrecision(v);
17702 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17706 decimalPrecisionFcn : function(v){
17707 return Math.floor(v);
17710 beforeBlur : function(){
17711 var v = this.parseValue(this.getRawValue());
17718 * Ext JS Library 1.1.1
17719 * Copyright(c) 2006-2007, Ext JS, LLC.
17721 * Originally Released Under LGPL - original licence link has changed is not relivant.
17724 * <script type="text/javascript">
17728 * @class Roo.form.DateField
17729 * @extends Roo.form.TriggerField
17730 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17732 * Create a new DateField
17733 * @param {Object} config
17735 Roo.form.DateField = function(config){
17736 Roo.form.DateField.superclass.constructor.call(this, config);
17742 * Fires when a date is selected
17743 * @param {Roo.form.DateField} combo This combo box
17744 * @param {Date} date The date selected
17751 if(typeof this.minValue == "string") {
17752 this.minValue = this.parseDate(this.minValue);
17754 if(typeof this.maxValue == "string") {
17755 this.maxValue = this.parseDate(this.maxValue);
17757 this.ddMatch = null;
17758 if(this.disabledDates){
17759 var dd = this.disabledDates;
17761 for(var i = 0; i < dd.length; i++){
17763 if(i != dd.length-1) {
17767 this.ddMatch = new RegExp(re + ")");
17771 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17773 * @cfg {String} format
17774 * The default date format string which can be overriden for localization support. The format must be
17775 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17779 * @cfg {String} altFormats
17780 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17781 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17783 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17785 * @cfg {Array} disabledDays
17786 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17788 disabledDays : null,
17790 * @cfg {String} disabledDaysText
17791 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17793 disabledDaysText : "Disabled",
17795 * @cfg {Array} disabledDates
17796 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17797 * expression so they are very powerful. Some examples:
17799 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17800 * <li>["03/08", "09/16"] would disable those days for every year</li>
17801 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17802 * <li>["03/../2006"] would disable every day in March 2006</li>
17803 * <li>["^03"] would disable every day in every March</li>
17805 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17806 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17808 disabledDates : null,
17810 * @cfg {String} disabledDatesText
17811 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17813 disabledDatesText : "Disabled",
17815 * @cfg {Date/String} minValue
17816 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17817 * valid format (defaults to null).
17821 * @cfg {Date/String} maxValue
17822 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17823 * valid format (defaults to null).
17827 * @cfg {String} minText
17828 * The error text to display when the date in the cell is before minValue (defaults to
17829 * 'The date in this field must be after {minValue}').
17831 minText : "The date in this field must be equal to or after {0}",
17833 * @cfg {String} maxText
17834 * The error text to display when the date in the cell is after maxValue (defaults to
17835 * 'The date in this field must be before {maxValue}').
17837 maxText : "The date in this field must be equal to or before {0}",
17839 * @cfg {String} invalidText
17840 * The error text to display when the date in the field is invalid (defaults to
17841 * '{value} is not a valid date - it must be in the format {format}').
17843 invalidText : "{0} is not a valid date - it must be in the format {1}",
17845 * @cfg {String} triggerClass
17846 * An additional CSS class used to style the trigger button. The trigger will always get the
17847 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17848 * which displays a calendar icon).
17850 triggerClass : 'x-form-date-trigger',
17854 * @cfg {Boolean} useIso
17855 * if enabled, then the date field will use a hidden field to store the
17856 * real value as iso formated date. default (false)
17860 * @cfg {String/Object} autoCreate
17861 * A DomHelper element spec, or true for a default element spec (defaults to
17862 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17865 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17868 hiddenField: false,
17870 onRender : function(ct, position)
17872 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17874 //this.el.dom.removeAttribute('name');
17875 Roo.log("Changing name?");
17876 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17877 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17879 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17880 // prevent input submission
17881 this.hiddenName = this.name;
17888 validateValue : function(value)
17890 value = this.formatDate(value);
17891 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17892 Roo.log('super failed');
17895 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17898 var svalue = value;
17899 value = this.parseDate(value);
17901 Roo.log('parse date failed' + svalue);
17902 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17905 var time = value.getTime();
17906 if(this.minValue && time < this.minValue.getTime()){
17907 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17910 if(this.maxValue && time > this.maxValue.getTime()){
17911 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17914 if(this.disabledDays){
17915 var day = value.getDay();
17916 for(var i = 0; i < this.disabledDays.length; i++) {
17917 if(day === this.disabledDays[i]){
17918 this.markInvalid(this.disabledDaysText);
17923 var fvalue = this.formatDate(value);
17924 if(this.ddMatch && this.ddMatch.test(fvalue)){
17925 this.markInvalid(String.format(this.disabledDatesText, fvalue));
17932 // Provides logic to override the default TriggerField.validateBlur which just returns true
17933 validateBlur : function(){
17934 return !this.menu || !this.menu.isVisible();
17937 getName: function()
17939 // returns hidden if it's set..
17940 if (!this.rendered) {return ''};
17941 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
17946 * Returns the current date value of the date field.
17947 * @return {Date} The date value
17949 getValue : function(){
17951 return this.hiddenField ?
17952 this.hiddenField.value :
17953 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17957 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
17958 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17959 * (the default format used is "m/d/y").
17962 //All of these calls set the same date value (May 4, 2006)
17964 //Pass a date object:
17965 var dt = new Date('5/4/06');
17966 dateField.setValue(dt);
17968 //Pass a date string (default format):
17969 dateField.setValue('5/4/06');
17971 //Pass a date string (custom format):
17972 dateField.format = 'Y-m-d';
17973 dateField.setValue('2006-5-4');
17975 * @param {String/Date} date The date or valid date string
17977 setValue : function(date){
17978 if (this.hiddenField) {
17979 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17981 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17982 // make sure the value field is always stored as a date..
17983 this.value = this.parseDate(date);
17989 parseDate : function(value){
17990 if(!value || value instanceof Date){
17993 var v = Date.parseDate(value, this.format);
17994 if (!v && this.useIso) {
17995 v = Date.parseDate(value, 'Y-m-d');
17997 if(!v && this.altFormats){
17998 if(!this.altFormatsArray){
17999 this.altFormatsArray = this.altFormats.split("|");
18001 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18002 v = Date.parseDate(value, this.altFormatsArray[i]);
18009 formatDate : function(date, fmt){
18010 return (!date || !(date instanceof Date)) ?
18011 date : date.dateFormat(fmt || this.format);
18016 select: function(m, d){
18019 this.fireEvent('select', this, d);
18021 show : function(){ // retain focus styling
18025 this.focus.defer(10, this);
18026 var ml = this.menuListeners;
18027 this.menu.un("select", ml.select, this);
18028 this.menu.un("show", ml.show, this);
18029 this.menu.un("hide", ml.hide, this);
18034 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18035 onTriggerClick : function(){
18039 if(this.menu == null){
18040 this.menu = new Roo.menu.DateMenu();
18042 Roo.apply(this.menu.picker, {
18043 showClear: this.allowBlank,
18044 minDate : this.minValue,
18045 maxDate : this.maxValue,
18046 disabledDatesRE : this.ddMatch,
18047 disabledDatesText : this.disabledDatesText,
18048 disabledDays : this.disabledDays,
18049 disabledDaysText : this.disabledDaysText,
18050 format : this.useIso ? 'Y-m-d' : this.format,
18051 minText : String.format(this.minText, this.formatDate(this.minValue)),
18052 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18054 this.menu.on(Roo.apply({}, this.menuListeners, {
18057 this.menu.picker.setValue(this.getValue() || new Date());
18058 this.menu.show(this.el, "tl-bl?");
18061 beforeBlur : function(){
18062 var v = this.parseDate(this.getRawValue());
18072 isDirty : function() {
18073 if(this.disabled) {
18077 if(typeof(this.startValue) === 'undefined'){
18081 return String(this.getValue()) !== String(this.startValue);
18086 * Ext JS Library 1.1.1
18087 * Copyright(c) 2006-2007, Ext JS, LLC.
18089 * Originally Released Under LGPL - original licence link has changed is not relivant.
18092 * <script type="text/javascript">
18096 * @class Roo.form.MonthField
18097 * @extends Roo.form.TriggerField
18098 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18100 * Create a new MonthField
18101 * @param {Object} config
18103 Roo.form.MonthField = function(config){
18105 Roo.form.MonthField.superclass.constructor.call(this, config);
18111 * Fires when a date is selected
18112 * @param {Roo.form.MonthFieeld} combo This combo box
18113 * @param {Date} date The date selected
18120 if(typeof this.minValue == "string") {
18121 this.minValue = this.parseDate(this.minValue);
18123 if(typeof this.maxValue == "string") {
18124 this.maxValue = this.parseDate(this.maxValue);
18126 this.ddMatch = null;
18127 if(this.disabledDates){
18128 var dd = this.disabledDates;
18130 for(var i = 0; i < dd.length; i++){
18132 if(i != dd.length-1) {
18136 this.ddMatch = new RegExp(re + ")");
18140 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
18142 * @cfg {String} format
18143 * The default date format string which can be overriden for localization support. The format must be
18144 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18148 * @cfg {String} altFormats
18149 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18150 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18152 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18154 * @cfg {Array} disabledDays
18155 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18157 disabledDays : [0,1,2,3,4,5,6],
18159 * @cfg {String} disabledDaysText
18160 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18162 disabledDaysText : "Disabled",
18164 * @cfg {Array} disabledDates
18165 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18166 * expression so they are very powerful. Some examples:
18168 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18169 * <li>["03/08", "09/16"] would disable those days for every year</li>
18170 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18171 * <li>["03/../2006"] would disable every day in March 2006</li>
18172 * <li>["^03"] would disable every day in every March</li>
18174 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18175 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18177 disabledDates : null,
18179 * @cfg {String} disabledDatesText
18180 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18182 disabledDatesText : "Disabled",
18184 * @cfg {Date/String} minValue
18185 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18186 * valid format (defaults to null).
18190 * @cfg {Date/String} maxValue
18191 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18192 * valid format (defaults to null).
18196 * @cfg {String} minText
18197 * The error text to display when the date in the cell is before minValue (defaults to
18198 * 'The date in this field must be after {minValue}').
18200 minText : "The date in this field must be equal to or after {0}",
18202 * @cfg {String} maxTextf
18203 * The error text to display when the date in the cell is after maxValue (defaults to
18204 * 'The date in this field must be before {maxValue}').
18206 maxText : "The date in this field must be equal to or before {0}",
18208 * @cfg {String} invalidText
18209 * The error text to display when the date in the field is invalid (defaults to
18210 * '{value} is not a valid date - it must be in the format {format}').
18212 invalidText : "{0} is not a valid date - it must be in the format {1}",
18214 * @cfg {String} triggerClass
18215 * An additional CSS class used to style the trigger button. The trigger will always get the
18216 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18217 * which displays a calendar icon).
18219 triggerClass : 'x-form-date-trigger',
18223 * @cfg {Boolean} useIso
18224 * if enabled, then the date field will use a hidden field to store the
18225 * real value as iso formated date. default (true)
18229 * @cfg {String/Object} autoCreate
18230 * A DomHelper element spec, or true for a default element spec (defaults to
18231 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18234 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18237 hiddenField: false,
18239 hideMonthPicker : false,
18241 onRender : function(ct, position)
18243 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18245 this.el.dom.removeAttribute('name');
18246 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18248 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18249 // prevent input submission
18250 this.hiddenName = this.name;
18257 validateValue : function(value)
18259 value = this.formatDate(value);
18260 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18263 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18266 var svalue = value;
18267 value = this.parseDate(value);
18269 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18272 var time = value.getTime();
18273 if(this.minValue && time < this.minValue.getTime()){
18274 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18277 if(this.maxValue && time > this.maxValue.getTime()){
18278 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18281 /*if(this.disabledDays){
18282 var day = value.getDay();
18283 for(var i = 0; i < this.disabledDays.length; i++) {
18284 if(day === this.disabledDays[i]){
18285 this.markInvalid(this.disabledDaysText);
18291 var fvalue = this.formatDate(value);
18292 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18293 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18301 // Provides logic to override the default TriggerField.validateBlur which just returns true
18302 validateBlur : function(){
18303 return !this.menu || !this.menu.isVisible();
18307 * Returns the current date value of the date field.
18308 * @return {Date} The date value
18310 getValue : function(){
18314 return this.hiddenField ?
18315 this.hiddenField.value :
18316 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18320 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18321 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18322 * (the default format used is "m/d/y").
18325 //All of these calls set the same date value (May 4, 2006)
18327 //Pass a date object:
18328 var dt = new Date('5/4/06');
18329 monthField.setValue(dt);
18331 //Pass a date string (default format):
18332 monthField.setValue('5/4/06');
18334 //Pass a date string (custom format):
18335 monthField.format = 'Y-m-d';
18336 monthField.setValue('2006-5-4');
18338 * @param {String/Date} date The date or valid date string
18340 setValue : function(date){
18341 Roo.log('month setValue' + date);
18342 // can only be first of month..
18344 var val = this.parseDate(date);
18346 if (this.hiddenField) {
18347 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18349 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18350 this.value = this.parseDate(date);
18354 parseDate : function(value){
18355 if(!value || value instanceof Date){
18356 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18359 var v = Date.parseDate(value, this.format);
18360 if (!v && this.useIso) {
18361 v = Date.parseDate(value, 'Y-m-d');
18365 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18369 if(!v && this.altFormats){
18370 if(!this.altFormatsArray){
18371 this.altFormatsArray = this.altFormats.split("|");
18373 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18374 v = Date.parseDate(value, this.altFormatsArray[i]);
18381 formatDate : function(date, fmt){
18382 return (!date || !(date instanceof Date)) ?
18383 date : date.dateFormat(fmt || this.format);
18388 select: function(m, d){
18390 this.fireEvent('select', this, d);
18392 show : function(){ // retain focus styling
18396 this.focus.defer(10, this);
18397 var ml = this.menuListeners;
18398 this.menu.un("select", ml.select, this);
18399 this.menu.un("show", ml.show, this);
18400 this.menu.un("hide", ml.hide, this);
18404 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18405 onTriggerClick : function(){
18409 if(this.menu == null){
18410 this.menu = new Roo.menu.DateMenu();
18414 Roo.apply(this.menu.picker, {
18416 showClear: this.allowBlank,
18417 minDate : this.minValue,
18418 maxDate : this.maxValue,
18419 disabledDatesRE : this.ddMatch,
18420 disabledDatesText : this.disabledDatesText,
18422 format : this.useIso ? 'Y-m-d' : this.format,
18423 minText : String.format(this.minText, this.formatDate(this.minValue)),
18424 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18427 this.menu.on(Roo.apply({}, this.menuListeners, {
18435 // hide month picker get's called when we called by 'before hide';
18437 var ignorehide = true;
18438 p.hideMonthPicker = function(disableAnim){
18442 if(this.monthPicker){
18443 Roo.log("hideMonthPicker called");
18444 if(disableAnim === true){
18445 this.monthPicker.hide();
18447 this.monthPicker.slideOut('t', {duration:.2});
18448 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18449 p.fireEvent("select", this, this.value);
18455 Roo.log('picker set value');
18456 Roo.log(this.getValue());
18457 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18458 m.show(this.el, 'tl-bl?');
18459 ignorehide = false;
18460 // this will trigger hideMonthPicker..
18463 // hidden the day picker
18464 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18470 p.showMonthPicker.defer(100, p);
18476 beforeBlur : function(){
18477 var v = this.parseDate(this.getRawValue());
18483 /** @cfg {Boolean} grow @hide */
18484 /** @cfg {Number} growMin @hide */
18485 /** @cfg {Number} growMax @hide */
18492 * Ext JS Library 1.1.1
18493 * Copyright(c) 2006-2007, Ext JS, LLC.
18495 * Originally Released Under LGPL - original licence link has changed is not relivant.
18498 * <script type="text/javascript">
18503 * @class Roo.form.ComboBox
18504 * @extends Roo.form.TriggerField
18505 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18507 * Create a new ComboBox.
18508 * @param {Object} config Configuration options
18510 Roo.form.ComboBox = function(config){
18511 Roo.form.ComboBox.superclass.constructor.call(this, config);
18515 * Fires when the dropdown list is expanded
18516 * @param {Roo.form.ComboBox} combo This combo box
18521 * Fires when the dropdown list is collapsed
18522 * @param {Roo.form.ComboBox} combo This combo box
18526 * @event beforeselect
18527 * Fires before a list item is selected. Return false to cancel the selection.
18528 * @param {Roo.form.ComboBox} combo This combo box
18529 * @param {Roo.data.Record} record The data record returned from the underlying store
18530 * @param {Number} index The index of the selected item in the dropdown list
18532 'beforeselect' : true,
18535 * Fires when a list item is selected
18536 * @param {Roo.form.ComboBox} combo This combo box
18537 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18538 * @param {Number} index The index of the selected item in the dropdown list
18542 * @event beforequery
18543 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18544 * The event object passed has these properties:
18545 * @param {Roo.form.ComboBox} combo This combo box
18546 * @param {String} query The query
18547 * @param {Boolean} forceAll true to force "all" query
18548 * @param {Boolean} cancel true to cancel the query
18549 * @param {Object} e The query event object
18551 'beforequery': true,
18554 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18555 * @param {Roo.form.ComboBox} combo This combo box
18560 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18561 * @param {Roo.form.ComboBox} combo This combo box
18562 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18568 if(this.transform){
18569 this.allowDomMove = false;
18570 var s = Roo.getDom(this.transform);
18571 if(!this.hiddenName){
18572 this.hiddenName = s.name;
18575 this.mode = 'local';
18576 var d = [], opts = s.options;
18577 for(var i = 0, len = opts.length;i < len; i++){
18579 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18581 this.value = value;
18583 d.push([value, o.text]);
18585 this.store = new Roo.data.SimpleStore({
18587 fields: ['value', 'text'],
18590 this.valueField = 'value';
18591 this.displayField = 'text';
18593 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18594 if(!this.lazyRender){
18595 this.target = true;
18596 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18597 s.parentNode.removeChild(s); // remove it
18598 this.render(this.el.parentNode);
18600 s.parentNode.removeChild(s); // remove it
18605 this.store = Roo.factory(this.store, Roo.data);
18608 this.selectedIndex = -1;
18609 if(this.mode == 'local'){
18610 if(config.queryDelay === undefined){
18611 this.queryDelay = 10;
18613 if(config.minChars === undefined){
18619 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18621 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18624 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18625 * rendering into an Roo.Editor, defaults to false)
18628 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18629 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18632 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18635 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18636 * the dropdown list (defaults to undefined, with no header element)
18640 * @cfg {String/Roo.Template} tpl The template to use to render the output
18644 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18646 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18648 listWidth: undefined,
18650 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18651 * mode = 'remote' or 'text' if mode = 'local')
18653 displayField: undefined,
18655 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18656 * mode = 'remote' or 'value' if mode = 'local').
18657 * Note: use of a valueField requires the user make a selection
18658 * in order for a value to be mapped.
18660 valueField: undefined,
18664 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18665 * field's data value (defaults to the underlying DOM element's name)
18667 hiddenName: undefined,
18669 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18673 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18675 selectedClass: 'x-combo-selected',
18677 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18678 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18679 * which displays a downward arrow icon).
18681 triggerClass : 'x-form-arrow-trigger',
18683 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18687 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18688 * anchor positions (defaults to 'tl-bl')
18690 listAlign: 'tl-bl?',
18692 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18696 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18697 * query specified by the allQuery config option (defaults to 'query')
18699 triggerAction: 'query',
18701 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18702 * (defaults to 4, does not apply if editable = false)
18706 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18707 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18711 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18712 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18716 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18717 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18721 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18722 * when editable = true (defaults to false)
18724 selectOnFocus:false,
18726 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18728 queryParam: 'query',
18730 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18731 * when mode = 'remote' (defaults to 'Loading...')
18733 loadingText: 'Loading...',
18735 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18739 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18743 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18744 * traditional select (defaults to true)
18748 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18752 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18756 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18757 * listWidth has a higher value)
18761 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18762 * allow the user to set arbitrary text into the field (defaults to false)
18764 forceSelection:false,
18766 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18767 * if typeAhead = true (defaults to 250)
18769 typeAheadDelay : 250,
18771 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18772 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18774 valueNotFoundText : undefined,
18776 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18778 blockFocus : false,
18781 * @cfg {Boolean} disableClear Disable showing of clear button.
18783 disableClear : false,
18785 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18787 alwaysQuery : false,
18793 // element that contains real text value.. (when hidden is used..)
18796 onRender : function(ct, position){
18797 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18798 if(this.hiddenName){
18799 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18801 this.hiddenField.value =
18802 this.hiddenValue !== undefined ? this.hiddenValue :
18803 this.value !== undefined ? this.value : '';
18805 // prevent input submission
18806 this.el.dom.removeAttribute('name');
18811 this.el.dom.setAttribute('autocomplete', 'off');
18814 var cls = 'x-combo-list';
18816 this.list = new Roo.Layer({
18817 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18820 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18821 this.list.setWidth(lw);
18822 this.list.swallowEvent('mousewheel');
18823 this.assetHeight = 0;
18826 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18827 this.assetHeight += this.header.getHeight();
18830 this.innerList = this.list.createChild({cls:cls+'-inner'});
18831 this.innerList.on('mouseover', this.onViewOver, this);
18832 this.innerList.on('mousemove', this.onViewMove, this);
18833 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18835 if(this.allowBlank && !this.pageSize && !this.disableClear){
18836 this.footer = this.list.createChild({cls:cls+'-ft'});
18837 this.pageTb = new Roo.Toolbar(this.footer);
18841 this.footer = this.list.createChild({cls:cls+'-ft'});
18842 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18843 {pageSize: this.pageSize});
18847 if (this.pageTb && this.allowBlank && !this.disableClear) {
18849 this.pageTb.add(new Roo.Toolbar.Fill(), {
18850 cls: 'x-btn-icon x-btn-clear',
18852 handler: function()
18855 _this.clearValue();
18856 _this.onSelect(false, -1);
18861 this.assetHeight += this.footer.getHeight();
18866 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18869 this.view = new Roo.View(this.innerList, this.tpl, {
18870 singleSelect:true, store: this.store, selectedClass: this.selectedClass
18873 this.view.on('click', this.onViewClick, this);
18875 this.store.on('beforeload', this.onBeforeLoad, this);
18876 this.store.on('load', this.onLoad, this);
18877 this.store.on('loadexception', this.onLoadException, this);
18879 if(this.resizable){
18880 this.resizer = new Roo.Resizable(this.list, {
18881 pinned:true, handles:'se'
18883 this.resizer.on('resize', function(r, w, h){
18884 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18885 this.listWidth = w;
18886 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18887 this.restrictHeight();
18889 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18891 if(!this.editable){
18892 this.editable = true;
18893 this.setEditable(false);
18897 if (typeof(this.events.add.listeners) != 'undefined') {
18899 this.addicon = this.wrap.createChild(
18900 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18902 this.addicon.on('click', function(e) {
18903 this.fireEvent('add', this);
18906 if (typeof(this.events.edit.listeners) != 'undefined') {
18908 this.editicon = this.wrap.createChild(
18909 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
18910 if (this.addicon) {
18911 this.editicon.setStyle('margin-left', '40px');
18913 this.editicon.on('click', function(e) {
18915 // we fire even if inothing is selected..
18916 this.fireEvent('edit', this, this.lastData );
18926 initEvents : function(){
18927 Roo.form.ComboBox.superclass.initEvents.call(this);
18929 this.keyNav = new Roo.KeyNav(this.el, {
18930 "up" : function(e){
18931 this.inKeyMode = true;
18935 "down" : function(e){
18936 if(!this.isExpanded()){
18937 this.onTriggerClick();
18939 this.inKeyMode = true;
18944 "enter" : function(e){
18945 this.onViewClick();
18949 "esc" : function(e){
18953 "tab" : function(e){
18954 this.onViewClick(false);
18955 this.fireEvent("specialkey", this, e);
18961 doRelay : function(foo, bar, hname){
18962 if(hname == 'down' || this.scope.isExpanded()){
18963 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18970 this.queryDelay = Math.max(this.queryDelay || 10,
18971 this.mode == 'local' ? 10 : 250);
18972 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18973 if(this.typeAhead){
18974 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18976 if(this.editable !== false){
18977 this.el.on("keyup", this.onKeyUp, this);
18979 if(this.forceSelection){
18980 this.on('blur', this.doForce, this);
18984 onDestroy : function(){
18986 this.view.setStore(null);
18987 this.view.el.removeAllListeners();
18988 this.view.el.remove();
18989 this.view.purgeListeners();
18992 this.list.destroy();
18995 this.store.un('beforeload', this.onBeforeLoad, this);
18996 this.store.un('load', this.onLoad, this);
18997 this.store.un('loadexception', this.onLoadException, this);
18999 Roo.form.ComboBox.superclass.onDestroy.call(this);
19003 fireKey : function(e){
19004 if(e.isNavKeyPress() && !this.list.isVisible()){
19005 this.fireEvent("specialkey", this, e);
19010 onResize: function(w, h){
19011 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19013 if(typeof w != 'number'){
19014 // we do not handle it!?!?
19017 var tw = this.trigger.getWidth();
19018 tw += this.addicon ? this.addicon.getWidth() : 0;
19019 tw += this.editicon ? this.editicon.getWidth() : 0;
19021 this.el.setWidth( this.adjustWidth('input', x));
19023 this.trigger.setStyle('left', x+'px');
19025 if(this.list && this.listWidth === undefined){
19026 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19027 this.list.setWidth(lw);
19028 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19036 * Allow or prevent the user from directly editing the field text. If false is passed,
19037 * the user will only be able to select from the items defined in the dropdown list. This method
19038 * is the runtime equivalent of setting the 'editable' config option at config time.
19039 * @param {Boolean} value True to allow the user to directly edit the field text
19041 setEditable : function(value){
19042 if(value == this.editable){
19045 this.editable = value;
19047 this.el.dom.setAttribute('readOnly', true);
19048 this.el.on('mousedown', this.onTriggerClick, this);
19049 this.el.addClass('x-combo-noedit');
19051 this.el.dom.setAttribute('readOnly', false);
19052 this.el.un('mousedown', this.onTriggerClick, this);
19053 this.el.removeClass('x-combo-noedit');
19058 onBeforeLoad : function(){
19059 if(!this.hasFocus){
19062 this.innerList.update(this.loadingText ?
19063 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19064 this.restrictHeight();
19065 this.selectedIndex = -1;
19069 onLoad : function(){
19070 if(!this.hasFocus){
19073 if(this.store.getCount() > 0){
19075 this.restrictHeight();
19076 if(this.lastQuery == this.allQuery){
19078 this.el.dom.select();
19080 if(!this.selectByValue(this.value, true)){
19081 this.select(0, true);
19085 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19086 this.taTask.delay(this.typeAheadDelay);
19090 this.onEmptyResults();
19095 onLoadException : function()
19098 Roo.log(this.store.reader.jsonData);
19099 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19100 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19106 onTypeAhead : function(){
19107 if(this.store.getCount() > 0){
19108 var r = this.store.getAt(0);
19109 var newValue = r.data[this.displayField];
19110 var len = newValue.length;
19111 var selStart = this.getRawValue().length;
19112 if(selStart != len){
19113 this.setRawValue(newValue);
19114 this.selectText(selStart, newValue.length);
19120 onSelect : function(record, index){
19121 if(this.fireEvent('beforeselect', this, record, index) !== false){
19122 this.setFromData(index > -1 ? record.data : false);
19124 this.fireEvent('select', this, record, index);
19129 * Returns the currently selected field value or empty string if no value is set.
19130 * @return {String} value The selected value
19132 getValue : function(){
19133 if(this.valueField){
19134 return typeof this.value != 'undefined' ? this.value : '';
19136 return Roo.form.ComboBox.superclass.getValue.call(this);
19140 * Clears any text/value currently set in the field
19142 clearValue : function(){
19143 if(this.hiddenField){
19144 this.hiddenField.value = '';
19147 this.setRawValue('');
19148 this.lastSelectionText = '';
19153 * Sets the specified value into the field. If the value finds a match, the corresponding record text
19154 * will be displayed in the field. If the value does not match the data value of an existing item,
19155 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19156 * Otherwise the field will be blank (although the value will still be set).
19157 * @param {String} value The value to match
19159 setValue : function(v){
19161 if(this.valueField){
19162 var r = this.findRecord(this.valueField, v);
19164 text = r.data[this.displayField];
19165 }else if(this.valueNotFoundText !== undefined){
19166 text = this.valueNotFoundText;
19169 this.lastSelectionText = text;
19170 if(this.hiddenField){
19171 this.hiddenField.value = v;
19173 Roo.form.ComboBox.superclass.setValue.call(this, text);
19177 * @property {Object} the last set data for the element
19182 * Sets the value of the field based on a object which is related to the record format for the store.
19183 * @param {Object} value the value to set as. or false on reset?
19185 setFromData : function(o){
19186 var dv = ''; // display value
19187 var vv = ''; // value value..
19189 if (this.displayField) {
19190 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19192 // this is an error condition!!!
19193 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19196 if(this.valueField){
19197 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19199 if(this.hiddenField){
19200 this.hiddenField.value = vv;
19202 this.lastSelectionText = dv;
19203 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19207 // no hidden field.. - we store the value in 'value', but still display
19208 // display field!!!!
19209 this.lastSelectionText = dv;
19210 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19216 reset : function(){
19217 // overridden so that last data is reset..
19218 this.setValue(this.resetValue);
19219 this.clearInvalid();
19220 this.lastData = false;
19222 this.view.clearSelections();
19226 findRecord : function(prop, value){
19228 if(this.store.getCount() > 0){
19229 this.store.each(function(r){
19230 if(r.data[prop] == value){
19240 getName: function()
19242 // returns hidden if it's set..
19243 if (!this.rendered) {return ''};
19244 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19248 onViewMove : function(e, t){
19249 this.inKeyMode = false;
19253 onViewOver : function(e, t){
19254 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19257 var item = this.view.findItemFromChild(t);
19259 var index = this.view.indexOf(item);
19260 this.select(index, false);
19265 onViewClick : function(doFocus)
19267 var index = this.view.getSelectedIndexes()[0];
19268 var r = this.store.getAt(index);
19270 this.onSelect(r, index);
19272 if(doFocus !== false && !this.blockFocus){
19278 restrictHeight : function(){
19279 this.innerList.dom.style.height = '';
19280 var inner = this.innerList.dom;
19281 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19282 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19283 this.list.beginUpdate();
19284 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19285 this.list.alignTo(this.el, this.listAlign);
19286 this.list.endUpdate();
19290 onEmptyResults : function(){
19295 * Returns true if the dropdown list is expanded, else false.
19297 isExpanded : function(){
19298 return this.list.isVisible();
19302 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19303 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19304 * @param {String} value The data value of the item to select
19305 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19306 * selected item if it is not currently in view (defaults to true)
19307 * @return {Boolean} True if the value matched an item in the list, else false
19309 selectByValue : function(v, scrollIntoView){
19310 if(v !== undefined && v !== null){
19311 var r = this.findRecord(this.valueField || this.displayField, v);
19313 this.select(this.store.indexOf(r), scrollIntoView);
19321 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19322 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19323 * @param {Number} index The zero-based index of the list item to select
19324 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19325 * selected item if it is not currently in view (defaults to true)
19327 select : function(index, scrollIntoView){
19328 this.selectedIndex = index;
19329 this.view.select(index);
19330 if(scrollIntoView !== false){
19331 var el = this.view.getNode(index);
19333 this.innerList.scrollChildIntoView(el, false);
19339 selectNext : function(){
19340 var ct = this.store.getCount();
19342 if(this.selectedIndex == -1){
19344 }else if(this.selectedIndex < ct-1){
19345 this.select(this.selectedIndex+1);
19351 selectPrev : function(){
19352 var ct = this.store.getCount();
19354 if(this.selectedIndex == -1){
19356 }else if(this.selectedIndex != 0){
19357 this.select(this.selectedIndex-1);
19363 onKeyUp : function(e){
19364 if(this.editable !== false && !e.isSpecialKey()){
19365 this.lastKey = e.getKey();
19366 this.dqTask.delay(this.queryDelay);
19371 validateBlur : function(){
19372 return !this.list || !this.list.isVisible();
19376 initQuery : function(){
19377 this.doQuery(this.getRawValue());
19381 doForce : function(){
19382 if(this.el.dom.value.length > 0){
19383 this.el.dom.value =
19384 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19390 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19391 * query allowing the query action to be canceled if needed.
19392 * @param {String} query The SQL query to execute
19393 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19394 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19395 * saved in the current store (defaults to false)
19397 doQuery : function(q, forceAll){
19398 if(q === undefined || q === null){
19403 forceAll: forceAll,
19407 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19411 forceAll = qe.forceAll;
19412 if(forceAll === true || (q.length >= this.minChars)){
19413 if(this.lastQuery != q || this.alwaysQuery){
19414 this.lastQuery = q;
19415 if(this.mode == 'local'){
19416 this.selectedIndex = -1;
19418 this.store.clearFilter();
19420 this.store.filter(this.displayField, q);
19424 this.store.baseParams[this.queryParam] = q;
19426 params: this.getParams(q)
19431 this.selectedIndex = -1;
19438 getParams : function(q){
19440 //p[this.queryParam] = q;
19443 p.limit = this.pageSize;
19449 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19451 collapse : function(){
19452 if(!this.isExpanded()){
19456 Roo.get(document).un('mousedown', this.collapseIf, this);
19457 Roo.get(document).un('mousewheel', this.collapseIf, this);
19458 if (!this.editable) {
19459 Roo.get(document).un('keydown', this.listKeyPress, this);
19461 this.fireEvent('collapse', this);
19465 collapseIf : function(e){
19466 if(!e.within(this.wrap) && !e.within(this.list)){
19472 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19474 expand : function(){
19475 if(this.isExpanded() || !this.hasFocus){
19478 this.list.alignTo(this.el, this.listAlign);
19480 Roo.get(document).on('mousedown', this.collapseIf, this);
19481 Roo.get(document).on('mousewheel', this.collapseIf, this);
19482 if (!this.editable) {
19483 Roo.get(document).on('keydown', this.listKeyPress, this);
19486 this.fireEvent('expand', this);
19490 // Implements the default empty TriggerField.onTriggerClick function
19491 onTriggerClick : function(){
19495 if(this.isExpanded()){
19497 if (!this.blockFocus) {
19502 this.hasFocus = true;
19503 if(this.triggerAction == 'all') {
19504 this.doQuery(this.allQuery, true);
19506 this.doQuery(this.getRawValue());
19508 if (!this.blockFocus) {
19513 listKeyPress : function(e)
19515 //Roo.log('listkeypress');
19516 // scroll to first matching element based on key pres..
19517 if (e.isSpecialKey()) {
19520 var k = String.fromCharCode(e.getKey()).toUpperCase();
19523 var csel = this.view.getSelectedNodes();
19524 var cselitem = false;
19526 var ix = this.view.indexOf(csel[0]);
19527 cselitem = this.store.getAt(ix);
19528 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19534 this.store.each(function(v) {
19536 // start at existing selection.
19537 if (cselitem.id == v.id) {
19543 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19544 match = this.store.indexOf(v);
19549 if (match === false) {
19550 return true; // no more action?
19553 this.view.select(match);
19554 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19555 sn.scrollIntoView(sn.dom.parentNode, false);
19559 * @cfg {Boolean} grow
19563 * @cfg {Number} growMin
19567 * @cfg {Number} growMax
19575 * Copyright(c) 2010-2012, Roo J Solutions Limited
19582 * @class Roo.form.ComboBoxArray
19583 * @extends Roo.form.TextField
19584 * A facebook style adder... for lists of email / people / countries etc...
19585 * pick multiple items from a combo box, and shows each one.
19587 * Fred [x] Brian [x] [Pick another |v]
19590 * For this to work: it needs various extra information
19591 * - normal combo problay has
19593 * + displayField, valueField
19595 * For our purpose...
19598 * If we change from 'extends' to wrapping...
19605 * Create a new ComboBoxArray.
19606 * @param {Object} config Configuration options
19610 Roo.form.ComboBoxArray = function(config)
19614 * @event beforeremove
19615 * Fires before remove the value from the list
19616 * @param {Roo.form.ComboBoxArray} _self This combo box array
19617 * @param {Roo.form.ComboBoxArray.Item} item removed item
19619 'beforeremove' : true,
19622 * Fires when remove the value from the list
19623 * @param {Roo.form.ComboBoxArray} _self This combo box array
19624 * @param {Roo.form.ComboBoxArray.Item} item removed item
19631 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19633 this.items = new Roo.util.MixedCollection(false);
19635 // construct the child combo...
19645 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19648 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19653 // behavies liek a hiddne field
19654 inputType: 'hidden',
19656 * @cfg {Number} width The width of the box that displays the selected element
19663 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19667 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19669 hiddenName : false,
19672 // private the array of items that are displayed..
19674 // private - the hidden field el.
19676 // private - the filed el..
19679 //validateValue : function() { return true; }, // all values are ok!
19680 //onAddClick: function() { },
19682 onRender : function(ct, position)
19685 // create the standard hidden element
19686 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19689 // give fake names to child combo;
19690 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19691 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19693 this.combo = Roo.factory(this.combo, Roo.form);
19694 this.combo.onRender(ct, position);
19695 if (typeof(this.combo.width) != 'undefined') {
19696 this.combo.onResize(this.combo.width,0);
19699 this.combo.initEvents();
19701 // assigned so form know we need to do this..
19702 this.store = this.combo.store;
19703 this.valueField = this.combo.valueField;
19704 this.displayField = this.combo.displayField ;
19707 this.combo.wrap.addClass('x-cbarray-grp');
19709 var cbwrap = this.combo.wrap.createChild(
19710 {tag: 'div', cls: 'x-cbarray-cb'},
19715 this.hiddenEl = this.combo.wrap.createChild({
19716 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19718 this.el = this.combo.wrap.createChild({
19719 tag: 'input', type:'hidden' , name: this.name, value : ''
19721 // this.el.dom.removeAttribute("name");
19724 this.outerWrap = this.combo.wrap;
19725 this.wrap = cbwrap;
19727 this.outerWrap.setWidth(this.width);
19728 this.outerWrap.dom.removeChild(this.el.dom);
19730 this.wrap.dom.appendChild(this.el.dom);
19731 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19732 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19734 this.combo.trigger.setStyle('position','relative');
19735 this.combo.trigger.setStyle('left', '0px');
19736 this.combo.trigger.setStyle('top', '2px');
19738 this.combo.el.setStyle('vertical-align', 'text-bottom');
19740 //this.trigger.setStyle('vertical-align', 'top');
19742 // this should use the code from combo really... on('add' ....)
19746 this.adder = this.outerWrap.createChild(
19747 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19749 this.adder.on('click', function(e) {
19750 _t.fireEvent('adderclick', this, e);
19754 //this.adder.on('click', this.onAddClick, _t);
19757 this.combo.on('select', function(cb, rec, ix) {
19758 this.addItem(rec.data);
19761 cb.el.dom.value = '';
19762 //cb.lastData = rec.data;
19771 getName: function()
19773 // returns hidden if it's set..
19774 if (!this.rendered) {return ''};
19775 return this.hiddenName ? this.hiddenName : this.name;
19780 onResize: function(w, h){
19783 // not sure if this is needed..
19784 //this.combo.onResize(w,h);
19786 if(typeof w != 'number'){
19787 // we do not handle it!?!?
19790 var tw = this.combo.trigger.getWidth();
19791 tw += this.addicon ? this.addicon.getWidth() : 0;
19792 tw += this.editicon ? this.editicon.getWidth() : 0;
19794 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19796 this.combo.trigger.setStyle('left', '0px');
19798 if(this.list && this.listWidth === undefined){
19799 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19800 this.list.setWidth(lw);
19801 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19808 addItem: function(rec)
19810 var valueField = this.combo.valueField;
19811 var displayField = this.combo.displayField;
19812 if (this.items.indexOfKey(rec[valueField]) > -1) {
19813 //console.log("GOT " + rec.data.id);
19817 var x = new Roo.form.ComboBoxArray.Item({
19818 //id : rec[this.idField],
19820 displayField : displayField ,
19821 tipField : displayField ,
19825 this.items.add(rec[valueField],x);
19826 // add it before the element..
19827 this.updateHiddenEl();
19828 x.render(this.outerWrap, this.wrap.dom);
19829 // add the image handler..
19832 updateHiddenEl : function()
19835 if (!this.hiddenEl) {
19839 var idField = this.combo.valueField;
19841 this.items.each(function(f) {
19842 ar.push(f.data[idField]);
19845 this.hiddenEl.dom.value = ar.join(',');
19851 this.items.clear();
19853 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19857 this.el.dom.value = '';
19858 if (this.hiddenEl) {
19859 this.hiddenEl.dom.value = '';
19863 getValue: function()
19865 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19867 setValue: function(v) // not a valid action - must use addItems..
19874 if (this.store.isLocal && (typeof(v) == 'string')) {
19875 // then we can use the store to find the values..
19876 // comma seperated at present.. this needs to allow JSON based encoding..
19877 this.hiddenEl.value = v;
19879 Roo.each(v.split(','), function(k) {
19880 Roo.log("CHECK " + this.valueField + ',' + k);
19881 var li = this.store.query(this.valueField, k);
19886 add[this.valueField] = k;
19887 add[this.displayField] = li.item(0).data[this.displayField];
19893 if (typeof(v) == 'object' ) {
19894 // then let's assume it's an array of objects..
19895 Roo.each(v, function(l) {
19903 setFromData: function(v)
19905 // this recieves an object, if setValues is called.
19907 this.el.dom.value = v[this.displayField];
19908 this.hiddenEl.dom.value = v[this.valueField];
19909 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19912 var kv = v[this.valueField];
19913 var dv = v[this.displayField];
19914 kv = typeof(kv) != 'string' ? '' : kv;
19915 dv = typeof(dv) != 'string' ? '' : dv;
19918 var keys = kv.split(',');
19919 var display = dv.split(',');
19920 for (var i = 0 ; i < keys.length; i++) {
19923 add[this.valueField] = keys[i];
19924 add[this.displayField] = display[i];
19932 * Validates the combox array value
19933 * @return {Boolean} True if the value is valid, else false
19935 validate : function(){
19936 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19937 this.clearInvalid();
19943 validateValue : function(value){
19944 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19952 isDirty : function() {
19953 if(this.disabled) {
19958 var d = Roo.decode(String(this.originalValue));
19960 return String(this.getValue()) !== String(this.originalValue);
19963 var originalValue = [];
19965 for (var i = 0; i < d.length; i++){
19966 originalValue.push(d[i][this.valueField]);
19969 return String(this.getValue()) !== String(originalValue.join(','));
19978 * @class Roo.form.ComboBoxArray.Item
19979 * @extends Roo.BoxComponent
19980 * A selected item in the list
19981 * Fred [x] Brian [x] [Pick another |v]
19984 * Create a new item.
19985 * @param {Object} config Configuration options
19988 Roo.form.ComboBoxArray.Item = function(config) {
19989 config.id = Roo.id();
19990 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19993 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19996 displayField : false,
20000 defaultAutoCreate : {
20002 cls: 'x-cbarray-item',
20009 src : Roo.BLANK_IMAGE_URL ,
20017 onRender : function(ct, position)
20019 Roo.form.Field.superclass.onRender.call(this, ct, position);
20022 var cfg = this.getAutoCreate();
20023 this.el = ct.createChild(cfg, position);
20026 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20028 this.el.child('div').dom.innerHTML = this.cb.renderer ?
20029 this.cb.renderer(this.data) :
20030 String.format('{0}',this.data[this.displayField]);
20033 this.el.child('div').dom.setAttribute('qtip',
20034 String.format('{0}',this.data[this.tipField])
20037 this.el.child('img').on('click', this.remove, this);
20041 remove : function()
20043 if(this.cb.disabled){
20047 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20048 this.cb.items.remove(this);
20049 this.el.child('img').un('click', this.remove, this);
20051 this.cb.updateHiddenEl();
20053 this.cb.fireEvent('remove', this.cb, this);
20059 * Ext JS Library 1.1.1
20060 * Copyright(c) 2006-2007, Ext JS, LLC.
20062 * Originally Released Under LGPL - original licence link has changed is not relivant.
20065 * <script type="text/javascript">
20068 * @class Roo.form.Checkbox
20069 * @extends Roo.form.Field
20070 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20072 * Creates a new Checkbox
20073 * @param {Object} config Configuration options
20075 Roo.form.Checkbox = function(config){
20076 Roo.form.Checkbox.superclass.constructor.call(this, config);
20080 * Fires when the checkbox is checked or unchecked.
20081 * @param {Roo.form.Checkbox} this This checkbox
20082 * @param {Boolean} checked The new checked value
20088 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20090 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20092 focusClass : undefined,
20094 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20096 fieldClass: "x-form-field",
20098 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20102 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20103 * {tag: "input", type: "checkbox", autocomplete: "off"})
20105 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20107 * @cfg {String} boxLabel The text that appears beside the checkbox
20111 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20115 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20117 valueOff: '0', // value when not checked..
20119 actionMode : 'viewEl',
20122 itemCls : 'x-menu-check-item x-form-item',
20123 groupClass : 'x-menu-group-item',
20124 inputType : 'hidden',
20127 inSetChecked: false, // check that we are not calling self...
20129 inputElement: false, // real input element?
20130 basedOn: false, // ????
20132 isFormField: true, // not sure where this is needed!!!!
20134 onResize : function(){
20135 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20136 if(!this.boxLabel){
20137 this.el.alignTo(this.wrap, 'c-c');
20141 initEvents : function(){
20142 Roo.form.Checkbox.superclass.initEvents.call(this);
20143 this.el.on("click", this.onClick, this);
20144 this.el.on("change", this.onClick, this);
20148 getResizeEl : function(){
20152 getPositionEl : function(){
20157 onRender : function(ct, position){
20158 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20160 if(this.inputValue !== undefined){
20161 this.el.dom.value = this.inputValue;
20164 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20165 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20166 var viewEl = this.wrap.createChild({
20167 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20168 this.viewEl = viewEl;
20169 this.wrap.on('click', this.onClick, this);
20171 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20172 this.el.on('propertychange', this.setFromHidden, this); //ie
20177 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20178 // viewEl.on('click', this.onClick, this);
20180 //if(this.checked){
20181 this.setChecked(this.checked);
20183 //this.checked = this.el.dom;
20189 initValue : Roo.emptyFn,
20192 * Returns the checked state of the checkbox.
20193 * @return {Boolean} True if checked, else false
20195 getValue : function(){
20197 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20199 return this.valueOff;
20204 onClick : function(){
20205 if (this.disabled) {
20208 this.setChecked(!this.checked);
20210 //if(this.el.dom.checked != this.checked){
20211 // this.setValue(this.el.dom.checked);
20216 * Sets the checked state of the checkbox.
20217 * On is always based on a string comparison between inputValue and the param.
20218 * @param {Boolean/String} value - the value to set
20219 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20221 setValue : function(v,suppressEvent){
20224 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20225 //if(this.el && this.el.dom){
20226 // this.el.dom.checked = this.checked;
20227 // this.el.dom.defaultChecked = this.checked;
20229 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20230 //this.fireEvent("check", this, this.checked);
20233 setChecked : function(state,suppressEvent)
20235 if (this.inSetChecked) {
20236 this.checked = state;
20242 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20244 this.checked = state;
20245 if(suppressEvent !== true){
20246 this.fireEvent('check', this, state);
20248 this.inSetChecked = true;
20249 this.el.dom.value = state ? this.inputValue : this.valueOff;
20250 this.inSetChecked = false;
20253 // handle setting of hidden value by some other method!!?!?
20254 setFromHidden: function()
20259 //console.log("SET FROM HIDDEN");
20260 //alert('setFrom hidden');
20261 this.setValue(this.el.dom.value);
20264 onDestroy : function()
20267 Roo.get(this.viewEl).remove();
20270 Roo.form.Checkbox.superclass.onDestroy.call(this);
20273 setBoxLabel : function(str)
20275 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20280 * Ext JS Library 1.1.1
20281 * Copyright(c) 2006-2007, Ext JS, LLC.
20283 * Originally Released Under LGPL - original licence link has changed is not relivant.
20286 * <script type="text/javascript">
20290 * @class Roo.form.Radio
20291 * @extends Roo.form.Checkbox
20292 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20293 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20295 * Creates a new Radio
20296 * @param {Object} config Configuration options
20298 Roo.form.Radio = function(){
20299 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20301 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20302 inputType: 'radio',
20305 * If this radio is part of a group, it will return the selected value
20308 getGroupValue : function(){
20309 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20313 onRender : function(ct, position){
20314 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20316 if(this.inputValue !== undefined){
20317 this.el.dom.value = this.inputValue;
20320 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20321 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20322 //var viewEl = this.wrap.createChild({
20323 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20324 //this.viewEl = viewEl;
20325 //this.wrap.on('click', this.onClick, this);
20327 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20328 //this.el.on('propertychange', this.setFromHidden, this); //ie
20333 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20334 // viewEl.on('click', this.onClick, this);
20337 this.el.dom.checked = 'checked' ;
20343 });//<script type="text/javascript">
20346 * Based Ext JS Library 1.1.1
20347 * Copyright(c) 2006-2007, Ext JS, LLC.
20353 * @class Roo.HtmlEditorCore
20354 * @extends Roo.Component
20355 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20357 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20360 Roo.HtmlEditorCore = function(config){
20363 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20368 * @event initialize
20369 * Fires when the editor is fully initialized (including the iframe)
20370 * @param {Roo.HtmlEditorCore} this
20375 * Fires when the editor is first receives the focus. Any insertion must wait
20376 * until after this event.
20377 * @param {Roo.HtmlEditorCore} this
20381 * @event beforesync
20382 * Fires before the textarea is updated with content from the editor iframe. Return false
20383 * to cancel the sync.
20384 * @param {Roo.HtmlEditorCore} this
20385 * @param {String} html
20389 * @event beforepush
20390 * Fires before the iframe editor is updated with content from the textarea. Return false
20391 * to cancel the push.
20392 * @param {Roo.HtmlEditorCore} this
20393 * @param {String} html
20398 * Fires when the textarea is updated with content from the editor iframe.
20399 * @param {Roo.HtmlEditorCore} this
20400 * @param {String} html
20405 * Fires when the iframe editor is updated with content from the textarea.
20406 * @param {Roo.HtmlEditorCore} this
20407 * @param {String} html
20412 * @event editorevent
20413 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20414 * @param {Roo.HtmlEditorCore} this
20420 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20422 // defaults : white / black...
20423 this.applyBlacklists();
20430 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20434 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20440 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20445 * @cfg {Number} height (in pixels)
20449 * @cfg {Number} width (in pixels)
20454 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20457 stylesheets: false,
20462 // private properties
20463 validationEvent : false,
20465 initialized : false,
20467 sourceEditMode : false,
20468 onFocus : Roo.emptyFn,
20470 hideMode:'offsets',
20474 // blacklist + whitelisted elements..
20481 * Protected method that will not generally be called directly. It
20482 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20483 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20485 getDocMarkup : function(){
20489 // inherit styels from page...??
20490 if (this.stylesheets === false) {
20492 Roo.get(document.head).select('style').each(function(node) {
20493 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20496 Roo.get(document.head).select('link').each(function(node) {
20497 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20500 } else if (!this.stylesheets.length) {
20502 st = '<style type="text/css">' +
20503 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20509 st += '<style type="text/css">' +
20510 'IMG { cursor: pointer } ' +
20514 return '<html><head>' + st +
20515 //<style type="text/css">' +
20516 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20518 ' </head><body class="roo-htmleditor-body"></body></html>';
20522 onRender : function(ct, position)
20525 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20526 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20529 this.el.dom.style.border = '0 none';
20530 this.el.dom.setAttribute('tabIndex', -1);
20531 this.el.addClass('x-hidden hide');
20535 if(Roo.isIE){ // fix IE 1px bogus margin
20536 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20540 this.frameId = Roo.id();
20544 var iframe = this.owner.wrap.createChild({
20546 cls: 'form-control', // bootstrap..
20548 name: this.frameId,
20549 frameBorder : 'no',
20550 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20555 this.iframe = iframe.dom;
20557 this.assignDocWin();
20559 this.doc.designMode = 'on';
20562 this.doc.write(this.getDocMarkup());
20566 var task = { // must defer to wait for browser to be ready
20568 //console.log("run task?" + this.doc.readyState);
20569 this.assignDocWin();
20570 if(this.doc.body || this.doc.readyState == 'complete'){
20572 this.doc.designMode="on";
20576 Roo.TaskMgr.stop(task);
20577 this.initEditor.defer(10, this);
20584 Roo.TaskMgr.start(task);
20589 onResize : function(w, h)
20591 Roo.log('resize: ' +w + ',' + h );
20592 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20596 if(typeof w == 'number'){
20598 this.iframe.style.width = w + 'px';
20600 if(typeof h == 'number'){
20602 this.iframe.style.height = h + 'px';
20604 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20611 * Toggles the editor between standard and source edit mode.
20612 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20614 toggleSourceEdit : function(sourceEditMode){
20616 this.sourceEditMode = sourceEditMode === true;
20618 if(this.sourceEditMode){
20620 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20623 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20624 //this.iframe.className = '';
20627 //this.setSize(this.owner.wrap.getSize());
20628 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20635 * Protected method that will not generally be called directly. If you need/want
20636 * custom HTML cleanup, this is the method you should override.
20637 * @param {String} html The HTML to be cleaned
20638 * return {String} The cleaned HTML
20640 cleanHtml : function(html){
20641 html = String(html);
20642 if(html.length > 5){
20643 if(Roo.isSafari){ // strip safari nonsense
20644 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20647 if(html == ' '){
20654 * HTML Editor -> Textarea
20655 * Protected method that will not generally be called directly. Syncs the contents
20656 * of the editor iframe with the textarea.
20658 syncValue : function(){
20659 if(this.initialized){
20660 var bd = (this.doc.body || this.doc.documentElement);
20661 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20662 var html = bd.innerHTML;
20664 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20665 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20667 html = '<div style="'+m[0]+'">' + html + '</div>';
20670 html = this.cleanHtml(html);
20671 // fix up the special chars.. normaly like back quotes in word...
20672 // however we do not want to do this with chinese..
20673 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20674 var cc = b.charCodeAt();
20676 (cc >= 0x4E00 && cc < 0xA000 ) ||
20677 (cc >= 0x3400 && cc < 0x4E00 ) ||
20678 (cc >= 0xf900 && cc < 0xfb00 )
20684 if(this.owner.fireEvent('beforesync', this, html) !== false){
20685 this.el.dom.value = html;
20686 this.owner.fireEvent('sync', this, html);
20692 * Protected method that will not generally be called directly. Pushes the value of the textarea
20693 * into the iframe editor.
20695 pushValue : function(){
20696 if(this.initialized){
20697 var v = this.el.dom.value.trim();
20699 // if(v.length < 1){
20703 if(this.owner.fireEvent('beforepush', this, v) !== false){
20704 var d = (this.doc.body || this.doc.documentElement);
20706 this.cleanUpPaste();
20707 this.el.dom.value = d.innerHTML;
20708 this.owner.fireEvent('push', this, v);
20714 deferFocus : function(){
20715 this.focus.defer(10, this);
20719 focus : function(){
20720 if(this.win && !this.sourceEditMode){
20727 assignDocWin: function()
20729 var iframe = this.iframe;
20732 this.doc = iframe.contentWindow.document;
20733 this.win = iframe.contentWindow;
20735 // if (!Roo.get(this.frameId)) {
20738 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20739 // this.win = Roo.get(this.frameId).dom.contentWindow;
20741 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20745 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20746 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20751 initEditor : function(){
20752 //console.log("INIT EDITOR");
20753 this.assignDocWin();
20757 this.doc.designMode="on";
20759 this.doc.write(this.getDocMarkup());
20762 var dbody = (this.doc.body || this.doc.documentElement);
20763 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20764 // this copies styles from the containing element into thsi one..
20765 // not sure why we need all of this..
20766 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20768 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20769 //ss['background-attachment'] = 'fixed'; // w3c
20770 dbody.bgProperties = 'fixed'; // ie
20771 //Roo.DomHelper.applyStyles(dbody, ss);
20772 Roo.EventManager.on(this.doc, {
20773 //'mousedown': this.onEditorEvent,
20774 'mouseup': this.onEditorEvent,
20775 'dblclick': this.onEditorEvent,
20776 'click': this.onEditorEvent,
20777 'keyup': this.onEditorEvent,
20782 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20784 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20785 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20787 this.initialized = true;
20789 this.owner.fireEvent('initialize', this);
20794 onDestroy : function(){
20800 //for (var i =0; i < this.toolbars.length;i++) {
20801 // // fixme - ask toolbars for heights?
20802 // this.toolbars[i].onDestroy();
20805 //this.wrap.dom.innerHTML = '';
20806 //this.wrap.remove();
20811 onFirstFocus : function(){
20813 this.assignDocWin();
20816 this.activated = true;
20819 if(Roo.isGecko){ // prevent silly gecko errors
20821 var s = this.win.getSelection();
20822 if(!s.focusNode || s.focusNode.nodeType != 3){
20823 var r = s.getRangeAt(0);
20824 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20829 this.execCmd('useCSS', true);
20830 this.execCmd('styleWithCSS', false);
20833 this.owner.fireEvent('activate', this);
20837 adjustFont: function(btn){
20838 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20839 //if(Roo.isSafari){ // safari
20842 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20843 if(Roo.isSafari){ // safari
20844 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20845 v = (v < 10) ? 10 : v;
20846 v = (v > 48) ? 48 : v;
20847 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20852 v = Math.max(1, v+adjust);
20854 this.execCmd('FontSize', v );
20857 onEditorEvent : function(e)
20859 this.owner.fireEvent('editorevent', this, e);
20860 // this.updateToolbar();
20861 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20864 insertTag : function(tg)
20866 // could be a bit smarter... -> wrap the current selected tRoo..
20867 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20869 range = this.createRange(this.getSelection());
20870 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20871 wrappingNode.appendChild(range.extractContents());
20872 range.insertNode(wrappingNode);
20879 this.execCmd("formatblock", tg);
20883 insertText : function(txt)
20887 var range = this.createRange();
20888 range.deleteContents();
20889 //alert(Sender.getAttribute('label'));
20891 range.insertNode(this.doc.createTextNode(txt));
20897 * Executes a Midas editor command on the editor document and performs necessary focus and
20898 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20899 * @param {String} cmd The Midas command
20900 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20902 relayCmd : function(cmd, value){
20904 this.execCmd(cmd, value);
20905 this.owner.fireEvent('editorevent', this);
20906 //this.updateToolbar();
20907 this.owner.deferFocus();
20911 * Executes a Midas editor command directly on the editor document.
20912 * For visual commands, you should use {@link #relayCmd} instead.
20913 * <b>This should only be called after the editor is initialized.</b>
20914 * @param {String} cmd The Midas command
20915 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20917 execCmd : function(cmd, value){
20918 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20925 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20927 * @param {String} text | dom node..
20929 insertAtCursor : function(text)
20934 if(!this.activated){
20940 var r = this.doc.selection.createRange();
20951 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20955 // from jquery ui (MIT licenced)
20957 var win = this.win;
20959 if (win.getSelection && win.getSelection().getRangeAt) {
20960 range = win.getSelection().getRangeAt(0);
20961 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20962 range.insertNode(node);
20963 } else if (win.document.selection && win.document.selection.createRange) {
20964 // no firefox support
20965 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20966 win.document.selection.createRange().pasteHTML(txt);
20968 // no firefox support
20969 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20970 this.execCmd('InsertHTML', txt);
20979 mozKeyPress : function(e){
20981 var c = e.getCharCode(), cmd;
20984 c = String.fromCharCode(c).toLowerCase();
20998 this.cleanUpPaste.defer(100, this);
21006 e.preventDefault();
21014 fixKeys : function(){ // load time branching for fastest keydown performance
21016 return function(e){
21017 var k = e.getKey(), r;
21020 r = this.doc.selection.createRange();
21023 r.pasteHTML('    ');
21030 r = this.doc.selection.createRange();
21032 var target = r.parentElement();
21033 if(!target || target.tagName.toLowerCase() != 'li'){
21035 r.pasteHTML('<br />');
21041 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21042 this.cleanUpPaste.defer(100, this);
21048 }else if(Roo.isOpera){
21049 return function(e){
21050 var k = e.getKey();
21054 this.execCmd('InsertHTML','    ');
21057 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21058 this.cleanUpPaste.defer(100, this);
21063 }else if(Roo.isSafari){
21064 return function(e){
21065 var k = e.getKey();
21069 this.execCmd('InsertText','\t');
21073 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21074 this.cleanUpPaste.defer(100, this);
21082 getAllAncestors: function()
21084 var p = this.getSelectedNode();
21087 a.push(p); // push blank onto stack..
21088 p = this.getParentElement();
21092 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21096 a.push(this.doc.body);
21100 lastSelNode : false,
21103 getSelection : function()
21105 this.assignDocWin();
21106 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21109 getSelectedNode: function()
21111 // this may only work on Gecko!!!
21113 // should we cache this!!!!
21118 var range = this.createRange(this.getSelection()).cloneRange();
21121 var parent = range.parentElement();
21123 var testRange = range.duplicate();
21124 testRange.moveToElementText(parent);
21125 if (testRange.inRange(range)) {
21128 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21131 parent = parent.parentElement;
21136 // is ancestor a text element.
21137 var ac = range.commonAncestorContainer;
21138 if (ac.nodeType == 3) {
21139 ac = ac.parentNode;
21142 var ar = ac.childNodes;
21145 var other_nodes = [];
21146 var has_other_nodes = false;
21147 for (var i=0;i<ar.length;i++) {
21148 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21151 // fullly contained node.
21153 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21158 // probably selected..
21159 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21160 other_nodes.push(ar[i]);
21164 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21169 has_other_nodes = true;
21171 if (!nodes.length && other_nodes.length) {
21172 nodes= other_nodes;
21174 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21180 createRange: function(sel)
21182 // this has strange effects when using with
21183 // top toolbar - not sure if it's a great idea.
21184 //this.editor.contentWindow.focus();
21185 if (typeof sel != "undefined") {
21187 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21189 return this.doc.createRange();
21192 return this.doc.createRange();
21195 getParentElement: function()
21198 this.assignDocWin();
21199 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21201 var range = this.createRange(sel);
21204 var p = range.commonAncestorContainer;
21205 while (p.nodeType == 3) { // text node
21216 * Range intersection.. the hard stuff...
21220 * [ -- selected range --- ]
21224 * if end is before start or hits it. fail.
21225 * if start is after end or hits it fail.
21227 * if either hits (but other is outside. - then it's not
21233 // @see http://www.thismuchiknow.co.uk/?p=64.
21234 rangeIntersectsNode : function(range, node)
21236 var nodeRange = node.ownerDocument.createRange();
21238 nodeRange.selectNode(node);
21240 nodeRange.selectNodeContents(node);
21243 var rangeStartRange = range.cloneRange();
21244 rangeStartRange.collapse(true);
21246 var rangeEndRange = range.cloneRange();
21247 rangeEndRange.collapse(false);
21249 var nodeStartRange = nodeRange.cloneRange();
21250 nodeStartRange.collapse(true);
21252 var nodeEndRange = nodeRange.cloneRange();
21253 nodeEndRange.collapse(false);
21255 return rangeStartRange.compareBoundaryPoints(
21256 Range.START_TO_START, nodeEndRange) == -1 &&
21257 rangeEndRange.compareBoundaryPoints(
21258 Range.START_TO_START, nodeStartRange) == 1;
21262 rangeCompareNode : function(range, node)
21264 var nodeRange = node.ownerDocument.createRange();
21266 nodeRange.selectNode(node);
21268 nodeRange.selectNodeContents(node);
21272 range.collapse(true);
21274 nodeRange.collapse(true);
21276 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21277 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21279 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21281 var nodeIsBefore = ss == 1;
21282 var nodeIsAfter = ee == -1;
21284 if (nodeIsBefore && nodeIsAfter) {
21287 if (!nodeIsBefore && nodeIsAfter) {
21288 return 1; //right trailed.
21291 if (nodeIsBefore && !nodeIsAfter) {
21292 return 2; // left trailed.
21298 // private? - in a new class?
21299 cleanUpPaste : function()
21301 // cleans up the whole document..
21302 Roo.log('cleanuppaste');
21304 this.cleanUpChildren(this.doc.body);
21305 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21306 if (clean != this.doc.body.innerHTML) {
21307 this.doc.body.innerHTML = clean;
21312 cleanWordChars : function(input) {// change the chars to hex code
21313 var he = Roo.HtmlEditorCore;
21315 var output = input;
21316 Roo.each(he.swapCodes, function(sw) {
21317 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21319 output = output.replace(swapper, sw[1]);
21326 cleanUpChildren : function (n)
21328 if (!n.childNodes.length) {
21331 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21332 this.cleanUpChild(n.childNodes[i]);
21339 cleanUpChild : function (node)
21342 //console.log(node);
21343 if (node.nodeName == "#text") {
21344 // clean up silly Windows -- stuff?
21347 if (node.nodeName == "#comment") {
21348 node.parentNode.removeChild(node);
21349 // clean up silly Windows -- stuff?
21352 var lcname = node.tagName.toLowerCase();
21353 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21354 // whitelist of tags..
21356 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21358 node.parentNode.removeChild(node);
21363 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21365 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21366 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21368 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21369 // remove_keep_children = true;
21372 if (remove_keep_children) {
21373 this.cleanUpChildren(node);
21374 // inserts everything just before this node...
21375 while (node.childNodes.length) {
21376 var cn = node.childNodes[0];
21377 node.removeChild(cn);
21378 node.parentNode.insertBefore(cn, node);
21380 node.parentNode.removeChild(node);
21384 if (!node.attributes || !node.attributes.length) {
21385 this.cleanUpChildren(node);
21389 function cleanAttr(n,v)
21392 if (v.match(/^\./) || v.match(/^\//)) {
21395 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21398 if (v.match(/^#/)) {
21401 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21402 node.removeAttribute(n);
21406 var cwhite = this.cwhite;
21407 var cblack = this.cblack;
21409 function cleanStyle(n,v)
21411 if (v.match(/expression/)) { //XSS?? should we even bother..
21412 node.removeAttribute(n);
21416 var parts = v.split(/;/);
21419 Roo.each(parts, function(p) {
21420 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21424 var l = p.split(':').shift().replace(/\s+/g,'');
21425 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21427 if ( cwhite.length && cblack.indexOf(l) > -1) {
21428 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21429 //node.removeAttribute(n);
21433 // only allow 'c whitelisted system attributes'
21434 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21435 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21436 //node.removeAttribute(n);
21446 if (clean.length) {
21447 node.setAttribute(n, clean.join(';'));
21449 node.removeAttribute(n);
21455 for (var i = node.attributes.length-1; i > -1 ; i--) {
21456 var a = node.attributes[i];
21459 if (a.name.toLowerCase().substr(0,2)=='on') {
21460 node.removeAttribute(a.name);
21463 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21464 node.removeAttribute(a.name);
21467 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21468 cleanAttr(a.name,a.value); // fixme..
21471 if (a.name == 'style') {
21472 cleanStyle(a.name,a.value);
21475 /// clean up MS crap..
21476 // tecnically this should be a list of valid class'es..
21479 if (a.name == 'class') {
21480 if (a.value.match(/^Mso/)) {
21481 node.className = '';
21484 if (a.value.match(/body/)) {
21485 node.className = '';
21496 this.cleanUpChildren(node);
21502 * Clean up MS wordisms...
21504 cleanWord : function(node)
21509 this.cleanWord(this.doc.body);
21512 if (node.nodeName == "#text") {
21513 // clean up silly Windows -- stuff?
21516 if (node.nodeName == "#comment") {
21517 node.parentNode.removeChild(node);
21518 // clean up silly Windows -- stuff?
21522 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21523 node.parentNode.removeChild(node);
21527 // remove - but keep children..
21528 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21529 while (node.childNodes.length) {
21530 var cn = node.childNodes[0];
21531 node.removeChild(cn);
21532 node.parentNode.insertBefore(cn, node);
21534 node.parentNode.removeChild(node);
21535 this.iterateChildren(node, this.cleanWord);
21539 if (node.className.length) {
21541 var cn = node.className.split(/\W+/);
21543 Roo.each(cn, function(cls) {
21544 if (cls.match(/Mso[a-zA-Z]+/)) {
21549 node.className = cna.length ? cna.join(' ') : '';
21551 node.removeAttribute("class");
21555 if (node.hasAttribute("lang")) {
21556 node.removeAttribute("lang");
21559 if (node.hasAttribute("style")) {
21561 var styles = node.getAttribute("style").split(";");
21563 Roo.each(styles, function(s) {
21564 if (!s.match(/:/)) {
21567 var kv = s.split(":");
21568 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21571 // what ever is left... we allow.
21574 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21575 if (!nstyle.length) {
21576 node.removeAttribute('style');
21579 this.iterateChildren(node, this.cleanWord);
21585 * iterateChildren of a Node, calling fn each time, using this as the scole..
21586 * @param {DomNode} node node to iterate children of.
21587 * @param {Function} fn method of this class to call on each item.
21589 iterateChildren : function(node, fn)
21591 if (!node.childNodes.length) {
21594 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21595 fn.call(this, node.childNodes[i])
21601 * cleanTableWidths.
21603 * Quite often pasting from word etc.. results in tables with column and widths.
21604 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21607 cleanTableWidths : function(node)
21612 this.cleanTableWidths(this.doc.body);
21617 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21620 Roo.log(node.tagName);
21621 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21622 this.iterateChildren(node, this.cleanTableWidths);
21625 if (node.hasAttribute('width')) {
21626 node.removeAttribute('width');
21630 if (node.hasAttribute("style")) {
21633 var styles = node.getAttribute("style").split(";");
21635 Roo.each(styles, function(s) {
21636 if (!s.match(/:/)) {
21639 var kv = s.split(":");
21640 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21643 // what ever is left... we allow.
21646 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21647 if (!nstyle.length) {
21648 node.removeAttribute('style');
21652 this.iterateChildren(node, this.cleanTableWidths);
21660 domToHTML : function(currentElement, depth, nopadtext) {
21662 depth = depth || 0;
21663 nopadtext = nopadtext || false;
21665 if (!currentElement) {
21666 return this.domToHTML(this.doc.body);
21669 //Roo.log(currentElement);
21671 var allText = false;
21672 var nodeName = currentElement.nodeName;
21673 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21675 if (nodeName == '#text') {
21677 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21682 if (nodeName != 'BODY') {
21685 // Prints the node tagName, such as <A>, <IMG>, etc
21688 for(i = 0; i < currentElement.attributes.length;i++) {
21690 var aname = currentElement.attributes.item(i).name;
21691 if (!currentElement.attributes.item(i).value.length) {
21694 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21697 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21706 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21709 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21714 // Traverse the tree
21716 var currentElementChild = currentElement.childNodes.item(i);
21717 var allText = true;
21718 var innerHTML = '';
21720 while (currentElementChild) {
21721 // Formatting code (indent the tree so it looks nice on the screen)
21722 var nopad = nopadtext;
21723 if (lastnode == 'SPAN') {
21727 if (currentElementChild.nodeName == '#text') {
21728 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21729 toadd = nopadtext ? toadd : toadd.trim();
21730 if (!nopad && toadd.length > 80) {
21731 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21733 innerHTML += toadd;
21736 currentElementChild = currentElement.childNodes.item(i);
21742 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21744 // Recursively traverse the tree structure of the child node
21745 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21746 lastnode = currentElementChild.nodeName;
21748 currentElementChild=currentElement.childNodes.item(i);
21754 // The remaining code is mostly for formatting the tree
21755 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21760 ret+= "</"+tagName+">";
21766 applyBlacklists : function()
21768 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21769 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21773 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21774 if (b.indexOf(tag) > -1) {
21777 this.white.push(tag);
21781 Roo.each(w, function(tag) {
21782 if (b.indexOf(tag) > -1) {
21785 if (this.white.indexOf(tag) > -1) {
21788 this.white.push(tag);
21793 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21794 if (w.indexOf(tag) > -1) {
21797 this.black.push(tag);
21801 Roo.each(b, function(tag) {
21802 if (w.indexOf(tag) > -1) {
21805 if (this.black.indexOf(tag) > -1) {
21808 this.black.push(tag);
21813 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21814 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21818 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21819 if (b.indexOf(tag) > -1) {
21822 this.cwhite.push(tag);
21826 Roo.each(w, function(tag) {
21827 if (b.indexOf(tag) > -1) {
21830 if (this.cwhite.indexOf(tag) > -1) {
21833 this.cwhite.push(tag);
21838 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21839 if (w.indexOf(tag) > -1) {
21842 this.cblack.push(tag);
21846 Roo.each(b, function(tag) {
21847 if (w.indexOf(tag) > -1) {
21850 if (this.cblack.indexOf(tag) > -1) {
21853 this.cblack.push(tag);
21858 setStylesheets : function(stylesheets)
21860 if(typeof(stylesheets) == 'string'){
21861 Roo.get(this.iframe.contentDocument.head).createChild({
21863 rel : 'stylesheet',
21872 Roo.each(stylesheets, function(s) {
21877 Roo.get(_this.iframe.contentDocument.head).createChild({
21879 rel : 'stylesheet',
21888 removeStylesheets : function()
21892 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21897 // hide stuff that is not compatible
21911 * @event specialkey
21915 * @cfg {String} fieldClass @hide
21918 * @cfg {String} focusClass @hide
21921 * @cfg {String} autoCreate @hide
21924 * @cfg {String} inputType @hide
21927 * @cfg {String} invalidClass @hide
21930 * @cfg {String} invalidText @hide
21933 * @cfg {String} msgFx @hide
21936 * @cfg {String} validateOnBlur @hide
21940 Roo.HtmlEditorCore.white = [
21941 'area', 'br', 'img', 'input', 'hr', 'wbr',
21943 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21944 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21945 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21946 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21947 'table', 'ul', 'xmp',
21949 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21952 'dir', 'menu', 'ol', 'ul', 'dl',
21958 Roo.HtmlEditorCore.black = [
21959 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21961 'base', 'basefont', 'bgsound', 'blink', 'body',
21962 'frame', 'frameset', 'head', 'html', 'ilayer',
21963 'iframe', 'layer', 'link', 'meta', 'object',
21964 'script', 'style' ,'title', 'xml' // clean later..
21966 Roo.HtmlEditorCore.clean = [
21967 'script', 'style', 'title', 'xml'
21969 Roo.HtmlEditorCore.remove = [
21974 Roo.HtmlEditorCore.ablack = [
21978 Roo.HtmlEditorCore.aclean = [
21979 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21983 Roo.HtmlEditorCore.pwhite= [
21984 'http', 'https', 'mailto'
21987 // white listed style attributes.
21988 Roo.HtmlEditorCore.cwhite= [
21989 // 'text-align', /// default is to allow most things..
21995 // black listed style attributes.
21996 Roo.HtmlEditorCore.cblack= [
21997 // 'font-size' -- this can be set by the project
22001 Roo.HtmlEditorCore.swapCodes =[
22012 //<script type="text/javascript">
22015 * Ext JS Library 1.1.1
22016 * Copyright(c) 2006-2007, Ext JS, LLC.
22022 Roo.form.HtmlEditor = function(config){
22026 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22028 if (!this.toolbars) {
22029 this.toolbars = [];
22031 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22037 * @class Roo.form.HtmlEditor
22038 * @extends Roo.form.Field
22039 * Provides a lightweight HTML Editor component.
22041 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22043 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22044 * supported by this editor.</b><br/><br/>
22045 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22046 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22048 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22050 * @cfg {Boolean} clearUp
22054 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22059 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22064 * @cfg {Number} height (in pixels)
22068 * @cfg {Number} width (in pixels)
22073 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22076 stylesheets: false,
22080 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22085 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22091 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22096 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22104 // private properties
22105 validationEvent : false,
22107 initialized : false,
22110 onFocus : Roo.emptyFn,
22112 hideMode:'offsets',
22114 actionMode : 'container', // defaults to hiding it...
22116 defaultAutoCreate : { // modified by initCompnoent..
22118 style:"width:500px;height:300px;",
22119 autocomplete: "new-password"
22123 initComponent : function(){
22126 * @event initialize
22127 * Fires when the editor is fully initialized (including the iframe)
22128 * @param {HtmlEditor} this
22133 * Fires when the editor is first receives the focus. Any insertion must wait
22134 * until after this event.
22135 * @param {HtmlEditor} this
22139 * @event beforesync
22140 * Fires before the textarea is updated with content from the editor iframe. Return false
22141 * to cancel the sync.
22142 * @param {HtmlEditor} this
22143 * @param {String} html
22147 * @event beforepush
22148 * Fires before the iframe editor is updated with content from the textarea. Return false
22149 * to cancel the push.
22150 * @param {HtmlEditor} this
22151 * @param {String} html
22156 * Fires when the textarea is updated with content from the editor iframe.
22157 * @param {HtmlEditor} this
22158 * @param {String} html
22163 * Fires when the iframe editor is updated with content from the textarea.
22164 * @param {HtmlEditor} this
22165 * @param {String} html
22169 * @event editmodechange
22170 * Fires when the editor switches edit modes
22171 * @param {HtmlEditor} this
22172 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22174 editmodechange: true,
22176 * @event editorevent
22177 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22178 * @param {HtmlEditor} this
22182 * @event firstfocus
22183 * Fires when on first focus - needed by toolbars..
22184 * @param {HtmlEditor} this
22189 * Auto save the htmlEditor value as a file into Events
22190 * @param {HtmlEditor} this
22194 * @event savedpreview
22195 * preview the saved version of htmlEditor
22196 * @param {HtmlEditor} this
22198 savedpreview: true,
22201 * @event stylesheetsclick
22202 * Fires when press the Sytlesheets button
22203 * @param {Roo.HtmlEditorCore} this
22205 stylesheetsclick: true
22207 this.defaultAutoCreate = {
22209 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22210 autocomplete: "new-password"
22215 * Protected method that will not generally be called directly. It
22216 * is called when the editor creates its toolbar. Override this method if you need to
22217 * add custom toolbar buttons.
22218 * @param {HtmlEditor} editor
22220 createToolbar : function(editor){
22221 Roo.log("create toolbars");
22222 if (!editor.toolbars || !editor.toolbars.length) {
22223 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22226 for (var i =0 ; i < editor.toolbars.length;i++) {
22227 editor.toolbars[i] = Roo.factory(
22228 typeof(editor.toolbars[i]) == 'string' ?
22229 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22230 Roo.form.HtmlEditor);
22231 editor.toolbars[i].init(editor);
22239 onRender : function(ct, position)
22242 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22244 this.wrap = this.el.wrap({
22245 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22248 this.editorcore.onRender(ct, position);
22250 if (this.resizable) {
22251 this.resizeEl = new Roo.Resizable(this.wrap, {
22255 minHeight : this.height,
22256 height: this.height,
22257 handles : this.resizable,
22260 resize : function(r, w, h) {
22261 _t.onResize(w,h); // -something
22267 this.createToolbar(this);
22271 this.setSize(this.wrap.getSize());
22273 if (this.resizeEl) {
22274 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22275 // should trigger onReize..
22278 this.keyNav = new Roo.KeyNav(this.el, {
22280 "tab" : function(e){
22281 e.preventDefault();
22283 var value = this.getValue();
22285 var start = this.el.dom.selectionStart;
22286 var end = this.el.dom.selectionEnd;
22290 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22291 this.el.dom.setSelectionRange(end + 1, end + 1);
22295 var f = value.substring(0, start).split("\t");
22297 if(f.pop().length != 0){
22301 this.setValue(f.join("\t") + value.substring(end));
22302 this.el.dom.setSelectionRange(start - 1, start - 1);
22306 "home" : function(e){
22307 e.preventDefault();
22309 var curr = this.el.dom.selectionStart;
22310 var lines = this.getValue().split("\n");
22317 this.el.dom.setSelectionRange(0, 0);
22323 for (var i = 0; i < lines.length;i++) {
22324 pos += lines[i].length;
22334 pos -= lines[i].length;
22340 this.el.dom.setSelectionRange(pos, pos);
22344 this.el.dom.selectionStart = pos;
22345 this.el.dom.selectionEnd = curr;
22348 "end" : function(e){
22349 e.preventDefault();
22351 var curr = this.el.dom.selectionStart;
22352 var lines = this.getValue().split("\n");
22359 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22365 for (var i = 0; i < lines.length;i++) {
22367 pos += lines[i].length;
22381 this.el.dom.setSelectionRange(pos, pos);
22385 this.el.dom.selectionStart = curr;
22386 this.el.dom.selectionEnd = pos;
22391 doRelay : function(foo, bar, hname){
22392 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22398 // if(this.autosave && this.w){
22399 // this.autoSaveFn = setInterval(this.autosave, 1000);
22404 onResize : function(w, h)
22406 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22411 if(typeof w == 'number'){
22412 var aw = w - this.wrap.getFrameWidth('lr');
22413 this.el.setWidth(this.adjustWidth('textarea', aw));
22416 if(typeof h == 'number'){
22418 for (var i =0; i < this.toolbars.length;i++) {
22419 // fixme - ask toolbars for heights?
22420 tbh += this.toolbars[i].tb.el.getHeight();
22421 if (this.toolbars[i].footer) {
22422 tbh += this.toolbars[i].footer.el.getHeight();
22429 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22430 ah -= 5; // knock a few pixes off for look..
22432 this.el.setHeight(this.adjustWidth('textarea', ah));
22436 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22437 this.editorcore.onResize(ew,eh);
22442 * Toggles the editor between standard and source edit mode.
22443 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22445 toggleSourceEdit : function(sourceEditMode)
22447 this.editorcore.toggleSourceEdit(sourceEditMode);
22449 if(this.editorcore.sourceEditMode){
22450 Roo.log('editor - showing textarea');
22453 // Roo.log(this.syncValue());
22454 this.editorcore.syncValue();
22455 this.el.removeClass('x-hidden');
22456 this.el.dom.removeAttribute('tabIndex');
22459 for (var i = 0; i < this.toolbars.length; i++) {
22460 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22461 this.toolbars[i].tb.hide();
22462 this.toolbars[i].footer.hide();
22467 Roo.log('editor - hiding textarea');
22469 // Roo.log(this.pushValue());
22470 this.editorcore.pushValue();
22472 this.el.addClass('x-hidden');
22473 this.el.dom.setAttribute('tabIndex', -1);
22475 for (var i = 0; i < this.toolbars.length; i++) {
22476 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22477 this.toolbars[i].tb.show();
22478 this.toolbars[i].footer.show();
22482 //this.deferFocus();
22485 this.setSize(this.wrap.getSize());
22486 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22488 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22491 // private (for BoxComponent)
22492 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22494 // private (for BoxComponent)
22495 getResizeEl : function(){
22499 // private (for BoxComponent)
22500 getPositionEl : function(){
22505 initEvents : function(){
22506 this.originalValue = this.getValue();
22510 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22513 markInvalid : Roo.emptyFn,
22515 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22518 clearInvalid : Roo.emptyFn,
22520 setValue : function(v){
22521 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22522 this.editorcore.pushValue();
22527 deferFocus : function(){
22528 this.focus.defer(10, this);
22532 focus : function(){
22533 this.editorcore.focus();
22539 onDestroy : function(){
22545 for (var i =0; i < this.toolbars.length;i++) {
22546 // fixme - ask toolbars for heights?
22547 this.toolbars[i].onDestroy();
22550 this.wrap.dom.innerHTML = '';
22551 this.wrap.remove();
22556 onFirstFocus : function(){
22557 //Roo.log("onFirstFocus");
22558 this.editorcore.onFirstFocus();
22559 for (var i =0; i < this.toolbars.length;i++) {
22560 this.toolbars[i].onFirstFocus();
22566 syncValue : function()
22568 this.editorcore.syncValue();
22571 pushValue : function()
22573 this.editorcore.pushValue();
22576 setStylesheets : function(stylesheets)
22578 this.editorcore.setStylesheets(stylesheets);
22581 removeStylesheets : function()
22583 this.editorcore.removeStylesheets();
22587 // hide stuff that is not compatible
22601 * @event specialkey
22605 * @cfg {String} fieldClass @hide
22608 * @cfg {String} focusClass @hide
22611 * @cfg {String} autoCreate @hide
22614 * @cfg {String} inputType @hide
22617 * @cfg {String} invalidClass @hide
22620 * @cfg {String} invalidText @hide
22623 * @cfg {String} msgFx @hide
22626 * @cfg {String} validateOnBlur @hide
22630 // <script type="text/javascript">
22633 * Ext JS Library 1.1.1
22634 * Copyright(c) 2006-2007, Ext JS, LLC.
22640 * @class Roo.form.HtmlEditorToolbar1
22645 new Roo.form.HtmlEditor({
22648 new Roo.form.HtmlEditorToolbar1({
22649 disable : { fonts: 1 , format: 1, ..., ... , ...],
22655 * @cfg {Object} disable List of elements to disable..
22656 * @cfg {Array} btns List of additional buttons.
22660 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22663 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22666 Roo.apply(this, config);
22668 // default disabled, based on 'good practice'..
22669 this.disable = this.disable || {};
22670 Roo.applyIf(this.disable, {
22673 specialElements : true
22677 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22678 // dont call parent... till later.
22681 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
22688 editorcore : false,
22690 * @cfg {Object} disable List of toolbar elements to disable
22697 * @cfg {String} createLinkText The default text for the create link prompt
22699 createLinkText : 'Please enter the URL for the link:',
22701 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22703 defaultLinkValue : 'http:/'+'/',
22707 * @cfg {Array} fontFamilies An array of available font families
22725 // "á" , ?? a acute?
22730 "°" // , // degrees
22732 // "é" , // e ecute
22733 // "ú" , // u ecute?
22736 specialElements : [
22738 text: "Insert Table",
22741 ihtml : '<table><tr><td>Cell</td></tr></table>'
22745 text: "Insert Image",
22748 ihtml : '<img src="about:blank"/>'
22757 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
22758 "input:submit", "input:button", "select", "textarea", "label" ],
22761 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
22763 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22771 * @cfg {String} defaultFont default font to use.
22773 defaultFont: 'tahoma',
22775 fontSelect : false,
22778 formatCombo : false,
22780 init : function(editor)
22782 this.editor = editor;
22783 this.editorcore = editor.editorcore ? editor.editorcore : editor;
22784 var editorcore = this.editorcore;
22788 var fid = editorcore.frameId;
22790 function btn(id, toggle, handler){
22791 var xid = fid + '-'+ id ;
22795 cls : 'x-btn-icon x-edit-'+id,
22796 enableToggle:toggle !== false,
22797 scope: _t, // was editor...
22798 handler:handler||_t.relayBtnCmd,
22799 clickEvent:'mousedown',
22800 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22807 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22809 // stop form submits
22810 tb.el.on('click', function(e){
22811 e.preventDefault(); // what does this do?
22814 if(!this.disable.font) { // && !Roo.isSafari){
22815 /* why no safari for fonts
22816 editor.fontSelect = tb.el.createChild({
22819 cls:'x-font-select',
22820 html: this.createFontOptions()
22823 editor.fontSelect.on('change', function(){
22824 var font = editor.fontSelect.dom.value;
22825 editor.relayCmd('fontname', font);
22826 editor.deferFocus();
22830 editor.fontSelect.dom,
22836 if(!this.disable.formats){
22837 this.formatCombo = new Roo.form.ComboBox({
22838 store: new Roo.data.SimpleStore({
22841 data : this.formats // from states.js
22845 //autoCreate : {tag: "div", size: "20"},
22846 displayField:'tag',
22850 triggerAction: 'all',
22851 emptyText:'Add tag',
22852 selectOnFocus:true,
22855 'select': function(c, r, i) {
22856 editorcore.insertTag(r.get('tag'));
22862 tb.addField(this.formatCombo);
22866 if(!this.disable.format){
22871 btn('strikethrough')
22874 if(!this.disable.fontSize){
22879 btn('increasefontsize', false, editorcore.adjustFont),
22880 btn('decreasefontsize', false, editorcore.adjustFont)
22885 if(!this.disable.colors){
22888 id:editorcore.frameId +'-forecolor',
22889 cls:'x-btn-icon x-edit-forecolor',
22890 clickEvent:'mousedown',
22891 tooltip: this.buttonTips['forecolor'] || undefined,
22893 menu : new Roo.menu.ColorMenu({
22894 allowReselect: true,
22895 focus: Roo.emptyFn,
22898 selectHandler: function(cp, color){
22899 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22900 editor.deferFocus();
22903 clickEvent:'mousedown'
22906 id:editorcore.frameId +'backcolor',
22907 cls:'x-btn-icon x-edit-backcolor',
22908 clickEvent:'mousedown',
22909 tooltip: this.buttonTips['backcolor'] || undefined,
22911 menu : new Roo.menu.ColorMenu({
22912 focus: Roo.emptyFn,
22915 allowReselect: true,
22916 selectHandler: function(cp, color){
22918 editorcore.execCmd('useCSS', false);
22919 editorcore.execCmd('hilitecolor', color);
22920 editorcore.execCmd('useCSS', true);
22921 editor.deferFocus();
22923 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
22924 Roo.isSafari || Roo.isIE ? '#'+color : color);
22925 editor.deferFocus();
22929 clickEvent:'mousedown'
22934 // now add all the items...
22937 if(!this.disable.alignments){
22940 btn('justifyleft'),
22941 btn('justifycenter'),
22942 btn('justifyright')
22946 //if(!Roo.isSafari){
22947 if(!this.disable.links){
22950 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
22954 if(!this.disable.lists){
22957 btn('insertorderedlist'),
22958 btn('insertunorderedlist')
22961 if(!this.disable.sourceEdit){
22964 btn('sourceedit', true, function(btn){
22965 this.toggleSourceEdit(btn.pressed);
22972 // special menu.. - needs to be tidied up..
22973 if (!this.disable.special) {
22976 cls: 'x-edit-none',
22982 for (var i =0; i < this.specialChars.length; i++) {
22983 smenu.menu.items.push({
22985 html: this.specialChars[i],
22986 handler: function(a,b) {
22987 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
22988 //editor.insertAtCursor(a.html);
23002 if (!this.disable.cleanStyles) {
23004 cls: 'x-btn-icon x-btn-clear',
23010 for (var i =0; i < this.cleanStyles.length; i++) {
23011 cmenu.menu.items.push({
23012 actiontype : this.cleanStyles[i],
23013 html: 'Remove ' + this.cleanStyles[i],
23014 handler: function(a,b) {
23017 var c = Roo.get(editorcore.doc.body);
23018 c.select('[style]').each(function(s) {
23019 s.dom.style.removeProperty(a.actiontype);
23021 editorcore.syncValue();
23026 cmenu.menu.items.push({
23027 actiontype : 'tablewidths',
23028 html: 'Remove Table Widths',
23029 handler: function(a,b) {
23030 editorcore.cleanTableWidths();
23031 editorcore.syncValue();
23035 cmenu.menu.items.push({
23036 actiontype : 'word',
23037 html: 'Remove MS Word Formating',
23038 handler: function(a,b) {
23039 editorcore.cleanWord();
23040 editorcore.syncValue();
23045 cmenu.menu.items.push({
23046 actiontype : 'all',
23047 html: 'Remove All Styles',
23048 handler: function(a,b) {
23050 var c = Roo.get(editorcore.doc.body);
23051 c.select('[style]').each(function(s) {
23052 s.dom.removeAttribute('style');
23054 editorcore.syncValue();
23059 cmenu.menu.items.push({
23060 actiontype : 'all',
23061 html: 'Remove All CSS Classes',
23062 handler: function(a,b) {
23064 var c = Roo.get(editorcore.doc.body);
23065 c.select('[class]').each(function(s) {
23066 s.dom.className = '';
23068 editorcore.syncValue();
23073 cmenu.menu.items.push({
23074 actiontype : 'tidy',
23075 html: 'Tidy HTML Source',
23076 handler: function(a,b) {
23077 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23078 editorcore.syncValue();
23087 if (!this.disable.specialElements) {
23090 cls: 'x-edit-none',
23095 for (var i =0; i < this.specialElements.length; i++) {
23096 semenu.menu.items.push(
23098 handler: function(a,b) {
23099 editor.insertAtCursor(this.ihtml);
23101 }, this.specialElements[i])
23113 for(var i =0; i< this.btns.length;i++) {
23114 var b = Roo.factory(this.btns[i],Roo.form);
23115 b.cls = 'x-edit-none';
23117 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23118 b.cls += ' x-init-enable';
23121 b.scope = editorcore;
23129 // disable everything...
23131 this.tb.items.each(function(item){
23134 item.id != editorcore.frameId+ '-sourceedit' &&
23135 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23141 this.rendered = true;
23143 // the all the btns;
23144 editor.on('editorevent', this.updateToolbar, this);
23145 // other toolbars need to implement this..
23146 //editor.on('editmodechange', this.updateToolbar, this);
23150 relayBtnCmd : function(btn) {
23151 this.editorcore.relayCmd(btn.cmd);
23153 // private used internally
23154 createLink : function(){
23155 Roo.log("create link?");
23156 var url = prompt(this.createLinkText, this.defaultLinkValue);
23157 if(url && url != 'http:/'+'/'){
23158 this.editorcore.relayCmd('createlink', url);
23164 * Protected method that will not generally be called directly. It triggers
23165 * a toolbar update by reading the markup state of the current selection in the editor.
23167 updateToolbar: function(){
23169 if(!this.editorcore.activated){
23170 this.editor.onFirstFocus();
23174 var btns = this.tb.items.map,
23175 doc = this.editorcore.doc,
23176 frameId = this.editorcore.frameId;
23178 if(!this.disable.font && !Roo.isSafari){
23180 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23181 if(name != this.fontSelect.dom.value){
23182 this.fontSelect.dom.value = name;
23186 if(!this.disable.format){
23187 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23188 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23189 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23190 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23192 if(!this.disable.alignments){
23193 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23194 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23195 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23197 if(!Roo.isSafari && !this.disable.lists){
23198 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23199 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23202 var ans = this.editorcore.getAllAncestors();
23203 if (this.formatCombo) {
23206 var store = this.formatCombo.store;
23207 this.formatCombo.setValue("");
23208 for (var i =0; i < ans.length;i++) {
23209 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23211 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23219 // hides menus... - so this cant be on a menu...
23220 Roo.menu.MenuMgr.hideAll();
23222 //this.editorsyncValue();
23226 createFontOptions : function(){
23227 var buf = [], fs = this.fontFamilies, ff, lc;
23231 for(var i = 0, len = fs.length; i< len; i++){
23233 lc = ff.toLowerCase();
23235 '<option value="',lc,'" style="font-family:',ff,';"',
23236 (this.defaultFont == lc ? ' selected="true">' : '>'),
23241 return buf.join('');
23244 toggleSourceEdit : function(sourceEditMode){
23246 Roo.log("toolbar toogle");
23247 if(sourceEditMode === undefined){
23248 sourceEditMode = !this.sourceEditMode;
23250 this.sourceEditMode = sourceEditMode === true;
23251 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23252 // just toggle the button?
23253 if(btn.pressed !== this.sourceEditMode){
23254 btn.toggle(this.sourceEditMode);
23258 if(sourceEditMode){
23259 Roo.log("disabling buttons");
23260 this.tb.items.each(function(item){
23261 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23267 Roo.log("enabling buttons");
23268 if(this.editorcore.initialized){
23269 this.tb.items.each(function(item){
23275 Roo.log("calling toggole on editor");
23276 // tell the editor that it's been pressed..
23277 this.editor.toggleSourceEdit(sourceEditMode);
23281 * Object collection of toolbar tooltips for the buttons in the editor. The key
23282 * is the command id associated with that button and the value is a valid QuickTips object.
23287 title: 'Bold (Ctrl+B)',
23288 text: 'Make the selected text bold.',
23289 cls: 'x-html-editor-tip'
23292 title: 'Italic (Ctrl+I)',
23293 text: 'Make the selected text italic.',
23294 cls: 'x-html-editor-tip'
23302 title: 'Bold (Ctrl+B)',
23303 text: 'Make the selected text bold.',
23304 cls: 'x-html-editor-tip'
23307 title: 'Italic (Ctrl+I)',
23308 text: 'Make the selected text italic.',
23309 cls: 'x-html-editor-tip'
23312 title: 'Underline (Ctrl+U)',
23313 text: 'Underline the selected text.',
23314 cls: 'x-html-editor-tip'
23317 title: 'Strikethrough',
23318 text: 'Strikethrough the selected text.',
23319 cls: 'x-html-editor-tip'
23321 increasefontsize : {
23322 title: 'Grow Text',
23323 text: 'Increase the font size.',
23324 cls: 'x-html-editor-tip'
23326 decreasefontsize : {
23327 title: 'Shrink Text',
23328 text: 'Decrease the font size.',
23329 cls: 'x-html-editor-tip'
23332 title: 'Text Highlight Color',
23333 text: 'Change the background color of the selected text.',
23334 cls: 'x-html-editor-tip'
23337 title: 'Font Color',
23338 text: 'Change the color of the selected text.',
23339 cls: 'x-html-editor-tip'
23342 title: 'Align Text Left',
23343 text: 'Align text to the left.',
23344 cls: 'x-html-editor-tip'
23347 title: 'Center Text',
23348 text: 'Center text in the editor.',
23349 cls: 'x-html-editor-tip'
23352 title: 'Align Text Right',
23353 text: 'Align text to the right.',
23354 cls: 'x-html-editor-tip'
23356 insertunorderedlist : {
23357 title: 'Bullet List',
23358 text: 'Start a bulleted list.',
23359 cls: 'x-html-editor-tip'
23361 insertorderedlist : {
23362 title: 'Numbered List',
23363 text: 'Start a numbered list.',
23364 cls: 'x-html-editor-tip'
23367 title: 'Hyperlink',
23368 text: 'Make the selected text a hyperlink.',
23369 cls: 'x-html-editor-tip'
23372 title: 'Source Edit',
23373 text: 'Switch to source editing mode.',
23374 cls: 'x-html-editor-tip'
23378 onDestroy : function(){
23381 this.tb.items.each(function(item){
23383 item.menu.removeAll();
23385 item.menu.el.destroy();
23393 onFirstFocus: function() {
23394 this.tb.items.each(function(item){
23403 // <script type="text/javascript">
23406 * Ext JS Library 1.1.1
23407 * Copyright(c) 2006-2007, Ext JS, LLC.
23414 * @class Roo.form.HtmlEditor.ToolbarContext
23419 new Roo.form.HtmlEditor({
23422 { xtype: 'ToolbarStandard', styles : {} }
23423 { xtype: 'ToolbarContext', disable : {} }
23429 * @config : {Object} disable List of elements to disable.. (not done yet.)
23430 * @config : {Object} styles Map of styles available.
23434 Roo.form.HtmlEditor.ToolbarContext = function(config)
23437 Roo.apply(this, config);
23438 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23439 // dont call parent... till later.
23440 this.styles = this.styles || {};
23445 Roo.form.HtmlEditor.ToolbarContext.types = {
23457 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23523 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23528 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23538 style : 'fontFamily',
23539 displayField: 'display',
23540 optname : 'font-family',
23589 // should we really allow this??
23590 // should this just be
23601 style : 'fontFamily',
23602 displayField: 'display',
23603 optname : 'font-family',
23610 style : 'fontFamily',
23611 displayField: 'display',
23612 optname : 'font-family',
23619 style : 'fontFamily',
23620 displayField: 'display',
23621 optname : 'font-family',
23632 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23633 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23635 Roo.form.HtmlEditor.ToolbarContext.options = {
23637 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23638 [ 'Courier New', 'Courier New'],
23639 [ 'Tahoma', 'Tahoma'],
23640 [ 'Times New Roman,serif', 'Times'],
23641 [ 'Verdana','Verdana' ]
23645 // fixme - these need to be configurable..
23648 //Roo.form.HtmlEditor.ToolbarContext.types
23651 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
23658 editorcore : false,
23660 * @cfg {Object} disable List of toolbar elements to disable
23665 * @cfg {Object} styles List of styles
23666 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
23668 * These must be defined in the page, so they get rendered correctly..
23679 init : function(editor)
23681 this.editor = editor;
23682 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23683 var editorcore = this.editorcore;
23685 var fid = editorcore.frameId;
23687 function btn(id, toggle, handler){
23688 var xid = fid + '-'+ id ;
23692 cls : 'x-btn-icon x-edit-'+id,
23693 enableToggle:toggle !== false,
23694 scope: editorcore, // was editor...
23695 handler:handler||editorcore.relayBtnCmd,
23696 clickEvent:'mousedown',
23697 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23701 // create a new element.
23702 var wdiv = editor.wrap.createChild({
23704 }, editor.wrap.dom.firstChild.nextSibling, true);
23706 // can we do this more than once??
23708 // stop form submits
23711 // disable everything...
23712 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23713 this.toolbars = {};
23715 for (var i in ty) {
23717 this.toolbars[i] = this.buildToolbar(ty[i],i);
23719 this.tb = this.toolbars.BODY;
23721 this.buildFooter();
23722 this.footer.show();
23723 editor.on('hide', function( ) { this.footer.hide() }, this);
23724 editor.on('show', function( ) { this.footer.show() }, this);
23727 this.rendered = true;
23729 // the all the btns;
23730 editor.on('editorevent', this.updateToolbar, this);
23731 // other toolbars need to implement this..
23732 //editor.on('editmodechange', this.updateToolbar, this);
23738 * Protected method that will not generally be called directly. It triggers
23739 * a toolbar update by reading the markup state of the current selection in the editor.
23741 * Note you can force an update by calling on('editorevent', scope, false)
23743 updateToolbar: function(editor,ev,sel){
23746 // capture mouse up - this is handy for selecting images..
23747 // perhaps should go somewhere else...
23748 if(!this.editorcore.activated){
23749 this.editor.onFirstFocus();
23755 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23756 // selectNode - might want to handle IE?
23758 (ev.type == 'mouseup' || ev.type == 'click' ) &&
23759 ev.target && ev.target.tagName == 'IMG') {
23760 // they have click on an image...
23761 // let's see if we can change the selection...
23764 var nodeRange = sel.ownerDocument.createRange();
23766 nodeRange.selectNode(sel);
23768 nodeRange.selectNodeContents(sel);
23770 //nodeRange.collapse(true);
23771 var s = this.editorcore.win.getSelection();
23772 s.removeAllRanges();
23773 s.addRange(nodeRange);
23777 var updateFooter = sel ? false : true;
23780 var ans = this.editorcore.getAllAncestors();
23783 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23786 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
23787 sel = sel ? sel : this.editorcore.doc.body;
23788 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23791 // pick a menu that exists..
23792 var tn = sel.tagName.toUpperCase();
23793 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23795 tn = sel.tagName.toUpperCase();
23797 var lastSel = this.tb.selectedNode;
23799 this.tb.selectedNode = sel;
23801 // if current menu does not match..
23803 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23806 ///console.log("show: " + tn);
23807 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23810 this.tb.items.first().el.innerHTML = tn + ': ';
23813 // update attributes
23814 if (this.tb.fields) {
23815 this.tb.fields.each(function(e) {
23817 e.setValue(sel.style[e.stylename]);
23820 e.setValue(sel.getAttribute(e.attrname));
23824 var hasStyles = false;
23825 for(var i in this.styles) {
23832 var st = this.tb.fields.item(0);
23834 st.store.removeAll();
23837 var cn = sel.className.split(/\s+/);
23840 if (this.styles['*']) {
23842 Roo.each(this.styles['*'], function(v) {
23843 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
23846 if (this.styles[tn]) {
23847 Roo.each(this.styles[tn], function(v) {
23848 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
23852 st.store.loadData(avs);
23856 // flag our selected Node.
23857 this.tb.selectedNode = sel;
23860 Roo.menu.MenuMgr.hideAll();
23864 if (!updateFooter) {
23865 //this.footDisp.dom.innerHTML = '';
23868 // update the footer
23872 this.footerEls = ans.reverse();
23873 Roo.each(this.footerEls, function(a,i) {
23874 if (!a) { return; }
23875 html += html.length ? ' > ' : '';
23877 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23882 var sz = this.footDisp.up('td').getSize();
23883 this.footDisp.dom.style.width = (sz.width -10) + 'px';
23884 this.footDisp.dom.style.marginLeft = '5px';
23886 this.footDisp.dom.style.overflow = 'hidden';
23888 this.footDisp.dom.innerHTML = html;
23890 //this.editorsyncValue();
23897 onDestroy : function(){
23900 this.tb.items.each(function(item){
23902 item.menu.removeAll();
23904 item.menu.el.destroy();
23912 onFirstFocus: function() {
23913 // need to do this for all the toolbars..
23914 this.tb.items.each(function(item){
23918 buildToolbar: function(tlist, nm)
23920 var editor = this.editor;
23921 var editorcore = this.editorcore;
23922 // create a new element.
23923 var wdiv = editor.wrap.createChild({
23925 }, editor.wrap.dom.firstChild.nextSibling, true);
23928 var tb = new Roo.Toolbar(wdiv);
23931 tb.add(nm+ ": ");
23934 for(var i in this.styles) {
23939 if (styles && styles.length) {
23941 // this needs a multi-select checkbox...
23942 tb.addField( new Roo.form.ComboBox({
23943 store: new Roo.data.SimpleStore({
23945 fields: ['val', 'selected'],
23948 name : '-roo-edit-className',
23949 attrname : 'className',
23950 displayField: 'val',
23954 triggerAction: 'all',
23955 emptyText:'Select Style',
23956 selectOnFocus:true,
23959 'select': function(c, r, i) {
23960 // initial support only for on class per el..
23961 tb.selectedNode.className = r ? r.get('val') : '';
23962 editorcore.syncValue();
23969 var tbc = Roo.form.HtmlEditor.ToolbarContext;
23970 var tbops = tbc.options;
23972 for (var i in tlist) {
23974 var item = tlist[i];
23975 tb.add(item.title + ": ");
23978 //optname == used so you can configure the options available..
23979 var opts = item.opts ? item.opts : false;
23980 if (item.optname) {
23981 opts = tbops[item.optname];
23986 // opts == pulldown..
23987 tb.addField( new Roo.form.ComboBox({
23988 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
23990 fields: ['val', 'display'],
23993 name : '-roo-edit-' + i,
23995 stylename : item.style ? item.style : false,
23996 displayField: item.displayField ? item.displayField : 'val',
23997 valueField : 'val',
23999 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24001 triggerAction: 'all',
24002 emptyText:'Select',
24003 selectOnFocus:true,
24004 width: item.width ? item.width : 130,
24006 'select': function(c, r, i) {
24008 tb.selectedNode.style[c.stylename] = r.get('val');
24011 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24020 tb.addField( new Roo.form.TextField({
24023 //allowBlank:false,
24028 tb.addField( new Roo.form.TextField({
24029 name: '-roo-edit-' + i,
24036 'change' : function(f, nv, ov) {
24037 tb.selectedNode.setAttribute(f.attrname, nv);
24038 editorcore.syncValue();
24051 text: 'Stylesheets',
24054 click : function ()
24056 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24064 text: 'Remove Tag',
24067 click : function ()
24070 // undo does not work.
24072 var sn = tb.selectedNode;
24074 var pn = sn.parentNode;
24076 var stn = sn.childNodes[0];
24077 var en = sn.childNodes[sn.childNodes.length - 1 ];
24078 while (sn.childNodes.length) {
24079 var node = sn.childNodes[0];
24080 sn.removeChild(node);
24082 pn.insertBefore(node, sn);
24085 pn.removeChild(sn);
24086 var range = editorcore.createRange();
24088 range.setStart(stn,0);
24089 range.setEnd(en,0); //????
24090 //range.selectNode(sel);
24093 var selection = editorcore.getSelection();
24094 selection.removeAllRanges();
24095 selection.addRange(range);
24099 //_this.updateToolbar(null, null, pn);
24100 _this.updateToolbar(null, null, null);
24101 _this.footDisp.dom.innerHTML = '';
24111 tb.el.on('click', function(e){
24112 e.preventDefault(); // what does this do?
24114 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24117 // dont need to disable them... as they will get hidden
24122 buildFooter : function()
24125 var fel = this.editor.wrap.createChild();
24126 this.footer = new Roo.Toolbar(fel);
24127 // toolbar has scrolly on left / right?
24128 var footDisp= new Roo.Toolbar.Fill();
24134 handler : function() {
24135 _t.footDisp.scrollTo('left',0,true)
24139 this.footer.add( footDisp );
24144 handler : function() {
24146 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24150 var fel = Roo.get(footDisp.el);
24151 fel.addClass('x-editor-context');
24152 this.footDispWrap = fel;
24153 this.footDispWrap.overflow = 'hidden';
24155 this.footDisp = fel.createChild();
24156 this.footDispWrap.on('click', this.onContextClick, this)
24160 onContextClick : function (ev,dom)
24162 ev.preventDefault();
24163 var cn = dom.className;
24165 if (!cn.match(/x-ed-loc-/)) {
24168 var n = cn.split('-').pop();
24169 var ans = this.footerEls;
24173 var range = this.editorcore.createRange();
24175 range.selectNodeContents(sel);
24176 //range.selectNode(sel);
24179 var selection = this.editorcore.getSelection();
24180 selection.removeAllRanges();
24181 selection.addRange(range);
24185 this.updateToolbar(null, null, sel);
24202 * Ext JS Library 1.1.1
24203 * Copyright(c) 2006-2007, Ext JS, LLC.
24205 * Originally Released Under LGPL - original licence link has changed is not relivant.
24208 * <script type="text/javascript">
24212 * @class Roo.form.BasicForm
24213 * @extends Roo.util.Observable
24214 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24216 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24217 * @param {Object} config Configuration options
24219 Roo.form.BasicForm = function(el, config){
24220 this.allItems = [];
24221 this.childForms = [];
24222 Roo.apply(this, config);
24224 * The Roo.form.Field items in this form.
24225 * @type MixedCollection
24229 this.items = new Roo.util.MixedCollection(false, function(o){
24230 return o.id || (o.id = Roo.id());
24234 * @event beforeaction
24235 * Fires before any action is performed. Return false to cancel the action.
24236 * @param {Form} this
24237 * @param {Action} action The action to be performed
24239 beforeaction: true,
24241 * @event actionfailed
24242 * Fires when an action fails.
24243 * @param {Form} this
24244 * @param {Action} action The action that failed
24246 actionfailed : true,
24248 * @event actioncomplete
24249 * Fires when an action is completed.
24250 * @param {Form} this
24251 * @param {Action} action The action that completed
24253 actioncomplete : true
24258 Roo.form.BasicForm.superclass.constructor.call(this);
24261 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24263 * @cfg {String} method
24264 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24267 * @cfg {DataReader} reader
24268 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24269 * This is optional as there is built-in support for processing JSON.
24272 * @cfg {DataReader} errorReader
24273 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24274 * This is completely optional as there is built-in support for processing JSON.
24277 * @cfg {String} url
24278 * The URL to use for form actions if one isn't supplied in the action options.
24281 * @cfg {Boolean} fileUpload
24282 * Set to true if this form is a file upload.
24286 * @cfg {Object} baseParams
24287 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24292 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24297 activeAction : null,
24300 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24301 * or setValues() data instead of when the form was first created.
24303 trackResetOnLoad : false,
24307 * childForms - used for multi-tab forms
24310 childForms : false,
24313 * allItems - full list of fields.
24319 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24320 * element by passing it or its id or mask the form itself by passing in true.
24323 waitMsgTarget : false,
24326 initEl : function(el){
24327 this.el = Roo.get(el);
24328 this.id = this.el.id || Roo.id();
24329 this.el.on('submit', this.onSubmit, this);
24330 this.el.addClass('x-form');
24334 onSubmit : function(e){
24339 * Returns true if client-side validation on the form is successful.
24342 isValid : function(){
24344 this.items.each(function(f){
24353 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24356 isDirty : function(){
24358 this.items.each(function(f){
24368 * Returns true if any fields in this form have changed since their original load. (New version)
24372 hasChanged : function()
24375 this.items.each(function(f){
24376 if(f.hasChanged()){
24385 * Resets all hasChanged to 'false' -
24386 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24387 * So hasChanged storage is only to be used for this purpose
24390 resetHasChanged : function()
24392 this.items.each(function(f){
24393 f.resetHasChanged();
24400 * Performs a predefined action (submit or load) or custom actions you define on this form.
24401 * @param {String} actionName The name of the action type
24402 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24403 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24404 * accept other config options):
24406 Property Type Description
24407 ---------------- --------------- ----------------------------------------------------------------------------------
24408 url String The url for the action (defaults to the form's url)
24409 method String The form method to use (defaults to the form's method, or POST if not defined)
24410 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
24411 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
24412 validate the form on the client (defaults to false)
24414 * @return {BasicForm} this
24416 doAction : function(action, options){
24417 if(typeof action == 'string'){
24418 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24420 if(this.fireEvent('beforeaction', this, action) !== false){
24421 this.beforeAction(action);
24422 action.run.defer(100, action);
24428 * Shortcut to do a submit action.
24429 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24430 * @return {BasicForm} this
24432 submit : function(options){
24433 this.doAction('submit', options);
24438 * Shortcut to do a load action.
24439 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24440 * @return {BasicForm} this
24442 load : function(options){
24443 this.doAction('load', options);
24448 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24449 * @param {Record} record The record to edit
24450 * @return {BasicForm} this
24452 updateRecord : function(record){
24453 record.beginEdit();
24454 var fs = record.fields;
24455 fs.each(function(f){
24456 var field = this.findField(f.name);
24458 record.set(f.name, field.getValue());
24466 * Loads an Roo.data.Record into this form.
24467 * @param {Record} record The record to load
24468 * @return {BasicForm} this
24470 loadRecord : function(record){
24471 this.setValues(record.data);
24476 beforeAction : function(action){
24477 var o = action.options;
24480 if(this.waitMsgTarget === true){
24481 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24482 }else if(this.waitMsgTarget){
24483 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24484 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24486 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24492 afterAction : function(action, success){
24493 this.activeAction = null;
24494 var o = action.options;
24496 if(this.waitMsgTarget === true){
24498 }else if(this.waitMsgTarget){
24499 this.waitMsgTarget.unmask();
24501 Roo.MessageBox.updateProgress(1);
24502 Roo.MessageBox.hide();
24509 Roo.callback(o.success, o.scope, [this, action]);
24510 this.fireEvent('actioncomplete', this, action);
24514 // failure condition..
24515 // we have a scenario where updates need confirming.
24516 // eg. if a locking scenario exists..
24517 // we look for { errors : { needs_confirm : true }} in the response.
24519 (typeof(action.result) != 'undefined') &&
24520 (typeof(action.result.errors) != 'undefined') &&
24521 (typeof(action.result.errors.needs_confirm) != 'undefined')
24524 Roo.MessageBox.confirm(
24525 "Change requires confirmation",
24526 action.result.errorMsg,
24531 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
24541 Roo.callback(o.failure, o.scope, [this, action]);
24542 // show an error message if no failed handler is set..
24543 if (!this.hasListener('actionfailed')) {
24544 Roo.MessageBox.alert("Error",
24545 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24546 action.result.errorMsg :
24547 "Saving Failed, please check your entries or try again"
24551 this.fireEvent('actionfailed', this, action);
24557 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24558 * @param {String} id The value to search for
24561 findField : function(id){
24562 var field = this.items.get(id);
24564 this.items.each(function(f){
24565 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24571 return field || null;
24575 * Add a secondary form to this one,
24576 * Used to provide tabbed forms. One form is primary, with hidden values
24577 * which mirror the elements from the other forms.
24579 * @param {Roo.form.Form} form to add.
24582 addForm : function(form)
24585 if (this.childForms.indexOf(form) > -1) {
24589 this.childForms.push(form);
24591 Roo.each(form.allItems, function (fe) {
24593 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24594 if (this.findField(n)) { // already added..
24597 var add = new Roo.form.Hidden({
24600 add.render(this.el);
24607 * Mark fields in this form invalid in bulk.
24608 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24609 * @return {BasicForm} this
24611 markInvalid : function(errors){
24612 if(errors instanceof Array){
24613 for(var i = 0, len = errors.length; i < len; i++){
24614 var fieldError = errors[i];
24615 var f = this.findField(fieldError.id);
24617 f.markInvalid(fieldError.msg);
24623 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24624 field.markInvalid(errors[id]);
24628 Roo.each(this.childForms || [], function (f) {
24629 f.markInvalid(errors);
24636 * Set values for fields in this form in bulk.
24637 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24638 * @return {BasicForm} this
24640 setValues : function(values){
24641 if(values instanceof Array){ // array of objects
24642 for(var i = 0, len = values.length; i < len; i++){
24644 var f = this.findField(v.id);
24646 f.setValue(v.value);
24647 if(this.trackResetOnLoad){
24648 f.originalValue = f.getValue();
24652 }else{ // object hash
24655 if(typeof values[id] != 'function' && (field = this.findField(id))){
24657 if (field.setFromData &&
24658 field.valueField &&
24659 field.displayField &&
24660 // combos' with local stores can
24661 // be queried via setValue()
24662 // to set their value..
24663 (field.store && !field.store.isLocal)
24667 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24668 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24669 field.setFromData(sd);
24672 field.setValue(values[id]);
24676 if(this.trackResetOnLoad){
24677 field.originalValue = field.getValue();
24682 this.resetHasChanged();
24685 Roo.each(this.childForms || [], function (f) {
24686 f.setValues(values);
24687 f.resetHasChanged();
24694 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24695 * they are returned as an array.
24696 * @param {Boolean} asString
24699 getValues : function(asString){
24700 if (this.childForms) {
24701 // copy values from the child forms
24702 Roo.each(this.childForms, function (f) {
24703 this.setValues(f.getValues());
24709 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24710 if(asString === true){
24713 return Roo.urlDecode(fs);
24717 * Returns the fields in this form as an object with key/value pairs.
24718 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24721 getFieldValues : function(with_hidden)
24723 if (this.childForms) {
24724 // copy values from the child forms
24725 // should this call getFieldValues - probably not as we do not currently copy
24726 // hidden fields when we generate..
24727 Roo.each(this.childForms, function (f) {
24728 this.setValues(f.getValues());
24733 this.items.each(function(f){
24734 if (!f.getName()) {
24737 var v = f.getValue();
24738 if (f.inputType =='radio') {
24739 if (typeof(ret[f.getName()]) == 'undefined') {
24740 ret[f.getName()] = ''; // empty..
24743 if (!f.el.dom.checked) {
24747 v = f.el.dom.value;
24751 // not sure if this supported any more..
24752 if ((typeof(v) == 'object') && f.getRawValue) {
24753 v = f.getRawValue() ; // dates..
24755 // combo boxes where name != hiddenName...
24756 if (f.name != f.getName()) {
24757 ret[f.name] = f.getRawValue();
24759 ret[f.getName()] = v;
24766 * Clears all invalid messages in this form.
24767 * @return {BasicForm} this
24769 clearInvalid : function(){
24770 this.items.each(function(f){
24774 Roo.each(this.childForms || [], function (f) {
24783 * Resets this form.
24784 * @return {BasicForm} this
24786 reset : function(){
24787 this.items.each(function(f){
24791 Roo.each(this.childForms || [], function (f) {
24794 this.resetHasChanged();
24800 * Add Roo.form components to this form.
24801 * @param {Field} field1
24802 * @param {Field} field2 (optional)
24803 * @param {Field} etc (optional)
24804 * @return {BasicForm} this
24807 this.items.addAll(Array.prototype.slice.call(arguments, 0));
24813 * Removes a field from the items collection (does NOT remove its markup).
24814 * @param {Field} field
24815 * @return {BasicForm} this
24817 remove : function(field){
24818 this.items.remove(field);
24823 * Looks at the fields in this form, checks them for an id attribute,
24824 * and calls applyTo on the existing dom element with that id.
24825 * @return {BasicForm} this
24827 render : function(){
24828 this.items.each(function(f){
24829 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24837 * Calls {@link Ext#apply} for all fields in this form with the passed object.
24838 * @param {Object} values
24839 * @return {BasicForm} this
24841 applyToFields : function(o){
24842 this.items.each(function(f){
24849 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24850 * @param {Object} values
24851 * @return {BasicForm} this
24853 applyIfToFields : function(o){
24854 this.items.each(function(f){
24862 Roo.BasicForm = Roo.form.BasicForm;/*
24864 * Ext JS Library 1.1.1
24865 * Copyright(c) 2006-2007, Ext JS, LLC.
24867 * Originally Released Under LGPL - original licence link has changed is not relivant.
24870 * <script type="text/javascript">
24874 * @class Roo.form.Form
24875 * @extends Roo.form.BasicForm
24876 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24878 * @param {Object} config Configuration options
24880 Roo.form.Form = function(config){
24882 if (config.items) {
24883 xitems = config.items;
24884 delete config.items;
24888 Roo.form.Form.superclass.constructor.call(this, null, config);
24889 this.url = this.url || this.action;
24891 this.root = new Roo.form.Layout(Roo.applyIf({
24895 this.active = this.root;
24897 * Array of all the buttons that have been added to this form via {@link addButton}
24901 this.allItems = [];
24904 * @event clientvalidation
24905 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24906 * @param {Form} this
24907 * @param {Boolean} valid true if the form has passed client-side validation
24909 clientvalidation: true,
24912 * Fires when the form is rendered
24913 * @param {Roo.form.Form} form
24918 if (this.progressUrl) {
24919 // push a hidden field onto the list of fields..
24923 name : 'UPLOAD_IDENTIFIER'
24928 Roo.each(xitems, this.addxtype, this);
24934 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24936 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24939 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24942 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24944 buttonAlign:'center',
24947 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24952 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24953 * This property cascades to child containers if not set.
24958 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24959 * fires a looping event with that state. This is required to bind buttons to the valid
24960 * state using the config value formBind:true on the button.
24962 monitorValid : false,
24965 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24970 * @cfg {String} progressUrl - Url to return progress data
24973 progressUrl : false,
24976 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
24977 * fields are added and the column is closed. If no fields are passed the column remains open
24978 * until end() is called.
24979 * @param {Object} config The config to pass to the column
24980 * @param {Field} field1 (optional)
24981 * @param {Field} field2 (optional)
24982 * @param {Field} etc (optional)
24983 * @return Column The column container object
24985 column : function(c){
24986 var col = new Roo.form.Column(c);
24988 if(arguments.length > 1){ // duplicate code required because of Opera
24989 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
24996 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
24997 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
24998 * until end() is called.
24999 * @param {Object} config The config to pass to the fieldset
25000 * @param {Field} field1 (optional)
25001 * @param {Field} field2 (optional)
25002 * @param {Field} etc (optional)
25003 * @return FieldSet The fieldset container object
25005 fieldset : function(c){
25006 var fs = new Roo.form.FieldSet(c);
25008 if(arguments.length > 1){ // duplicate code required because of Opera
25009 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25016 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25017 * fields are added and the container is closed. If no fields are passed the container remains open
25018 * until end() is called.
25019 * @param {Object} config The config to pass to the Layout
25020 * @param {Field} field1 (optional)
25021 * @param {Field} field2 (optional)
25022 * @param {Field} etc (optional)
25023 * @return Layout The container object
25025 container : function(c){
25026 var l = new Roo.form.Layout(c);
25028 if(arguments.length > 1){ // duplicate code required because of Opera
25029 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25036 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25037 * @param {Object} container A Roo.form.Layout or subclass of Layout
25038 * @return {Form} this
25040 start : function(c){
25041 // cascade label info
25042 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25043 this.active.stack.push(c);
25044 c.ownerCt = this.active;
25050 * Closes the current open container
25051 * @return {Form} this
25054 if(this.active == this.root){
25057 this.active = this.active.ownerCt;
25062 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25063 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25064 * as the label of the field.
25065 * @param {Field} field1
25066 * @param {Field} field2 (optional)
25067 * @param {Field} etc. (optional)
25068 * @return {Form} this
25071 this.active.stack.push.apply(this.active.stack, arguments);
25072 this.allItems.push.apply(this.allItems,arguments);
25074 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25075 if(a[i].isFormField){
25080 Roo.form.Form.superclass.add.apply(this, r);
25090 * Find any element that has been added to a form, using it's ID or name
25091 * This can include framesets, columns etc. along with regular fields..
25092 * @param {String} id - id or name to find.
25094 * @return {Element} e - or false if nothing found.
25096 findbyId : function(id)
25102 Roo.each(this.allItems, function(f){
25103 if (f.id == id || f.name == id ){
25114 * Render this form into the passed container. This should only be called once!
25115 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25116 * @return {Form} this
25118 render : function(ct)
25124 var o = this.autoCreate || {
25126 method : this.method || 'POST',
25127 id : this.id || Roo.id()
25129 this.initEl(ct.createChild(o));
25131 this.root.render(this.el);
25135 this.items.each(function(f){
25136 f.render('x-form-el-'+f.id);
25139 if(this.buttons.length > 0){
25140 // tables are required to maintain order and for correct IE layout
25141 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25142 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25143 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25145 var tr = tb.getElementsByTagName('tr')[0];
25146 for(var i = 0, len = this.buttons.length; i < len; i++) {
25147 var b = this.buttons[i];
25148 var td = document.createElement('td');
25149 td.className = 'x-form-btn-td';
25150 b.render(tr.appendChild(td));
25153 if(this.monitorValid){ // initialize after render
25154 this.startMonitoring();
25156 this.fireEvent('rendered', this);
25161 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25162 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25163 * object or a valid Roo.DomHelper element config
25164 * @param {Function} handler The function called when the button is clicked
25165 * @param {Object} scope (optional) The scope of the handler function
25166 * @return {Roo.Button}
25168 addButton : function(config, handler, scope){
25172 minWidth: this.minButtonWidth,
25175 if(typeof config == "string"){
25178 Roo.apply(bc, config);
25180 var btn = new Roo.Button(null, bc);
25181 this.buttons.push(btn);
25186 * Adds a series of form elements (using the xtype property as the factory method.
25187 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25188 * @param {Object} config
25191 addxtype : function()
25193 var ar = Array.prototype.slice.call(arguments, 0);
25195 for(var i = 0; i < ar.length; i++) {
25197 continue; // skip -- if this happends something invalid got sent, we
25198 // should ignore it, as basically that interface element will not show up
25199 // and that should be pretty obvious!!
25202 if (Roo.form[ar[i].xtype]) {
25204 var fe = Roo.factory(ar[i], Roo.form);
25210 fe.store.form = this;
25215 this.allItems.push(fe);
25216 if (fe.items && fe.addxtype) {
25217 fe.addxtype.apply(fe, fe.items);
25227 // console.log('adding ' + ar[i].xtype);
25229 if (ar[i].xtype == 'Button') {
25230 //console.log('adding button');
25231 //console.log(ar[i]);
25232 this.addButton(ar[i]);
25233 this.allItems.push(fe);
25237 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25238 alert('end is not supported on xtype any more, use items');
25240 // //console.log('adding end');
25248 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25249 * option "monitorValid"
25251 startMonitoring : function(){
25254 Roo.TaskMgr.start({
25255 run : this.bindHandler,
25256 interval : this.monitorPoll || 200,
25263 * Stops monitoring of the valid state of this form
25265 stopMonitoring : function(){
25266 this.bound = false;
25270 bindHandler : function(){
25272 return false; // stops binding
25275 this.items.each(function(f){
25276 if(!f.isValid(true)){
25281 for(var i = 0, len = this.buttons.length; i < len; i++){
25282 var btn = this.buttons[i];
25283 if(btn.formBind === true && btn.disabled === valid){
25284 btn.setDisabled(!valid);
25287 this.fireEvent('clientvalidation', this, valid);
25301 Roo.Form = Roo.form.Form;
25304 * Ext JS Library 1.1.1
25305 * Copyright(c) 2006-2007, Ext JS, LLC.
25307 * Originally Released Under LGPL - original licence link has changed is not relivant.
25310 * <script type="text/javascript">
25313 // as we use this in bootstrap.
25314 Roo.namespace('Roo.form');
25316 * @class Roo.form.Action
25317 * Internal Class used to handle form actions
25319 * @param {Roo.form.BasicForm} el The form element or its id
25320 * @param {Object} config Configuration options
25325 // define the action interface
25326 Roo.form.Action = function(form, options){
25328 this.options = options || {};
25331 * Client Validation Failed
25334 Roo.form.Action.CLIENT_INVALID = 'client';
25336 * Server Validation Failed
25339 Roo.form.Action.SERVER_INVALID = 'server';
25341 * Connect to Server Failed
25344 Roo.form.Action.CONNECT_FAILURE = 'connect';
25346 * Reading Data from Server Failed
25349 Roo.form.Action.LOAD_FAILURE = 'load';
25351 Roo.form.Action.prototype = {
25353 failureType : undefined,
25354 response : undefined,
25355 result : undefined,
25357 // interface method
25358 run : function(options){
25362 // interface method
25363 success : function(response){
25367 // interface method
25368 handleResponse : function(response){
25372 // default connection failure
25373 failure : function(response){
25375 this.response = response;
25376 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25377 this.form.afterAction(this, false);
25380 processResponse : function(response){
25381 this.response = response;
25382 if(!response.responseText){
25385 this.result = this.handleResponse(response);
25386 return this.result;
25389 // utility functions used internally
25390 getUrl : function(appendParams){
25391 var url = this.options.url || this.form.url || this.form.el.dom.action;
25393 var p = this.getParams();
25395 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25401 getMethod : function(){
25402 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25405 getParams : function(){
25406 var bp = this.form.baseParams;
25407 var p = this.options.params;
25409 if(typeof p == "object"){
25410 p = Roo.urlEncode(Roo.applyIf(p, bp));
25411 }else if(typeof p == 'string' && bp){
25412 p += '&' + Roo.urlEncode(bp);
25415 p = Roo.urlEncode(bp);
25420 createCallback : function(){
25422 success: this.success,
25423 failure: this.failure,
25425 timeout: (this.form.timeout*1000),
25426 upload: this.form.fileUpload ? this.success : undefined
25431 Roo.form.Action.Submit = function(form, options){
25432 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25435 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25438 haveProgress : false,
25439 uploadComplete : false,
25441 // uploadProgress indicator.
25442 uploadProgress : function()
25444 if (!this.form.progressUrl) {
25448 if (!this.haveProgress) {
25449 Roo.MessageBox.progress("Uploading", "Uploading");
25451 if (this.uploadComplete) {
25452 Roo.MessageBox.hide();
25456 this.haveProgress = true;
25458 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25460 var c = new Roo.data.Connection();
25462 url : this.form.progressUrl,
25467 success : function(req){
25468 //console.log(data);
25472 rdata = Roo.decode(req.responseText)
25474 Roo.log("Invalid data from server..");
25478 if (!rdata || !rdata.success) {
25480 Roo.MessageBox.alert(Roo.encode(rdata));
25483 var data = rdata.data;
25485 if (this.uploadComplete) {
25486 Roo.MessageBox.hide();
25491 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25492 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25495 this.uploadProgress.defer(2000,this);
25498 failure: function(data) {
25499 Roo.log('progress url failed ');
25510 // run get Values on the form, so it syncs any secondary forms.
25511 this.form.getValues();
25513 var o = this.options;
25514 var method = this.getMethod();
25515 var isPost = method == 'POST';
25516 if(o.clientValidation === false || this.form.isValid()){
25518 if (this.form.progressUrl) {
25519 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25520 (new Date() * 1) + '' + Math.random());
25525 Roo.Ajax.request(Roo.apply(this.createCallback(), {
25526 form:this.form.el.dom,
25527 url:this.getUrl(!isPost),
25529 params:isPost ? this.getParams() : null,
25530 isUpload: this.form.fileUpload
25533 this.uploadProgress();
25535 }else if (o.clientValidation !== false){ // client validation failed
25536 this.failureType = Roo.form.Action.CLIENT_INVALID;
25537 this.form.afterAction(this, false);
25541 success : function(response)
25543 this.uploadComplete= true;
25544 if (this.haveProgress) {
25545 Roo.MessageBox.hide();
25549 var result = this.processResponse(response);
25550 if(result === true || result.success){
25551 this.form.afterAction(this, true);
25555 this.form.markInvalid(result.errors);
25556 this.failureType = Roo.form.Action.SERVER_INVALID;
25558 this.form.afterAction(this, false);
25560 failure : function(response)
25562 this.uploadComplete= true;
25563 if (this.haveProgress) {
25564 Roo.MessageBox.hide();
25567 this.response = response;
25568 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25569 this.form.afterAction(this, false);
25572 handleResponse : function(response){
25573 if(this.form.errorReader){
25574 var rs = this.form.errorReader.read(response);
25577 for(var i = 0, len = rs.records.length; i < len; i++) {
25578 var r = rs.records[i];
25579 errors[i] = r.data;
25582 if(errors.length < 1){
25586 success : rs.success,
25592 ret = Roo.decode(response.responseText);
25596 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25606 Roo.form.Action.Load = function(form, options){
25607 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25608 this.reader = this.form.reader;
25611 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25616 Roo.Ajax.request(Roo.apply(
25617 this.createCallback(), {
25618 method:this.getMethod(),
25619 url:this.getUrl(false),
25620 params:this.getParams()
25624 success : function(response){
25626 var result = this.processResponse(response);
25627 if(result === true || !result.success || !result.data){
25628 this.failureType = Roo.form.Action.LOAD_FAILURE;
25629 this.form.afterAction(this, false);
25632 this.form.clearInvalid();
25633 this.form.setValues(result.data);
25634 this.form.afterAction(this, true);
25637 handleResponse : function(response){
25638 if(this.form.reader){
25639 var rs = this.form.reader.read(response);
25640 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25642 success : rs.success,
25646 return Roo.decode(response.responseText);
25650 Roo.form.Action.ACTION_TYPES = {
25651 'load' : Roo.form.Action.Load,
25652 'submit' : Roo.form.Action.Submit
25655 * Ext JS Library 1.1.1
25656 * Copyright(c) 2006-2007, Ext JS, LLC.
25658 * Originally Released Under LGPL - original licence link has changed is not relivant.
25661 * <script type="text/javascript">
25665 * @class Roo.form.Layout
25666 * @extends Roo.Component
25667 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25669 * @param {Object} config Configuration options
25671 Roo.form.Layout = function(config){
25673 if (config.items) {
25674 xitems = config.items;
25675 delete config.items;
25677 Roo.form.Layout.superclass.constructor.call(this, config);
25679 Roo.each(xitems, this.addxtype, this);
25683 Roo.extend(Roo.form.Layout, Roo.Component, {
25685 * @cfg {String/Object} autoCreate
25686 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25689 * @cfg {String/Object/Function} style
25690 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25691 * a function which returns such a specification.
25694 * @cfg {String} labelAlign
25695 * Valid values are "left," "top" and "right" (defaults to "left")
25698 * @cfg {Number} labelWidth
25699 * Fixed width in pixels of all field labels (defaults to undefined)
25702 * @cfg {Boolean} clear
25703 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25707 * @cfg {String} labelSeparator
25708 * The separator to use after field labels (defaults to ':')
25710 labelSeparator : ':',
25712 * @cfg {Boolean} hideLabels
25713 * True to suppress the display of field labels in this layout (defaults to false)
25715 hideLabels : false,
25718 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25723 onRender : function(ct, position){
25724 if(this.el){ // from markup
25725 this.el = Roo.get(this.el);
25726 }else { // generate
25727 var cfg = this.getAutoCreate();
25728 this.el = ct.createChild(cfg, position);
25731 this.el.applyStyles(this.style);
25733 if(this.labelAlign){
25734 this.el.addClass('x-form-label-'+this.labelAlign);
25736 if(this.hideLabels){
25737 this.labelStyle = "display:none";
25738 this.elementStyle = "padding-left:0;";
25740 if(typeof this.labelWidth == 'number'){
25741 this.labelStyle = "width:"+this.labelWidth+"px;";
25742 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25744 if(this.labelAlign == 'top'){
25745 this.labelStyle = "width:auto;";
25746 this.elementStyle = "padding-left:0;";
25749 var stack = this.stack;
25750 var slen = stack.length;
25752 if(!this.fieldTpl){
25753 var t = new Roo.Template(
25754 '<div class="x-form-item {5}">',
25755 '<label for="{0}" style="{2}">{1}{4}</label>',
25756 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25758 '</div><div class="x-form-clear-left"></div>'
25760 t.disableFormats = true;
25762 Roo.form.Layout.prototype.fieldTpl = t;
25764 for(var i = 0; i < slen; i++) {
25765 if(stack[i].isFormField){
25766 this.renderField(stack[i]);
25768 this.renderComponent(stack[i]);
25773 this.el.createChild({cls:'x-form-clear'});
25778 renderField : function(f){
25779 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25782 f.labelStyle||this.labelStyle||'', //2
25783 this.elementStyle||'', //3
25784 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25785 f.itemCls||this.itemCls||'' //5
25786 ], true).getPrevSibling());
25790 renderComponent : function(c){
25791 c.render(c.isLayout ? this.el : this.el.createChild());
25794 * Adds a object form elements (using the xtype property as the factory method.)
25795 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
25796 * @param {Object} config
25798 addxtype : function(o)
25800 // create the lement.
25801 o.form = this.form;
25802 var fe = Roo.factory(o, Roo.form);
25803 this.form.allItems.push(fe);
25804 this.stack.push(fe);
25806 if (fe.isFormField) {
25807 this.form.items.add(fe);
25815 * @class Roo.form.Column
25816 * @extends Roo.form.Layout
25817 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25819 * @param {Object} config Configuration options
25821 Roo.form.Column = function(config){
25822 Roo.form.Column.superclass.constructor.call(this, config);
25825 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25827 * @cfg {Number/String} width
25828 * The fixed width of the column in pixels or CSS value (defaults to "auto")
25831 * @cfg {String/Object} autoCreate
25832 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25836 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25839 onRender : function(ct, position){
25840 Roo.form.Column.superclass.onRender.call(this, ct, position);
25842 this.el.setWidth(this.width);
25849 * @class Roo.form.Row
25850 * @extends Roo.form.Layout
25851 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25853 * @param {Object} config Configuration options
25857 Roo.form.Row = function(config){
25858 Roo.form.Row.superclass.constructor.call(this, config);
25861 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25863 * @cfg {Number/String} width
25864 * The fixed width of the column in pixels or CSS value (defaults to "auto")
25867 * @cfg {Number/String} height
25868 * The fixed height of the column in pixels or CSS value (defaults to "auto")
25870 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25874 onRender : function(ct, position){
25875 //console.log('row render');
25877 var t = new Roo.Template(
25878 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25879 '<label for="{0}" style="{2}">{1}{4}</label>',
25880 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25884 t.disableFormats = true;
25886 Roo.form.Layout.prototype.rowTpl = t;
25888 this.fieldTpl = this.rowTpl;
25890 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25891 var labelWidth = 100;
25893 if ((this.labelAlign != 'top')) {
25894 if (typeof this.labelWidth == 'number') {
25895 labelWidth = this.labelWidth
25897 this.padWidth = 20 + labelWidth;
25901 Roo.form.Column.superclass.onRender.call(this, ct, position);
25903 this.el.setWidth(this.width);
25906 this.el.setHeight(this.height);
25911 renderField : function(f){
25912 f.fieldEl = this.fieldTpl.append(this.el, [
25913 f.id, f.fieldLabel,
25914 f.labelStyle||this.labelStyle||'',
25915 this.elementStyle||'',
25916 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25917 f.itemCls||this.itemCls||'',
25918 f.width ? f.width + this.padWidth : 160 + this.padWidth
25925 * @class Roo.form.FieldSet
25926 * @extends Roo.form.Layout
25927 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25929 * @param {Object} config Configuration options
25931 Roo.form.FieldSet = function(config){
25932 Roo.form.FieldSet.superclass.constructor.call(this, config);
25935 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25937 * @cfg {String} legend
25938 * The text to display as the legend for the FieldSet (defaults to '')
25941 * @cfg {String/Object} autoCreate
25942 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25946 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25949 onRender : function(ct, position){
25950 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25952 this.setLegend(this.legend);
25957 setLegend : function(text){
25959 this.el.child('legend').update(text);
25964 * Ext JS Library 1.1.1
25965 * Copyright(c) 2006-2007, Ext JS, LLC.
25967 * Originally Released Under LGPL - original licence link has changed is not relivant.
25970 * <script type="text/javascript">
25973 * @class Roo.form.VTypes
25974 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
25977 Roo.form.VTypes = function(){
25978 // closure these in so they are only created once.
25979 var alpha = /^[a-zA-Z_]+$/;
25980 var alphanum = /^[a-zA-Z0-9_]+$/;
25981 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
25982 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
25984 // All these messages and functions are configurable
25987 * The function used to validate email addresses
25988 * @param {String} value The email address
25990 'email' : function(v){
25991 return email.test(v);
25994 * The error text to display when the email validation function returns false
25997 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
25999 * The keystroke filter mask to be applied on email input
26002 'emailMask' : /[a-z0-9_\.\-@]/i,
26005 * The function used to validate URLs
26006 * @param {String} value The URL
26008 'url' : function(v){
26009 return url.test(v);
26012 * The error text to display when the url validation function returns false
26015 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26018 * The function used to validate alpha values
26019 * @param {String} value The value
26021 'alpha' : function(v){
26022 return alpha.test(v);
26025 * The error text to display when the alpha validation function returns false
26028 'alphaText' : 'This field should only contain letters and _',
26030 * The keystroke filter mask to be applied on alpha input
26033 'alphaMask' : /[a-z_]/i,
26036 * The function used to validate alphanumeric values
26037 * @param {String} value The value
26039 'alphanum' : function(v){
26040 return alphanum.test(v);
26043 * The error text to display when the alphanumeric validation function returns false
26046 'alphanumText' : 'This field should only contain letters, numbers and _',
26048 * The keystroke filter mask to be applied on alphanumeric input
26051 'alphanumMask' : /[a-z0-9_]/i
26053 }();//<script type="text/javascript">
26056 * @class Roo.form.FCKeditor
26057 * @extends Roo.form.TextArea
26058 * Wrapper around the FCKEditor http://www.fckeditor.net
26060 * Creates a new FCKeditor
26061 * @param {Object} config Configuration options
26063 Roo.form.FCKeditor = function(config){
26064 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26067 * @event editorinit
26068 * Fired when the editor is initialized - you can add extra handlers here..
26069 * @param {FCKeditor} this
26070 * @param {Object} the FCK object.
26077 Roo.form.FCKeditor.editors = { };
26078 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26080 //defaultAutoCreate : {
26081 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26085 * @cfg {Object} fck options - see fck manual for details.
26090 * @cfg {Object} fck toolbar set (Basic or Default)
26092 toolbarSet : 'Basic',
26094 * @cfg {Object} fck BasePath
26096 basePath : '/fckeditor/',
26104 onRender : function(ct, position)
26107 this.defaultAutoCreate = {
26109 style:"width:300px;height:60px;",
26110 autocomplete: "new-password"
26113 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26116 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26117 if(this.preventScrollbars){
26118 this.el.setStyle("overflow", "hidden");
26120 this.el.setHeight(this.growMin);
26123 //console.log('onrender' + this.getId() );
26124 Roo.form.FCKeditor.editors[this.getId()] = this;
26127 this.replaceTextarea() ;
26131 getEditor : function() {
26132 return this.fckEditor;
26135 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26136 * @param {Mixed} value The value to set
26140 setValue : function(value)
26142 //console.log('setValue: ' + value);
26144 if(typeof(value) == 'undefined') { // not sure why this is happending...
26147 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26149 //if(!this.el || !this.getEditor()) {
26150 // this.value = value;
26151 //this.setValue.defer(100,this,[value]);
26155 if(!this.getEditor()) {
26159 this.getEditor().SetData(value);
26166 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26167 * @return {Mixed} value The field value
26169 getValue : function()
26172 if (this.frame && this.frame.dom.style.display == 'none') {
26173 return Roo.form.FCKeditor.superclass.getValue.call(this);
26176 if(!this.el || !this.getEditor()) {
26178 // this.getValue.defer(100,this);
26183 var value=this.getEditor().GetData();
26184 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26185 return Roo.form.FCKeditor.superclass.getValue.call(this);
26191 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26192 * @return {Mixed} value The field value
26194 getRawValue : function()
26196 if (this.frame && this.frame.dom.style.display == 'none') {
26197 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26200 if(!this.el || !this.getEditor()) {
26201 //this.getRawValue.defer(100,this);
26208 var value=this.getEditor().GetData();
26209 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26210 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26214 setSize : function(w,h) {
26218 //if (this.frame && this.frame.dom.style.display == 'none') {
26219 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26222 //if(!this.el || !this.getEditor()) {
26223 // this.setSize.defer(100,this, [w,h]);
26229 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26231 this.frame.dom.setAttribute('width', w);
26232 this.frame.dom.setAttribute('height', h);
26233 this.frame.setSize(w,h);
26237 toggleSourceEdit : function(value) {
26241 this.el.dom.style.display = value ? '' : 'none';
26242 this.frame.dom.style.display = value ? 'none' : '';
26247 focus: function(tag)
26249 if (this.frame.dom.style.display == 'none') {
26250 return Roo.form.FCKeditor.superclass.focus.call(this);
26252 if(!this.el || !this.getEditor()) {
26253 this.focus.defer(100,this, [tag]);
26260 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26261 this.getEditor().Focus();
26263 if (!this.getEditor().Selection.GetSelection()) {
26264 this.focus.defer(100,this, [tag]);
26269 var r = this.getEditor().EditorDocument.createRange();
26270 r.setStart(tgs[0],0);
26271 r.setEnd(tgs[0],0);
26272 this.getEditor().Selection.GetSelection().removeAllRanges();
26273 this.getEditor().Selection.GetSelection().addRange(r);
26274 this.getEditor().Focus();
26281 replaceTextarea : function()
26283 if ( document.getElementById( this.getId() + '___Frame' ) ) {
26286 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26288 // We must check the elements firstly using the Id and then the name.
26289 var oTextarea = document.getElementById( this.getId() );
26291 var colElementsByName = document.getElementsByName( this.getId() ) ;
26293 oTextarea.style.display = 'none' ;
26295 if ( oTextarea.tabIndex ) {
26296 this.TabIndex = oTextarea.tabIndex ;
26299 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26300 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26301 this.frame = Roo.get(this.getId() + '___Frame')
26304 _getConfigHtml : function()
26308 for ( var o in this.fckconfig ) {
26309 sConfig += sConfig.length > 0 ? '&' : '';
26310 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26313 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26317 _getIFrameHtml : function()
26319 var sFile = 'fckeditor.html' ;
26320 /* no idea what this is about..
26323 if ( (/fcksource=true/i).test( window.top.location.search ) )
26324 sFile = 'fckeditor.original.html' ;
26329 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26330 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
26333 var html = '<iframe id="' + this.getId() +
26334 '___Frame" src="' + sLink +
26335 '" width="' + this.width +
26336 '" height="' + this.height + '"' +
26337 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
26338 ' frameborder="0" scrolling="no"></iframe>' ;
26343 _insertHtmlBefore : function( html, element )
26345 if ( element.insertAdjacentHTML ) {
26347 element.insertAdjacentHTML( 'beforeBegin', html ) ;
26349 var oRange = document.createRange() ;
26350 oRange.setStartBefore( element ) ;
26351 var oFragment = oRange.createContextualFragment( html );
26352 element.parentNode.insertBefore( oFragment, element ) ;
26365 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26367 function FCKeditor_OnComplete(editorInstance){
26368 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26369 f.fckEditor = editorInstance;
26370 //console.log("loaded");
26371 f.fireEvent('editorinit', f, editorInstance);
26391 //<script type="text/javascript">
26393 * @class Roo.form.GridField
26394 * @extends Roo.form.Field
26395 * Embed a grid (or editable grid into a form)
26398 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26400 * xgrid.store = Roo.data.Store
26401 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26402 * xgrid.store.reader = Roo.data.JsonReader
26406 * Creates a new GridField
26407 * @param {Object} config Configuration options
26409 Roo.form.GridField = function(config){
26410 Roo.form.GridField.superclass.constructor.call(this, config);
26414 Roo.extend(Roo.form.GridField, Roo.form.Field, {
26416 * @cfg {Number} width - used to restrict width of grid..
26420 * @cfg {Number} height - used to restrict height of grid..
26424 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26430 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26431 * {tag: "input", type: "checkbox", autocomplete: "off"})
26433 // defaultAutoCreate : { tag: 'div' },
26434 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26436 * @cfg {String} addTitle Text to include for adding a title.
26440 onResize : function(){
26441 Roo.form.Field.superclass.onResize.apply(this, arguments);
26444 initEvents : function(){
26445 // Roo.form.Checkbox.superclass.initEvents.call(this);
26446 // has no events...
26451 getResizeEl : function(){
26455 getPositionEl : function(){
26460 onRender : function(ct, position){
26462 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26463 var style = this.style;
26466 Roo.form.GridField.superclass.onRender.call(this, ct, position);
26467 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26468 this.viewEl = this.wrap.createChild({ tag: 'div' });
26470 this.viewEl.applyStyles(style);
26473 this.viewEl.setWidth(this.width);
26476 this.viewEl.setHeight(this.height);
26478 //if(this.inputValue !== undefined){
26479 //this.setValue(this.value);
26482 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26485 this.grid.render();
26486 this.grid.getDataSource().on('remove', this.refreshValue, this);
26487 this.grid.getDataSource().on('update', this.refreshValue, this);
26488 this.grid.on('afteredit', this.refreshValue, this);
26494 * Sets the value of the item.
26495 * @param {String} either an object or a string..
26497 setValue : function(v){
26499 v = v || []; // empty set..
26500 // this does not seem smart - it really only affects memoryproxy grids..
26501 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26502 var ds = this.grid.getDataSource();
26503 // assumes a json reader..
26505 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
26506 ds.loadData( data);
26508 // clear selection so it does not get stale.
26509 if (this.grid.sm) {
26510 this.grid.sm.clearSelections();
26513 Roo.form.GridField.superclass.setValue.call(this, v);
26514 this.refreshValue();
26515 // should load data in the grid really....
26519 refreshValue: function() {
26521 this.grid.getDataSource().each(function(r) {
26524 this.el.dom.value = Roo.encode(val);
26532 * Ext JS Library 1.1.1
26533 * Copyright(c) 2006-2007, Ext JS, LLC.
26535 * Originally Released Under LGPL - original licence link has changed is not relivant.
26538 * <script type="text/javascript">
26541 * @class Roo.form.DisplayField
26542 * @extends Roo.form.Field
26543 * A generic Field to display non-editable data.
26544 * @cfg {Boolean} closable (true|false) default false
26546 * Creates a new Display Field item.
26547 * @param {Object} config Configuration options
26549 Roo.form.DisplayField = function(config){
26550 Roo.form.DisplayField.superclass.constructor.call(this, config);
26555 * Fires after the click the close btn
26556 * @param {Roo.form.DisplayField} this
26562 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
26563 inputType: 'hidden',
26569 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26571 focusClass : undefined,
26573 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26575 fieldClass: 'x-form-field',
26578 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26580 valueRenderer: undefined,
26584 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26585 * {tag: "input", type: "checkbox", autocomplete: "off"})
26588 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26592 onResize : function(){
26593 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26597 initEvents : function(){
26598 // Roo.form.Checkbox.superclass.initEvents.call(this);
26599 // has no events...
26602 this.closeEl.on('click', this.onClose, this);
26608 getResizeEl : function(){
26612 getPositionEl : function(){
26617 onRender : function(ct, position){
26619 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26620 //if(this.inputValue !== undefined){
26621 this.wrap = this.el.wrap();
26623 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26626 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26629 if (this.bodyStyle) {
26630 this.viewEl.applyStyles(this.bodyStyle);
26632 //this.viewEl.setStyle('padding', '2px');
26634 this.setValue(this.value);
26639 initValue : Roo.emptyFn,
26644 onClick : function(){
26649 * Sets the checked state of the checkbox.
26650 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26652 setValue : function(v){
26654 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
26655 // this might be called before we have a dom element..
26656 if (!this.viewEl) {
26659 this.viewEl.dom.innerHTML = html;
26660 Roo.form.DisplayField.superclass.setValue.call(this, v);
26664 onClose : function(e)
26666 e.preventDefault();
26668 this.fireEvent('close', this);
26677 * @class Roo.form.DayPicker
26678 * @extends Roo.form.Field
26679 * A Day picker show [M] [T] [W] ....
26681 * Creates a new Day Picker
26682 * @param {Object} config Configuration options
26684 Roo.form.DayPicker= function(config){
26685 Roo.form.DayPicker.superclass.constructor.call(this, config);
26689 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
26691 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26693 focusClass : undefined,
26695 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26697 fieldClass: "x-form-field",
26700 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26701 * {tag: "input", type: "checkbox", autocomplete: "off"})
26703 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26706 actionMode : 'viewEl',
26710 inputType : 'hidden',
26713 inputElement: false, // real input element?
26714 basedOn: false, // ????
26716 isFormField: true, // not sure where this is needed!!!!
26718 onResize : function(){
26719 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26720 if(!this.boxLabel){
26721 this.el.alignTo(this.wrap, 'c-c');
26725 initEvents : function(){
26726 Roo.form.Checkbox.superclass.initEvents.call(this);
26727 this.el.on("click", this.onClick, this);
26728 this.el.on("change", this.onClick, this);
26732 getResizeEl : function(){
26736 getPositionEl : function(){
26742 onRender : function(ct, position){
26743 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26745 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26747 var r1 = '<table><tr>';
26748 var r2 = '<tr class="x-form-daypick-icons">';
26749 for (var i=0; i < 7; i++) {
26750 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26751 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
26754 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26755 viewEl.select('img').on('click', this.onClick, this);
26756 this.viewEl = viewEl;
26759 // this will not work on Chrome!!!
26760 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
26761 this.el.on('propertychange', this.setFromHidden, this); //ie
26769 initValue : Roo.emptyFn,
26772 * Returns the checked state of the checkbox.
26773 * @return {Boolean} True if checked, else false
26775 getValue : function(){
26776 return this.el.dom.value;
26781 onClick : function(e){
26782 //this.setChecked(!this.checked);
26783 Roo.get(e.target).toggleClass('x-menu-item-checked');
26784 this.refreshValue();
26785 //if(this.el.dom.checked != this.checked){
26786 // this.setValue(this.el.dom.checked);
26791 refreshValue : function()
26794 this.viewEl.select('img',true).each(function(e,i,n) {
26795 val += e.is(".x-menu-item-checked") ? String(n) : '';
26797 this.setValue(val, true);
26801 * Sets the checked state of the checkbox.
26802 * On is always based on a string comparison between inputValue and the param.
26803 * @param {Boolean/String} value - the value to set
26804 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26806 setValue : function(v,suppressEvent){
26807 if (!this.el.dom) {
26810 var old = this.el.dom.value ;
26811 this.el.dom.value = v;
26812 if (suppressEvent) {
26816 // update display..
26817 this.viewEl.select('img',true).each(function(e,i,n) {
26819 var on = e.is(".x-menu-item-checked");
26820 var newv = v.indexOf(String(n)) > -1;
26822 e.toggleClass('x-menu-item-checked');
26828 this.fireEvent('change', this, v, old);
26833 // handle setting of hidden value by some other method!!?!?
26834 setFromHidden: function()
26839 //console.log("SET FROM HIDDEN");
26840 //alert('setFrom hidden');
26841 this.setValue(this.el.dom.value);
26844 onDestroy : function()
26847 Roo.get(this.viewEl).remove();
26850 Roo.form.DayPicker.superclass.onDestroy.call(this);
26854 * RooJS Library 1.1.1
26855 * Copyright(c) 2008-2011 Alan Knowles
26862 * @class Roo.form.ComboCheck
26863 * @extends Roo.form.ComboBox
26864 * A combobox for multiple select items.
26866 * FIXME - could do with a reset button..
26869 * Create a new ComboCheck
26870 * @param {Object} config Configuration options
26872 Roo.form.ComboCheck = function(config){
26873 Roo.form.ComboCheck.superclass.constructor.call(this, config);
26874 // should verify some data...
26876 // hiddenName = required..
26877 // displayField = required
26878 // valudField == required
26879 var req= [ 'hiddenName', 'displayField', 'valueField' ];
26881 Roo.each(req, function(e) {
26882 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26883 throw "Roo.form.ComboCheck : missing value for: " + e;
26890 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26895 selectedClass: 'x-menu-item-checked',
26898 onRender : function(ct, position){
26904 var cls = 'x-combo-list';
26907 this.tpl = new Roo.Template({
26908 html : '<div class="'+cls+'-item x-menu-check-item">' +
26909 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
26910 '<span>{' + this.displayField + '}</span>' +
26917 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26918 this.view.singleSelect = false;
26919 this.view.multiSelect = true;
26920 this.view.toggleSelect = true;
26921 this.pageTb.add(new Roo.Toolbar.Fill(), {
26924 handler: function()
26931 onViewOver : function(e, t){
26937 onViewClick : function(doFocus,index){
26941 select: function () {
26942 //Roo.log("SELECT CALLED");
26945 selectByValue : function(xv, scrollIntoView){
26946 var ar = this.getValueArray();
26949 Roo.each(ar, function(v) {
26950 if(v === undefined || v === null){
26953 var r = this.findRecord(this.valueField, v);
26955 sels.push(this.store.indexOf(r))
26959 this.view.select(sels);
26965 onSelect : function(record, index){
26966 // Roo.log("onselect Called");
26967 // this is only called by the clear button now..
26968 this.view.clearSelections();
26969 this.setValue('[]');
26970 if (this.value != this.valueBefore) {
26971 this.fireEvent('change', this, this.value, this.valueBefore);
26972 this.valueBefore = this.value;
26975 getValueArray : function()
26980 //Roo.log(this.value);
26981 if (typeof(this.value) == 'undefined') {
26984 var ar = Roo.decode(this.value);
26985 return ar instanceof Array ? ar : []; //?? valid?
26988 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
26993 expand : function ()
26996 Roo.form.ComboCheck.superclass.expand.call(this);
26997 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
26998 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27003 collapse : function(){
27004 Roo.form.ComboCheck.superclass.collapse.call(this);
27005 var sl = this.view.getSelectedIndexes();
27006 var st = this.store;
27010 Roo.each(sl, function(i) {
27012 nv.push(r.get(this.valueField));
27014 this.setValue(Roo.encode(nv));
27015 if (this.value != this.valueBefore) {
27017 this.fireEvent('change', this, this.value, this.valueBefore);
27018 this.valueBefore = this.value;
27023 setValue : function(v){
27027 var vals = this.getValueArray();
27029 Roo.each(vals, function(k) {
27030 var r = this.findRecord(this.valueField, k);
27032 tv.push(r.data[this.displayField]);
27033 }else if(this.valueNotFoundText !== undefined){
27034 tv.push( this.valueNotFoundText );
27039 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27040 this.hiddenField.value = v;
27046 * Ext JS Library 1.1.1
27047 * Copyright(c) 2006-2007, Ext JS, LLC.
27049 * Originally Released Under LGPL - original licence link has changed is not relivant.
27052 * <script type="text/javascript">
27056 * @class Roo.form.Signature
27057 * @extends Roo.form.Field
27061 * @param {Object} config Configuration options
27064 Roo.form.Signature = function(config){
27065 Roo.form.Signature.superclass.constructor.call(this, config);
27067 this.addEvents({// not in used??
27070 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27071 * @param {Roo.form.Signature} combo This combo box
27076 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27077 * @param {Roo.form.ComboBox} combo This combo box
27078 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27084 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27086 * @cfg {Object} labels Label to use when rendering a form.
27090 * confirm : "Confirm"
27095 confirm : "Confirm"
27098 * @cfg {Number} width The signature panel width (defaults to 300)
27102 * @cfg {Number} height The signature panel height (defaults to 100)
27106 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27108 allowBlank : false,
27111 // {Object} signPanel The signature SVG panel element (defaults to {})
27113 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27114 isMouseDown : false,
27115 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27116 isConfirmed : false,
27117 // {String} signatureTmp SVG mapping string (defaults to empty string)
27121 defaultAutoCreate : { // modified by initCompnoent..
27127 onRender : function(ct, position){
27129 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27131 this.wrap = this.el.wrap({
27132 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27135 this.createToolbar(this);
27136 this.signPanel = this.wrap.createChild({
27138 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27142 this.svgID = Roo.id();
27143 this.svgEl = this.signPanel.createChild({
27144 xmlns : 'http://www.w3.org/2000/svg',
27146 id : this.svgID + "-svg",
27148 height: this.height,
27149 viewBox: '0 0 '+this.width+' '+this.height,
27153 id: this.svgID + "-svg-r",
27155 height: this.height,
27160 id: this.svgID + "-svg-l",
27162 y1: (this.height*0.8), // start set the line in 80% of height
27163 x2: this.width, // end
27164 y2: (this.height*0.8), // end set the line in 80% of height
27166 'stroke-width': "1",
27167 'stroke-dasharray': "3",
27168 'shape-rendering': "crispEdges",
27169 'pointer-events': "none"
27173 id: this.svgID + "-svg-p",
27175 'stroke-width': "3",
27177 'pointer-events': 'none'
27182 this.svgBox = this.svgEl.dom.getScreenCTM();
27184 createSVG : function(){
27185 var svg = this.signPanel;
27186 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27189 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27190 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27191 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27192 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27193 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27194 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27195 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27198 isTouchEvent : function(e){
27199 return e.type.match(/^touch/);
27201 getCoords : function (e) {
27202 var pt = this.svgEl.dom.createSVGPoint();
27205 if (this.isTouchEvent(e)) {
27206 pt.x = e.targetTouches[0].clientX;
27207 pt.y = e.targetTouches[0].clientY;
27209 var a = this.svgEl.dom.getScreenCTM();
27210 var b = a.inverse();
27211 var mx = pt.matrixTransform(b);
27212 return mx.x + ',' + mx.y;
27214 //mouse event headler
27215 down : function (e) {
27216 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27217 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27219 this.isMouseDown = true;
27221 e.preventDefault();
27223 move : function (e) {
27224 if (this.isMouseDown) {
27225 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27226 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27229 e.preventDefault();
27231 up : function (e) {
27232 this.isMouseDown = false;
27233 var sp = this.signatureTmp.split(' ');
27236 if(!sp[sp.length-2].match(/^L/)){
27240 this.signatureTmp = sp.join(" ");
27243 if(this.getValue() != this.signatureTmp){
27244 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27245 this.isConfirmed = false;
27247 e.preventDefault();
27251 * Protected method that will not generally be called directly. It
27252 * is called when the editor creates its toolbar. Override this method if you need to
27253 * add custom toolbar buttons.
27254 * @param {HtmlEditor} editor
27256 createToolbar : function(editor){
27257 function btn(id, toggle, handler){
27258 var xid = fid + '-'+ id ;
27262 cls : 'x-btn-icon x-edit-'+id,
27263 enableToggle:toggle !== false,
27264 scope: editor, // was editor...
27265 handler:handler||editor.relayBtnCmd,
27266 clickEvent:'mousedown',
27267 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27273 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27277 cls : ' x-signature-btn x-signature-'+id,
27278 scope: editor, // was editor...
27279 handler: this.reset,
27280 clickEvent:'mousedown',
27281 text: this.labels.clear
27288 cls : ' x-signature-btn x-signature-'+id,
27289 scope: editor, // was editor...
27290 handler: this.confirmHandler,
27291 clickEvent:'mousedown',
27292 text: this.labels.confirm
27299 * when user is clicked confirm then show this image.....
27301 * @return {String} Image Data URI
27303 getImageDataURI : function(){
27304 var svg = this.svgEl.dom.parentNode.innerHTML;
27305 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27310 * @return {Boolean} this.isConfirmed
27312 getConfirmed : function(){
27313 return this.isConfirmed;
27317 * @return {Number} this.width
27319 getWidth : function(){
27324 * @return {Number} this.height
27326 getHeight : function(){
27327 return this.height;
27330 getSignature : function(){
27331 return this.signatureTmp;
27334 reset : function(){
27335 this.signatureTmp = '';
27336 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27337 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27338 this.isConfirmed = false;
27339 Roo.form.Signature.superclass.reset.call(this);
27341 setSignature : function(s){
27342 this.signatureTmp = s;
27343 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27344 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27346 this.isConfirmed = false;
27347 Roo.form.Signature.superclass.reset.call(this);
27350 // Roo.log(this.signPanel.dom.contentWindow.up())
27353 setConfirmed : function(){
27357 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27360 confirmHandler : function(){
27361 if(!this.getSignature()){
27365 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27366 this.setValue(this.getSignature());
27367 this.isConfirmed = true;
27369 this.fireEvent('confirm', this);
27372 // Subclasses should provide the validation implementation by overriding this
27373 validateValue : function(value){
27374 if(this.allowBlank){
27378 if(this.isConfirmed){
27385 * Ext JS Library 1.1.1
27386 * Copyright(c) 2006-2007, Ext JS, LLC.
27388 * Originally Released Under LGPL - original licence link has changed is not relivant.
27391 * <script type="text/javascript">
27396 * @class Roo.form.ComboBox
27397 * @extends Roo.form.TriggerField
27398 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27400 * Create a new ComboBox.
27401 * @param {Object} config Configuration options
27403 Roo.form.Select = function(config){
27404 Roo.form.Select.superclass.constructor.call(this, config);
27408 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27410 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27413 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27414 * rendering into an Roo.Editor, defaults to false)
27417 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27418 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27421 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27424 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27425 * the dropdown list (defaults to undefined, with no header element)
27429 * @cfg {String/Roo.Template} tpl The template to use to render the output
27433 defaultAutoCreate : {tag: "select" },
27435 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27437 listWidth: undefined,
27439 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27440 * mode = 'remote' or 'text' if mode = 'local')
27442 displayField: undefined,
27444 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27445 * mode = 'remote' or 'value' if mode = 'local').
27446 * Note: use of a valueField requires the user make a selection
27447 * in order for a value to be mapped.
27449 valueField: undefined,
27453 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27454 * field's data value (defaults to the underlying DOM element's name)
27456 hiddenName: undefined,
27458 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27462 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27464 selectedClass: 'x-combo-selected',
27466 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
27467 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27468 * which displays a downward arrow icon).
27470 triggerClass : 'x-form-arrow-trigger',
27472 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27476 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27477 * anchor positions (defaults to 'tl-bl')
27479 listAlign: 'tl-bl?',
27481 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27485 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
27486 * query specified by the allQuery config option (defaults to 'query')
27488 triggerAction: 'query',
27490 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27491 * (defaults to 4, does not apply if editable = false)
27495 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27496 * delay (typeAheadDelay) if it matches a known value (defaults to false)
27500 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27501 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27505 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27506 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
27510 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
27511 * when editable = true (defaults to false)
27513 selectOnFocus:false,
27515 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27517 queryParam: 'query',
27519 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
27520 * when mode = 'remote' (defaults to 'Loading...')
27522 loadingText: 'Loading...',
27524 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27528 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27532 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27533 * traditional select (defaults to true)
27537 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27541 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27545 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27546 * listWidth has a higher value)
27550 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27551 * allow the user to set arbitrary text into the field (defaults to false)
27553 forceSelection:false,
27555 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27556 * if typeAhead = true (defaults to 250)
27558 typeAheadDelay : 250,
27560 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27561 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27563 valueNotFoundText : undefined,
27566 * @cfg {String} defaultValue The value displayed after loading the store.
27571 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27573 blockFocus : false,
27576 * @cfg {Boolean} disableClear Disable showing of clear button.
27578 disableClear : false,
27580 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
27582 alwaysQuery : false,
27588 // element that contains real text value.. (when hidden is used..)
27591 onRender : function(ct, position){
27592 Roo.form.Field.prototype.onRender.call(this, ct, position);
27595 this.store.on('beforeload', this.onBeforeLoad, this);
27596 this.store.on('load', this.onLoad, this);
27597 this.store.on('loadexception', this.onLoadException, this);
27598 this.store.load({});
27606 initEvents : function(){
27607 //Roo.form.ComboBox.superclass.initEvents.call(this);
27611 onDestroy : function(){
27614 this.store.un('beforeload', this.onBeforeLoad, this);
27615 this.store.un('load', this.onLoad, this);
27616 this.store.un('loadexception', this.onLoadException, this);
27618 //Roo.form.ComboBox.superclass.onDestroy.call(this);
27622 fireKey : function(e){
27623 if(e.isNavKeyPress() && !this.list.isVisible()){
27624 this.fireEvent("specialkey", this, e);
27629 onResize: function(w, h){
27637 * Allow or prevent the user from directly editing the field text. If false is passed,
27638 * the user will only be able to select from the items defined in the dropdown list. This method
27639 * is the runtime equivalent of setting the 'editable' config option at config time.
27640 * @param {Boolean} value True to allow the user to directly edit the field text
27642 setEditable : function(value){
27647 onBeforeLoad : function(){
27649 Roo.log("Select before load");
27652 this.innerList.update(this.loadingText ?
27653 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27654 //this.restrictHeight();
27655 this.selectedIndex = -1;
27659 onLoad : function(){
27662 var dom = this.el.dom;
27663 dom.innerHTML = '';
27664 var od = dom.ownerDocument;
27666 if (this.emptyText) {
27667 var op = od.createElement('option');
27668 op.setAttribute('value', '');
27669 op.innerHTML = String.format('{0}', this.emptyText);
27670 dom.appendChild(op);
27672 if(this.store.getCount() > 0){
27674 var vf = this.valueField;
27675 var df = this.displayField;
27676 this.store.data.each(function(r) {
27677 // which colmsn to use... testing - cdoe / title..
27678 var op = od.createElement('option');
27679 op.setAttribute('value', r.data[vf]);
27680 op.innerHTML = String.format('{0}', r.data[df]);
27681 dom.appendChild(op);
27683 if (typeof(this.defaultValue != 'undefined')) {
27684 this.setValue(this.defaultValue);
27689 //this.onEmptyResults();
27694 onLoadException : function()
27696 dom.innerHTML = '';
27698 Roo.log("Select on load exception");
27702 Roo.log(this.store.reader.jsonData);
27703 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27704 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27710 onTypeAhead : function(){
27715 onSelect : function(record, index){
27716 Roo.log('on select?');
27718 if(this.fireEvent('beforeselect', this, record, index) !== false){
27719 this.setFromData(index > -1 ? record.data : false);
27721 this.fireEvent('select', this, record, index);
27726 * Returns the currently selected field value or empty string if no value is set.
27727 * @return {String} value The selected value
27729 getValue : function(){
27730 var dom = this.el.dom;
27731 this.value = dom.options[dom.selectedIndex].value;
27737 * Clears any text/value currently set in the field
27739 clearValue : function(){
27741 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27746 * Sets the specified value into the field. If the value finds a match, the corresponding record text
27747 * will be displayed in the field. If the value does not match the data value of an existing item,
27748 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27749 * Otherwise the field will be blank (although the value will still be set).
27750 * @param {String} value The value to match
27752 setValue : function(v){
27753 var d = this.el.dom;
27754 for (var i =0; i < d.options.length;i++) {
27755 if (v == d.options[i].value) {
27756 d.selectedIndex = i;
27764 * @property {Object} the last set data for the element
27769 * Sets the value of the field based on a object which is related to the record format for the store.
27770 * @param {Object} value the value to set as. or false on reset?
27772 setFromData : function(o){
27773 Roo.log('setfrom data?');
27779 reset : function(){
27783 findRecord : function(prop, value){
27788 if(this.store.getCount() > 0){
27789 this.store.each(function(r){
27790 if(r.data[prop] == value){
27800 getName: function()
27802 // returns hidden if it's set..
27803 if (!this.rendered) {return ''};
27804 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
27812 onEmptyResults : function(){
27813 Roo.log('empty results');
27818 * Returns true if the dropdown list is expanded, else false.
27820 isExpanded : function(){
27825 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27826 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27827 * @param {String} value The data value of the item to select
27828 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27829 * selected item if it is not currently in view (defaults to true)
27830 * @return {Boolean} True if the value matched an item in the list, else false
27832 selectByValue : function(v, scrollIntoView){
27833 Roo.log('select By Value');
27836 if(v !== undefined && v !== null){
27837 var r = this.findRecord(this.valueField || this.displayField, v);
27839 this.select(this.store.indexOf(r), scrollIntoView);
27847 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27848 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27849 * @param {Number} index The zero-based index of the list item to select
27850 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27851 * selected item if it is not currently in view (defaults to true)
27853 select : function(index, scrollIntoView){
27854 Roo.log('select ');
27857 this.selectedIndex = index;
27858 this.view.select(index);
27859 if(scrollIntoView !== false){
27860 var el = this.view.getNode(index);
27862 this.innerList.scrollChildIntoView(el, false);
27870 validateBlur : function(){
27877 initQuery : function(){
27878 this.doQuery(this.getRawValue());
27882 doForce : function(){
27883 if(this.el.dom.value.length > 0){
27884 this.el.dom.value =
27885 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27891 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
27892 * query allowing the query action to be canceled if needed.
27893 * @param {String} query The SQL query to execute
27894 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27895 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
27896 * saved in the current store (defaults to false)
27898 doQuery : function(q, forceAll){
27900 Roo.log('doQuery?');
27901 if(q === undefined || q === null){
27906 forceAll: forceAll,
27910 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27914 forceAll = qe.forceAll;
27915 if(forceAll === true || (q.length >= this.minChars)){
27916 if(this.lastQuery != q || this.alwaysQuery){
27917 this.lastQuery = q;
27918 if(this.mode == 'local'){
27919 this.selectedIndex = -1;
27921 this.store.clearFilter();
27923 this.store.filter(this.displayField, q);
27927 this.store.baseParams[this.queryParam] = q;
27929 params: this.getParams(q)
27934 this.selectedIndex = -1;
27941 getParams : function(q){
27943 //p[this.queryParam] = q;
27946 p.limit = this.pageSize;
27952 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27954 collapse : function(){
27959 collapseIf : function(e){
27964 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27966 expand : function(){
27974 * @cfg {Boolean} grow
27978 * @cfg {Number} growMin
27982 * @cfg {Number} growMax
27990 setWidth : function()
27994 getResizeEl : function(){
27997 });//<script type="text/javasscript">
28001 * @class Roo.DDView
28002 * A DnD enabled version of Roo.View.
28003 * @param {Element/String} container The Element in which to create the View.
28004 * @param {String} tpl The template string used to create the markup for each element of the View
28005 * @param {Object} config The configuration properties. These include all the config options of
28006 * {@link Roo.View} plus some specific to this class.<br>
28008 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28009 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28011 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28012 .x-view-drag-insert-above {
28013 border-top:1px dotted #3366cc;
28015 .x-view-drag-insert-below {
28016 border-bottom:1px dotted #3366cc;
28022 Roo.DDView = function(container, tpl, config) {
28023 Roo.DDView.superclass.constructor.apply(this, arguments);
28024 this.getEl().setStyle("outline", "0px none");
28025 this.getEl().unselectable();
28026 if (this.dragGroup) {
28027 this.setDraggable(this.dragGroup.split(","));
28029 if (this.dropGroup) {
28030 this.setDroppable(this.dropGroup.split(","));
28032 if (this.deletable) {
28033 this.setDeletable();
28035 this.isDirtyFlag = false;
28041 Roo.extend(Roo.DDView, Roo.View, {
28042 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28043 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28044 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28045 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28049 reset: Roo.emptyFn,
28051 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28053 validate: function() {
28057 destroy: function() {
28058 this.purgeListeners();
28059 this.getEl.removeAllListeners();
28060 this.getEl().remove();
28061 if (this.dragZone) {
28062 if (this.dragZone.destroy) {
28063 this.dragZone.destroy();
28066 if (this.dropZone) {
28067 if (this.dropZone.destroy) {
28068 this.dropZone.destroy();
28073 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28074 getName: function() {
28078 /** Loads the View from a JSON string representing the Records to put into the Store. */
28079 setValue: function(v) {
28081 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28084 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28085 this.store.proxy = new Roo.data.MemoryProxy(data);
28089 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28090 getValue: function() {
28092 this.store.each(function(rec) {
28093 result += rec.id + ',';
28095 return result.substr(0, result.length - 1) + ')';
28098 getIds: function() {
28099 var i = 0, result = new Array(this.store.getCount());
28100 this.store.each(function(rec) {
28101 result[i++] = rec.id;
28106 isDirty: function() {
28107 return this.isDirtyFlag;
28111 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28112 * whole Element becomes the target, and this causes the drop gesture to append.
28114 getTargetFromEvent : function(e) {
28115 var target = e.getTarget();
28116 while ((target !== null) && (target.parentNode != this.el.dom)) {
28117 target = target.parentNode;
28120 target = this.el.dom.lastChild || this.el.dom;
28126 * Create the drag data which consists of an object which has the property "ddel" as
28127 * the drag proxy element.
28129 getDragData : function(e) {
28130 var target = this.findItemFromChild(e.getTarget());
28132 this.handleSelection(e);
28133 var selNodes = this.getSelectedNodes();
28136 copy: this.copy || (this.allowCopy && e.ctrlKey),
28140 var selectedIndices = this.getSelectedIndexes();
28141 for (var i = 0; i < selectedIndices.length; i++) {
28142 dragData.records.push(this.store.getAt(selectedIndices[i]));
28144 if (selNodes.length == 1) {
28145 dragData.ddel = target.cloneNode(true); // the div element
28147 var div = document.createElement('div'); // create the multi element drag "ghost"
28148 div.className = 'multi-proxy';
28149 for (var i = 0, len = selNodes.length; i < len; i++) {
28150 div.appendChild(selNodes[i].cloneNode(true));
28152 dragData.ddel = div;
28154 //console.log(dragData)
28155 //console.log(dragData.ddel.innerHTML)
28158 //console.log('nodragData')
28162 /** Specify to which ddGroup items in this DDView may be dragged. */
28163 setDraggable: function(ddGroup) {
28164 if (ddGroup instanceof Array) {
28165 Roo.each(ddGroup, this.setDraggable, this);
28168 if (this.dragZone) {
28169 this.dragZone.addToGroup(ddGroup);
28171 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28172 containerScroll: true,
28176 // Draggability implies selection. DragZone's mousedown selects the element.
28177 if (!this.multiSelect) { this.singleSelect = true; }
28179 // Wire the DragZone's handlers up to methods in *this*
28180 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28184 /** Specify from which ddGroup this DDView accepts drops. */
28185 setDroppable: function(ddGroup) {
28186 if (ddGroup instanceof Array) {
28187 Roo.each(ddGroup, this.setDroppable, this);
28190 if (this.dropZone) {
28191 this.dropZone.addToGroup(ddGroup);
28193 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28194 containerScroll: true,
28198 // Wire the DropZone's handlers up to methods in *this*
28199 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28200 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28201 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28202 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28203 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28207 /** Decide whether to drop above or below a View node. */
28208 getDropPoint : function(e, n, dd){
28209 if (n == this.el.dom) { return "above"; }
28210 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28211 var c = t + (b - t) / 2;
28212 var y = Roo.lib.Event.getPageY(e);
28220 onNodeEnter : function(n, dd, e, data){
28224 onNodeOver : function(n, dd, e, data){
28225 var pt = this.getDropPoint(e, n, dd);
28226 // set the insert point style on the target node
28227 var dragElClass = this.dropNotAllowed;
28230 if (pt == "above"){
28231 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28232 targetElClass = "x-view-drag-insert-above";
28234 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28235 targetElClass = "x-view-drag-insert-below";
28237 if (this.lastInsertClass != targetElClass){
28238 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28239 this.lastInsertClass = targetElClass;
28242 return dragElClass;
28245 onNodeOut : function(n, dd, e, data){
28246 this.removeDropIndicators(n);
28249 onNodeDrop : function(n, dd, e, data){
28250 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28253 var pt = this.getDropPoint(e, n, dd);
28254 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28255 if (pt == "below") { insertAt++; }
28256 for (var i = 0; i < data.records.length; i++) {
28257 var r = data.records[i];
28258 var dup = this.store.getById(r.id);
28259 if (dup && (dd != this.dragZone)) {
28260 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28263 this.store.insert(insertAt++, r.copy());
28265 data.source.isDirtyFlag = true;
28267 this.store.insert(insertAt++, r);
28269 this.isDirtyFlag = true;
28272 this.dragZone.cachedTarget = null;
28276 removeDropIndicators : function(n){
28278 Roo.fly(n).removeClass([
28279 "x-view-drag-insert-above",
28280 "x-view-drag-insert-below"]);
28281 this.lastInsertClass = "_noclass";
28286 * Utility method. Add a delete option to the DDView's context menu.
28287 * @param {String} imageUrl The URL of the "delete" icon image.
28289 setDeletable: function(imageUrl) {
28290 if (!this.singleSelect && !this.multiSelect) {
28291 this.singleSelect = true;
28293 var c = this.getContextMenu();
28294 this.contextMenu.on("itemclick", function(item) {
28297 this.remove(this.getSelectedIndexes());
28301 this.contextMenu.add({
28308 /** Return the context menu for this DDView. */
28309 getContextMenu: function() {
28310 if (!this.contextMenu) {
28311 // Create the View's context menu
28312 this.contextMenu = new Roo.menu.Menu({
28313 id: this.id + "-contextmenu"
28315 this.el.on("contextmenu", this.showContextMenu, this);
28317 return this.contextMenu;
28320 disableContextMenu: function() {
28321 if (this.contextMenu) {
28322 this.el.un("contextmenu", this.showContextMenu, this);
28326 showContextMenu: function(e, item) {
28327 item = this.findItemFromChild(e.getTarget());
28330 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28331 this.contextMenu.showAt(e.getXY());
28336 * Remove {@link Roo.data.Record}s at the specified indices.
28337 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28339 remove: function(selectedIndices) {
28340 selectedIndices = [].concat(selectedIndices);
28341 for (var i = 0; i < selectedIndices.length; i++) {
28342 var rec = this.store.getAt(selectedIndices[i]);
28343 this.store.remove(rec);
28348 * Double click fires the event, but also, if this is draggable, and there is only one other
28349 * related DropZone, it transfers the selected node.
28351 onDblClick : function(e){
28352 var item = this.findItemFromChild(e.getTarget());
28354 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28357 if (this.dragGroup) {
28358 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28359 while (targets.indexOf(this.dropZone) > -1) {
28360 targets.remove(this.dropZone);
28362 if (targets.length == 1) {
28363 this.dragZone.cachedTarget = null;
28364 var el = Roo.get(targets[0].getEl());
28365 var box = el.getBox(true);
28366 targets[0].onNodeDrop(el.dom, {
28368 xy: [box.x, box.y + box.height - 1]
28369 }, null, this.getDragData(e));
28375 handleSelection: function(e) {
28376 this.dragZone.cachedTarget = null;
28377 var item = this.findItemFromChild(e.getTarget());
28379 this.clearSelections(true);
28382 if (item && (this.multiSelect || this.singleSelect)){
28383 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28384 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28385 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28386 this.unselect(item);
28388 this.select(item, this.multiSelect && e.ctrlKey);
28389 this.lastSelection = item;
28394 onItemClick : function(item, index, e){
28395 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28401 unselect : function(nodeInfo, suppressEvent){
28402 var node = this.getNode(nodeInfo);
28403 if(node && this.isSelected(node)){
28404 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28405 Roo.fly(node).removeClass(this.selectedClass);
28406 this.selections.remove(node);
28407 if(!suppressEvent){
28408 this.fireEvent("selectionchange", this, this.selections);
28416 * Ext JS Library 1.1.1
28417 * Copyright(c) 2006-2007, Ext JS, LLC.
28419 * Originally Released Under LGPL - original licence link has changed is not relivant.
28422 * <script type="text/javascript">
28426 * @class Roo.LayoutManager
28427 * @extends Roo.util.Observable
28428 * Base class for layout managers.
28430 Roo.LayoutManager = function(container, config){
28431 Roo.LayoutManager.superclass.constructor.call(this);
28432 this.el = Roo.get(container);
28433 // ie scrollbar fix
28434 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28435 document.body.scroll = "no";
28436 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28437 this.el.position('relative');
28439 this.id = this.el.id;
28440 this.el.addClass("x-layout-container");
28441 /** false to disable window resize monitoring @type Boolean */
28442 this.monitorWindowResize = true;
28447 * Fires when a layout is performed.
28448 * @param {Roo.LayoutManager} this
28452 * @event regionresized
28453 * Fires when the user resizes a region.
28454 * @param {Roo.LayoutRegion} region The resized region
28455 * @param {Number} newSize The new size (width for east/west, height for north/south)
28457 "regionresized" : true,
28459 * @event regioncollapsed
28460 * Fires when a region is collapsed.
28461 * @param {Roo.LayoutRegion} region The collapsed region
28463 "regioncollapsed" : true,
28465 * @event regionexpanded
28466 * Fires when a region is expanded.
28467 * @param {Roo.LayoutRegion} region The expanded region
28469 "regionexpanded" : true
28471 this.updating = false;
28472 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28475 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28477 * Returns true if this layout is currently being updated
28478 * @return {Boolean}
28480 isUpdating : function(){
28481 return this.updating;
28485 * Suspend the LayoutManager from doing auto-layouts while
28486 * making multiple add or remove calls
28488 beginUpdate : function(){
28489 this.updating = true;
28493 * Restore auto-layouts and optionally disable the manager from performing a layout
28494 * @param {Boolean} noLayout true to disable a layout update
28496 endUpdate : function(noLayout){
28497 this.updating = false;
28503 layout: function(){
28507 onRegionResized : function(region, newSize){
28508 this.fireEvent("regionresized", region, newSize);
28512 onRegionCollapsed : function(region){
28513 this.fireEvent("regioncollapsed", region);
28516 onRegionExpanded : function(region){
28517 this.fireEvent("regionexpanded", region);
28521 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28522 * performs box-model adjustments.
28523 * @return {Object} The size as an object {width: (the width), height: (the height)}
28525 getViewSize : function(){
28527 if(this.el.dom != document.body){
28528 size = this.el.getSize();
28530 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28532 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28533 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28538 * Returns the Element this layout is bound to.
28539 * @return {Roo.Element}
28541 getEl : function(){
28546 * Returns the specified region.
28547 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28548 * @return {Roo.LayoutRegion}
28550 getRegion : function(target){
28551 return this.regions[target.toLowerCase()];
28554 onWindowResize : function(){
28555 if(this.monitorWindowResize){
28561 * Ext JS Library 1.1.1
28562 * Copyright(c) 2006-2007, Ext JS, LLC.
28564 * Originally Released Under LGPL - original licence link has changed is not relivant.
28567 * <script type="text/javascript">
28570 * @class Roo.BorderLayout
28571 * @extends Roo.LayoutManager
28572 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28573 * please see: <br><br>
28574 * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
28575 * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
28578 var layout = new Roo.BorderLayout(document.body, {
28612 preferredTabWidth: 150
28617 var CP = Roo.ContentPanel;
28619 layout.beginUpdate();
28620 layout.add("north", new CP("north", "North"));
28621 layout.add("south", new CP("south", {title: "South", closable: true}));
28622 layout.add("west", new CP("west", {title: "West"}));
28623 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28624 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28625 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28626 layout.getRegion("center").showPanel("center1");
28627 layout.endUpdate();
28630 <b>The container the layout is rendered into can be either the body element or any other element.
28631 If it is not the body element, the container needs to either be an absolute positioned element,
28632 or you will need to add "position:relative" to the css of the container. You will also need to specify
28633 the container size if it is not the body element.</b>
28636 * Create a new BorderLayout
28637 * @param {String/HTMLElement/Element} container The container this layout is bound to
28638 * @param {Object} config Configuration options
28640 Roo.BorderLayout = function(container, config){
28641 config = config || {};
28642 Roo.BorderLayout.superclass.constructor.call(this, container, config);
28643 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28644 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28645 var target = this.factory.validRegions[i];
28646 if(config[target]){
28647 this.addRegion(target, config[target]);
28652 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28654 * Creates and adds a new region if it doesn't already exist.
28655 * @param {String} target The target region key (north, south, east, west or center).
28656 * @param {Object} config The regions config object
28657 * @return {BorderLayoutRegion} The new region
28659 addRegion : function(target, config){
28660 if(!this.regions[target]){
28661 var r = this.factory.create(target, this, config);
28662 this.bindRegion(target, r);
28664 return this.regions[target];
28668 bindRegion : function(name, r){
28669 this.regions[name] = r;
28670 r.on("visibilitychange", this.layout, this);
28671 r.on("paneladded", this.layout, this);
28672 r.on("panelremoved", this.layout, this);
28673 r.on("invalidated", this.layout, this);
28674 r.on("resized", this.onRegionResized, this);
28675 r.on("collapsed", this.onRegionCollapsed, this);
28676 r.on("expanded", this.onRegionExpanded, this);
28680 * Performs a layout update.
28682 layout : function(){
28683 if(this.updating) {
28686 var size = this.getViewSize();
28687 var w = size.width;
28688 var h = size.height;
28693 //var x = 0, y = 0;
28695 var rs = this.regions;
28696 var north = rs["north"];
28697 var south = rs["south"];
28698 var west = rs["west"];
28699 var east = rs["east"];
28700 var center = rs["center"];
28701 //if(this.hideOnLayout){ // not supported anymore
28702 //c.el.setStyle("display", "none");
28704 if(north && north.isVisible()){
28705 var b = north.getBox();
28706 var m = north.getMargins();
28707 b.width = w - (m.left+m.right);
28710 centerY = b.height + b.y + m.bottom;
28711 centerH -= centerY;
28712 north.updateBox(this.safeBox(b));
28714 if(south && south.isVisible()){
28715 var b = south.getBox();
28716 var m = south.getMargins();
28717 b.width = w - (m.left+m.right);
28719 var totalHeight = (b.height + m.top + m.bottom);
28720 b.y = h - totalHeight + m.top;
28721 centerH -= totalHeight;
28722 south.updateBox(this.safeBox(b));
28724 if(west && west.isVisible()){
28725 var b = west.getBox();
28726 var m = west.getMargins();
28727 b.height = centerH - (m.top+m.bottom);
28729 b.y = centerY + m.top;
28730 var totalWidth = (b.width + m.left + m.right);
28731 centerX += totalWidth;
28732 centerW -= totalWidth;
28733 west.updateBox(this.safeBox(b));
28735 if(east && east.isVisible()){
28736 var b = east.getBox();
28737 var m = east.getMargins();
28738 b.height = centerH - (m.top+m.bottom);
28739 var totalWidth = (b.width + m.left + m.right);
28740 b.x = w - totalWidth + m.left;
28741 b.y = centerY + m.top;
28742 centerW -= totalWidth;
28743 east.updateBox(this.safeBox(b));
28746 var m = center.getMargins();
28748 x: centerX + m.left,
28749 y: centerY + m.top,
28750 width: centerW - (m.left+m.right),
28751 height: centerH - (m.top+m.bottom)
28753 //if(this.hideOnLayout){
28754 //center.el.setStyle("display", "block");
28756 center.updateBox(this.safeBox(centerBox));
28759 this.fireEvent("layout", this);
28763 safeBox : function(box){
28764 box.width = Math.max(0, box.width);
28765 box.height = Math.max(0, box.height);
28770 * Adds a ContentPanel (or subclass) to this layout.
28771 * @param {String} target The target region key (north, south, east, west or center).
28772 * @param {Roo.ContentPanel} panel The panel to add
28773 * @return {Roo.ContentPanel} The added panel
28775 add : function(target, panel){
28777 target = target.toLowerCase();
28778 return this.regions[target].add(panel);
28782 * Remove a ContentPanel (or subclass) to this layout.
28783 * @param {String} target The target region key (north, south, east, west or center).
28784 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28785 * @return {Roo.ContentPanel} The removed panel
28787 remove : function(target, panel){
28788 target = target.toLowerCase();
28789 return this.regions[target].remove(panel);
28793 * Searches all regions for a panel with the specified id
28794 * @param {String} panelId
28795 * @return {Roo.ContentPanel} The panel or null if it wasn't found
28797 findPanel : function(panelId){
28798 var rs = this.regions;
28799 for(var target in rs){
28800 if(typeof rs[target] != "function"){
28801 var p = rs[target].getPanel(panelId);
28811 * Searches all regions for a panel with the specified id and activates (shows) it.
28812 * @param {String/ContentPanel} panelId The panels id or the panel itself
28813 * @return {Roo.ContentPanel} The shown panel or null
28815 showPanel : function(panelId) {
28816 var rs = this.regions;
28817 for(var target in rs){
28818 var r = rs[target];
28819 if(typeof r != "function"){
28820 if(r.hasPanel(panelId)){
28821 return r.showPanel(panelId);
28829 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28830 * @param {Roo.state.Provider} provider (optional) An alternate state provider
28832 restoreState : function(provider){
28834 provider = Roo.state.Manager;
28836 var sm = new Roo.LayoutStateManager();
28837 sm.init(this, provider);
28841 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
28842 * object should contain properties for each region to add ContentPanels to, and each property's value should be
28843 * a valid ContentPanel config object. Example:
28845 // Create the main layout
28846 var layout = new Roo.BorderLayout('main-ct', {
28857 // Create and add multiple ContentPanels at once via configs
28860 id: 'source-files',
28862 title:'Ext Source Files',
28875 * @param {Object} regions An object containing ContentPanel configs by region name
28877 batchAdd : function(regions){
28878 this.beginUpdate();
28879 for(var rname in regions){
28880 var lr = this.regions[rname];
28882 this.addTypedPanels(lr, regions[rname]);
28889 addTypedPanels : function(lr, ps){
28890 if(typeof ps == 'string'){
28891 lr.add(new Roo.ContentPanel(ps));
28893 else if(ps instanceof Array){
28894 for(var i =0, len = ps.length; i < len; i++){
28895 this.addTypedPanels(lr, ps[i]);
28898 else if(!ps.events){ // raw config?
28900 delete ps.el; // prevent conflict
28901 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28903 else { // panel object assumed!
28908 * Adds a xtype elements to the layout.
28912 xtype : 'ContentPanel',
28919 xtype : 'NestedLayoutPanel',
28925 items : [ ... list of content panels or nested layout panels.. ]
28929 * @param {Object} cfg Xtype definition of item to add.
28931 addxtype : function(cfg)
28933 // basically accepts a pannel...
28934 // can accept a layout region..!?!?
28935 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28937 if (!cfg.xtype.match(/Panel$/)) {
28942 if (typeof(cfg.region) == 'undefined') {
28943 Roo.log("Failed to add Panel, region was not set");
28947 var region = cfg.region;
28953 xitems = cfg.items;
28960 case 'ContentPanel': // ContentPanel (el, cfg)
28961 case 'ScrollPanel': // ContentPanel (el, cfg)
28963 if(cfg.autoCreate) {
28964 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28966 var el = this.el.createChild();
28967 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28970 this.add(region, ret);
28974 case 'TreePanel': // our new panel!
28975 cfg.el = this.el.createChild();
28976 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28977 this.add(region, ret);
28980 case 'NestedLayoutPanel':
28981 // create a new Layout (which is a Border Layout...
28982 var el = this.el.createChild();
28983 var clayout = cfg.layout;
28985 clayout.items = clayout.items || [];
28986 // replace this exitems with the clayout ones..
28987 xitems = clayout.items;
28990 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28991 cfg.background = false;
28993 var layout = new Roo.BorderLayout(el, clayout);
28995 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28996 //console.log('adding nested layout panel ' + cfg.toSource());
28997 this.add(region, ret);
28998 nb = {}; /// find first...
29003 // needs grid and region
29005 //var el = this.getRegion(region).el.createChild();
29006 var el = this.el.createChild();
29007 // create the grid first...
29009 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29011 if (region == 'center' && this.active ) {
29012 cfg.background = false;
29014 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29016 this.add(region, ret);
29017 if (cfg.background) {
29018 ret.on('activate', function(gp) {
29019 if (!gp.grid.rendered) {
29034 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29036 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29037 this.add(region, ret);
29040 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29044 // GridPanel (grid, cfg)
29047 this.beginUpdate();
29051 Roo.each(xitems, function(i) {
29052 region = nb && i.region ? i.region : false;
29054 var add = ret.addxtype(i);
29057 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29058 if (!i.background) {
29059 abn[region] = nb[region] ;
29066 // make the last non-background panel active..
29067 //if (nb) { Roo.log(abn); }
29070 for(var r in abn) {
29071 region = this.getRegion(r);
29073 // tried using nb[r], but it does not work..
29075 region.showPanel(abn[r]);
29086 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29087 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29088 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29089 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29092 var CP = Roo.ContentPanel;
29094 var layout = Roo.BorderLayout.create({
29098 panels: [new CP("north", "North")]
29107 panels: [new CP("west", {title: "West"})]
29116 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29125 panels: [new CP("south", {title: "South", closable: true})]
29132 preferredTabWidth: 150,
29134 new CP("center1", {title: "Close Me", closable: true}),
29135 new CP("center2", {title: "Center Panel", closable: false})
29140 layout.getRegion("center").showPanel("center1");
29145 Roo.BorderLayout.create = function(config, targetEl){
29146 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29147 layout.beginUpdate();
29148 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29149 for(var j = 0, jlen = regions.length; j < jlen; j++){
29150 var lr = regions[j];
29151 if(layout.regions[lr] && config[lr].panels){
29152 var r = layout.regions[lr];
29153 var ps = config[lr].panels;
29154 layout.addTypedPanels(r, ps);
29157 layout.endUpdate();
29162 Roo.BorderLayout.RegionFactory = {
29164 validRegions : ["north","south","east","west","center"],
29167 create : function(target, mgr, config){
29168 target = target.toLowerCase();
29169 if(config.lightweight || config.basic){
29170 return new Roo.BasicLayoutRegion(mgr, config, target);
29174 return new Roo.NorthLayoutRegion(mgr, config);
29176 return new Roo.SouthLayoutRegion(mgr, config);
29178 return new Roo.EastLayoutRegion(mgr, config);
29180 return new Roo.WestLayoutRegion(mgr, config);
29182 return new Roo.CenterLayoutRegion(mgr, config);
29184 throw 'Layout region "'+target+'" not supported.';
29188 * Ext JS Library 1.1.1
29189 * Copyright(c) 2006-2007, Ext JS, LLC.
29191 * Originally Released Under LGPL - original licence link has changed is not relivant.
29194 * <script type="text/javascript">
29198 * @class Roo.BasicLayoutRegion
29199 * @extends Roo.util.Observable
29200 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29201 * and does not have a titlebar, tabs or any other features. All it does is size and position
29202 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29204 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29206 this.position = pos;
29209 * @scope Roo.BasicLayoutRegion
29213 * @event beforeremove
29214 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29215 * @param {Roo.LayoutRegion} this
29216 * @param {Roo.ContentPanel} panel The panel
29217 * @param {Object} e The cancel event object
29219 "beforeremove" : true,
29221 * @event invalidated
29222 * Fires when the layout for this region is changed.
29223 * @param {Roo.LayoutRegion} this
29225 "invalidated" : true,
29227 * @event visibilitychange
29228 * Fires when this region is shown or hidden
29229 * @param {Roo.LayoutRegion} this
29230 * @param {Boolean} visibility true or false
29232 "visibilitychange" : true,
29234 * @event paneladded
29235 * Fires when a panel is added.
29236 * @param {Roo.LayoutRegion} this
29237 * @param {Roo.ContentPanel} panel The panel
29239 "paneladded" : true,
29241 * @event panelremoved
29242 * Fires when a panel is removed.
29243 * @param {Roo.LayoutRegion} this
29244 * @param {Roo.ContentPanel} panel The panel
29246 "panelremoved" : true,
29248 * @event beforecollapse
29249 * Fires when this region before collapse.
29250 * @param {Roo.LayoutRegion} this
29252 "beforecollapse" : true,
29255 * Fires when this region is collapsed.
29256 * @param {Roo.LayoutRegion} this
29258 "collapsed" : true,
29261 * Fires when this region is expanded.
29262 * @param {Roo.LayoutRegion} this
29267 * Fires when this region is slid into view.
29268 * @param {Roo.LayoutRegion} this
29270 "slideshow" : true,
29273 * Fires when this region slides out of view.
29274 * @param {Roo.LayoutRegion} this
29276 "slidehide" : true,
29278 * @event panelactivated
29279 * Fires when a panel is activated.
29280 * @param {Roo.LayoutRegion} this
29281 * @param {Roo.ContentPanel} panel The activated panel
29283 "panelactivated" : true,
29286 * Fires when the user resizes this region.
29287 * @param {Roo.LayoutRegion} this
29288 * @param {Number} newSize The new size (width for east/west, height for north/south)
29292 /** A collection of panels in this region. @type Roo.util.MixedCollection */
29293 this.panels = new Roo.util.MixedCollection();
29294 this.panels.getKey = this.getPanelId.createDelegate(this);
29296 this.activePanel = null;
29297 // ensure listeners are added...
29299 if (config.listeners || config.events) {
29300 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29301 listeners : config.listeners || {},
29302 events : config.events || {}
29306 if(skipConfig !== true){
29307 this.applyConfig(config);
29311 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29312 getPanelId : function(p){
29316 applyConfig : function(config){
29317 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29318 this.config = config;
29323 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
29324 * the width, for horizontal (north, south) the height.
29325 * @param {Number} newSize The new width or height
29327 resizeTo : function(newSize){
29328 var el = this.el ? this.el :
29329 (this.activePanel ? this.activePanel.getEl() : null);
29331 switch(this.position){
29334 el.setWidth(newSize);
29335 this.fireEvent("resized", this, newSize);
29339 el.setHeight(newSize);
29340 this.fireEvent("resized", this, newSize);
29346 getBox : function(){
29347 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29350 getMargins : function(){
29351 return this.margins;
29354 updateBox : function(box){
29356 var el = this.activePanel.getEl();
29357 el.dom.style.left = box.x + "px";
29358 el.dom.style.top = box.y + "px";
29359 this.activePanel.setSize(box.width, box.height);
29363 * Returns the container element for this region.
29364 * @return {Roo.Element}
29366 getEl : function(){
29367 return this.activePanel;
29371 * Returns true if this region is currently visible.
29372 * @return {Boolean}
29374 isVisible : function(){
29375 return this.activePanel ? true : false;
29378 setActivePanel : function(panel){
29379 panel = this.getPanel(panel);
29380 if(this.activePanel && this.activePanel != panel){
29381 this.activePanel.setActiveState(false);
29382 this.activePanel.getEl().setLeftTop(-10000,-10000);
29384 this.activePanel = panel;
29385 panel.setActiveState(true);
29387 panel.setSize(this.box.width, this.box.height);
29389 this.fireEvent("panelactivated", this, panel);
29390 this.fireEvent("invalidated");
29394 * Show the specified panel.
29395 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29396 * @return {Roo.ContentPanel} The shown panel or null
29398 showPanel : function(panel){
29399 if(panel = this.getPanel(panel)){
29400 this.setActivePanel(panel);
29406 * Get the active panel for this region.
29407 * @return {Roo.ContentPanel} The active panel or null
29409 getActivePanel : function(){
29410 return this.activePanel;
29414 * Add the passed ContentPanel(s)
29415 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29416 * @return {Roo.ContentPanel} The panel added (if only one was added)
29418 add : function(panel){
29419 if(arguments.length > 1){
29420 for(var i = 0, len = arguments.length; i < len; i++) {
29421 this.add(arguments[i]);
29425 if(this.hasPanel(panel)){
29426 this.showPanel(panel);
29429 var el = panel.getEl();
29430 if(el.dom.parentNode != this.mgr.el.dom){
29431 this.mgr.el.dom.appendChild(el.dom);
29433 if(panel.setRegion){
29434 panel.setRegion(this);
29436 this.panels.add(panel);
29437 el.setStyle("position", "absolute");
29438 if(!panel.background){
29439 this.setActivePanel(panel);
29440 if(this.config.initialSize && this.panels.getCount()==1){
29441 this.resizeTo(this.config.initialSize);
29444 this.fireEvent("paneladded", this, panel);
29449 * Returns true if the panel is in this region.
29450 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29451 * @return {Boolean}
29453 hasPanel : function(panel){
29454 if(typeof panel == "object"){ // must be panel obj
29455 panel = panel.getId();
29457 return this.getPanel(panel) ? true : false;
29461 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29462 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29463 * @param {Boolean} preservePanel Overrides the config preservePanel option
29464 * @return {Roo.ContentPanel} The panel that was removed
29466 remove : function(panel, preservePanel){
29467 panel = this.getPanel(panel);
29472 this.fireEvent("beforeremove", this, panel, e);
29473 if(e.cancel === true){
29476 var panelId = panel.getId();
29477 this.panels.removeKey(panelId);
29482 * Returns the panel specified or null if it's not in this region.
29483 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29484 * @return {Roo.ContentPanel}
29486 getPanel : function(id){
29487 if(typeof id == "object"){ // must be panel obj
29490 return this.panels.get(id);
29494 * Returns this regions position (north/south/east/west/center).
29497 getPosition: function(){
29498 return this.position;
29502 * Ext JS Library 1.1.1
29503 * Copyright(c) 2006-2007, Ext JS, LLC.
29505 * Originally Released Under LGPL - original licence link has changed is not relivant.
29508 * <script type="text/javascript">
29512 * @class Roo.LayoutRegion
29513 * @extends Roo.BasicLayoutRegion
29514 * This class represents a region in a layout manager.
29515 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29516 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29517 * @cfg {Boolean} floatable False to disable floating (defaults to true)
29518 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29519 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
29520 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
29521 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29522 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29523 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29524 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29525 * @cfg {String} title The title for the region (overrides panel titles)
29526 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29527 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29528 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29529 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29530 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29531 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29532 * the space available, similar to FireFox 1.5 tabs (defaults to false)
29533 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29534 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29535 * @cfg {Boolean} showPin True to show a pin button
29536 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29537 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29538 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29539 * @cfg {Number} width For East/West panels
29540 * @cfg {Number} height For North/South panels
29541 * @cfg {Boolean} split To show the splitter
29542 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
29544 Roo.LayoutRegion = function(mgr, config, pos){
29545 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29546 var dh = Roo.DomHelper;
29547 /** This region's container element
29548 * @type Roo.Element */
29549 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29550 /** This region's title element
29551 * @type Roo.Element */
29553 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29554 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
29555 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29557 this.titleEl.enableDisplayMode();
29558 /** This region's title text element
29559 * @type HTMLElement */
29560 this.titleTextEl = this.titleEl.dom.firstChild;
29561 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29562 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29563 this.closeBtn.enableDisplayMode();
29564 this.closeBtn.on("click", this.closeClicked, this);
29565 this.closeBtn.hide();
29567 this.createBody(config);
29568 this.visible = true;
29569 this.collapsed = false;
29571 if(config.hideWhenEmpty){
29573 this.on("paneladded", this.validateVisibility, this);
29574 this.on("panelremoved", this.validateVisibility, this);
29576 this.applyConfig(config);
29579 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29581 createBody : function(){
29582 /** This region's body element
29583 * @type Roo.Element */
29584 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29587 applyConfig : function(c){
29588 if(c.collapsible && this.position != "center" && !this.collapsedEl){
29589 var dh = Roo.DomHelper;
29590 if(c.titlebar !== false){
29591 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29592 this.collapseBtn.on("click", this.collapse, this);
29593 this.collapseBtn.enableDisplayMode();
29595 if(c.showPin === true || this.showPin){
29596 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29597 this.stickBtn.enableDisplayMode();
29598 this.stickBtn.on("click", this.expand, this);
29599 this.stickBtn.hide();
29602 /** This region's collapsed element
29603 * @type Roo.Element */
29604 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29605 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29607 if(c.floatable !== false){
29608 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29609 this.collapsedEl.on("click", this.collapseClick, this);
29612 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29613 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29614 id: "message", unselectable: "on", style:{"float":"left"}});
29615 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29617 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29618 this.expandBtn.on("click", this.expand, this);
29620 if(this.collapseBtn){
29621 this.collapseBtn.setVisible(c.collapsible == true);
29623 this.cmargins = c.cmargins || this.cmargins ||
29624 (this.position == "west" || this.position == "east" ?
29625 {top: 0, left: 2, right:2, bottom: 0} :
29626 {top: 2, left: 0, right:0, bottom: 2});
29627 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29628 this.bottomTabs = c.tabPosition != "top";
29629 this.autoScroll = c.autoScroll || false;
29630 if(this.autoScroll){
29631 this.bodyEl.setStyle("overflow", "auto");
29633 this.bodyEl.setStyle("overflow", "hidden");
29635 //if(c.titlebar !== false){
29636 if((!c.titlebar && !c.title) || c.titlebar === false){
29637 this.titleEl.hide();
29639 this.titleEl.show();
29641 this.titleTextEl.innerHTML = c.title;
29645 this.duration = c.duration || .30;
29646 this.slideDuration = c.slideDuration || .45;
29649 this.collapse(true);
29656 * Returns true if this region is currently visible.
29657 * @return {Boolean}
29659 isVisible : function(){
29660 return this.visible;
29664 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29665 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
29667 setCollapsedTitle : function(title){
29668 title = title || " ";
29669 if(this.collapsedTitleTextEl){
29670 this.collapsedTitleTextEl.innerHTML = title;
29674 getBox : function(){
29676 if(!this.collapsed){
29677 b = this.el.getBox(false, true);
29679 b = this.collapsedEl.getBox(false, true);
29684 getMargins : function(){
29685 return this.collapsed ? this.cmargins : this.margins;
29688 highlight : function(){
29689 this.el.addClass("x-layout-panel-dragover");
29692 unhighlight : function(){
29693 this.el.removeClass("x-layout-panel-dragover");
29696 updateBox : function(box){
29698 if(!this.collapsed){
29699 this.el.dom.style.left = box.x + "px";
29700 this.el.dom.style.top = box.y + "px";
29701 this.updateBody(box.width, box.height);
29703 this.collapsedEl.dom.style.left = box.x + "px";
29704 this.collapsedEl.dom.style.top = box.y + "px";
29705 this.collapsedEl.setSize(box.width, box.height);
29708 this.tabs.autoSizeTabs();
29712 updateBody : function(w, h){
29714 this.el.setWidth(w);
29715 w -= this.el.getBorderWidth("rl");
29716 if(this.config.adjustments){
29717 w += this.config.adjustments[0];
29721 this.el.setHeight(h);
29722 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29723 h -= this.el.getBorderWidth("tb");
29724 if(this.config.adjustments){
29725 h += this.config.adjustments[1];
29727 this.bodyEl.setHeight(h);
29729 h = this.tabs.syncHeight(h);
29732 if(this.panelSize){
29733 w = w !== null ? w : this.panelSize.width;
29734 h = h !== null ? h : this.panelSize.height;
29736 if(this.activePanel){
29737 var el = this.activePanel.getEl();
29738 w = w !== null ? w : el.getWidth();
29739 h = h !== null ? h : el.getHeight();
29740 this.panelSize = {width: w, height: h};
29741 this.activePanel.setSize(w, h);
29743 if(Roo.isIE && this.tabs){
29744 this.tabs.el.repaint();
29749 * Returns the container element for this region.
29750 * @return {Roo.Element}
29752 getEl : function(){
29757 * Hides this region.
29760 if(!this.collapsed){
29761 this.el.dom.style.left = "-2000px";
29764 this.collapsedEl.dom.style.left = "-2000px";
29765 this.collapsedEl.hide();
29767 this.visible = false;
29768 this.fireEvent("visibilitychange", this, false);
29772 * Shows this region if it was previously hidden.
29775 if(!this.collapsed){
29778 this.collapsedEl.show();
29780 this.visible = true;
29781 this.fireEvent("visibilitychange", this, true);
29784 closeClicked : function(){
29785 if(this.activePanel){
29786 this.remove(this.activePanel);
29790 collapseClick : function(e){
29792 e.stopPropagation();
29795 e.stopPropagation();
29801 * Collapses this region.
29802 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29804 collapse : function(skipAnim, skipCheck = false){
29805 if(this.collapsed) {
29809 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29811 this.collapsed = true;
29813 this.split.el.hide();
29815 if(this.config.animate && skipAnim !== true){
29816 this.fireEvent("invalidated", this);
29817 this.animateCollapse();
29819 this.el.setLocation(-20000,-20000);
29821 this.collapsedEl.show();
29822 this.fireEvent("collapsed", this);
29823 this.fireEvent("invalidated", this);
29829 animateCollapse : function(){
29834 * Expands this region if it was previously collapsed.
29835 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29836 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29838 expand : function(e, skipAnim){
29840 e.stopPropagation();
29842 if(!this.collapsed || this.el.hasActiveFx()) {
29846 this.afterSlideIn();
29849 this.collapsed = false;
29850 if(this.config.animate && skipAnim !== true){
29851 this.animateExpand();
29855 this.split.el.show();
29857 this.collapsedEl.setLocation(-2000,-2000);
29858 this.collapsedEl.hide();
29859 this.fireEvent("invalidated", this);
29860 this.fireEvent("expanded", this);
29864 animateExpand : function(){
29868 initTabs : function()
29870 this.bodyEl.setStyle("overflow", "hidden");
29871 var ts = new Roo.TabPanel(
29874 tabPosition: this.bottomTabs ? 'bottom' : 'top',
29875 disableTooltips: this.config.disableTabTips,
29876 toolbar : this.config.toolbar
29879 if(this.config.hideTabs){
29880 ts.stripWrap.setDisplayed(false);
29883 ts.resizeTabs = this.config.resizeTabs === true;
29884 ts.minTabWidth = this.config.minTabWidth || 40;
29885 ts.maxTabWidth = this.config.maxTabWidth || 250;
29886 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29887 ts.monitorResize = false;
29888 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29889 ts.bodyEl.addClass('x-layout-tabs-body');
29890 this.panels.each(this.initPanelAsTab, this);
29893 initPanelAsTab : function(panel){
29894 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29895 this.config.closeOnTab && panel.isClosable());
29896 if(panel.tabTip !== undefined){
29897 ti.setTooltip(panel.tabTip);
29899 ti.on("activate", function(){
29900 this.setActivePanel(panel);
29902 if(this.config.closeOnTab){
29903 ti.on("beforeclose", function(t, e){
29905 this.remove(panel);
29911 updatePanelTitle : function(panel, title){
29912 if(this.activePanel == panel){
29913 this.updateTitle(title);
29916 var ti = this.tabs.getTab(panel.getEl().id);
29918 if(panel.tabTip !== undefined){
29919 ti.setTooltip(panel.tabTip);
29924 updateTitle : function(title){
29925 if(this.titleTextEl && !this.config.title){
29926 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
29930 setActivePanel : function(panel){
29931 panel = this.getPanel(panel);
29932 if(this.activePanel && this.activePanel != panel){
29933 this.activePanel.setActiveState(false);
29935 this.activePanel = panel;
29936 panel.setActiveState(true);
29937 if(this.panelSize){
29938 panel.setSize(this.panelSize.width, this.panelSize.height);
29941 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29943 this.updateTitle(panel.getTitle());
29945 this.fireEvent("invalidated", this);
29947 this.fireEvent("panelactivated", this, panel);
29951 * Shows the specified panel.
29952 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29953 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29955 showPanel : function(panel)
29957 panel = this.getPanel(panel);
29960 var tab = this.tabs.getTab(panel.getEl().id);
29961 if(tab.isHidden()){
29962 this.tabs.unhideTab(tab.id);
29966 this.setActivePanel(panel);
29973 * Get the active panel for this region.
29974 * @return {Roo.ContentPanel} The active panel or null
29976 getActivePanel : function(){
29977 return this.activePanel;
29980 validateVisibility : function(){
29981 if(this.panels.getCount() < 1){
29982 this.updateTitle(" ");
29983 this.closeBtn.hide();
29986 if(!this.isVisible()){
29993 * Adds the passed ContentPanel(s) to this region.
29994 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29995 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29997 add : function(panel){
29998 if(arguments.length > 1){
29999 for(var i = 0, len = arguments.length; i < len; i++) {
30000 this.add(arguments[i]);
30004 if(this.hasPanel(panel)){
30005 this.showPanel(panel);
30008 panel.setRegion(this);
30009 this.panels.add(panel);
30010 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30011 this.bodyEl.dom.appendChild(panel.getEl().dom);
30012 if(panel.background !== true){
30013 this.setActivePanel(panel);
30015 this.fireEvent("paneladded", this, panel);
30021 this.initPanelAsTab(panel);
30023 if(panel.background !== true){
30024 this.tabs.activate(panel.getEl().id);
30026 this.fireEvent("paneladded", this, panel);
30031 * Hides the tab for the specified panel.
30032 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30034 hidePanel : function(panel){
30035 if(this.tabs && (panel = this.getPanel(panel))){
30036 this.tabs.hideTab(panel.getEl().id);
30041 * Unhides the tab for a previously hidden panel.
30042 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30044 unhidePanel : function(panel){
30045 if(this.tabs && (panel = this.getPanel(panel))){
30046 this.tabs.unhideTab(panel.getEl().id);
30050 clearPanels : function(){
30051 while(this.panels.getCount() > 0){
30052 this.remove(this.panels.first());
30057 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30058 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30059 * @param {Boolean} preservePanel Overrides the config preservePanel option
30060 * @return {Roo.ContentPanel} The panel that was removed
30062 remove : function(panel, preservePanel){
30063 panel = this.getPanel(panel);
30068 this.fireEvent("beforeremove", this, panel, e);
30069 if(e.cancel === true){
30072 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30073 var panelId = panel.getId();
30074 this.panels.removeKey(panelId);
30076 document.body.appendChild(panel.getEl().dom);
30079 this.tabs.removeTab(panel.getEl().id);
30080 }else if (!preservePanel){
30081 this.bodyEl.dom.removeChild(panel.getEl().dom);
30083 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30084 var p = this.panels.first();
30085 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30086 tempEl.appendChild(p.getEl().dom);
30087 this.bodyEl.update("");
30088 this.bodyEl.dom.appendChild(p.getEl().dom);
30090 this.updateTitle(p.getTitle());
30092 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30093 this.setActivePanel(p);
30095 panel.setRegion(null);
30096 if(this.activePanel == panel){
30097 this.activePanel = null;
30099 if(this.config.autoDestroy !== false && preservePanel !== true){
30100 try{panel.destroy();}catch(e){}
30102 this.fireEvent("panelremoved", this, panel);
30107 * Returns the TabPanel component used by this region
30108 * @return {Roo.TabPanel}
30110 getTabs : function(){
30114 createTool : function(parentEl, className){
30115 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30116 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30117 btn.addClassOnOver("x-layout-tools-button-over");
30122 * Ext JS Library 1.1.1
30123 * Copyright(c) 2006-2007, Ext JS, LLC.
30125 * Originally Released Under LGPL - original licence link has changed is not relivant.
30128 * <script type="text/javascript">
30134 * @class Roo.SplitLayoutRegion
30135 * @extends Roo.LayoutRegion
30136 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30138 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30139 this.cursor = cursor;
30140 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30143 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30144 splitTip : "Drag to resize.",
30145 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30146 useSplitTips : false,
30148 applyConfig : function(config){
30149 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30152 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30153 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30154 /** The SplitBar for this region
30155 * @type Roo.SplitBar */
30156 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30157 this.split.on("moved", this.onSplitMove, this);
30158 this.split.useShim = config.useShim === true;
30159 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30160 if(this.useSplitTips){
30161 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30163 if(config.collapsible){
30164 this.split.el.on("dblclick", this.collapse, this);
30167 if(typeof config.minSize != "undefined"){
30168 this.split.minSize = config.minSize;
30170 if(typeof config.maxSize != "undefined"){
30171 this.split.maxSize = config.maxSize;
30173 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30174 this.hideSplitter();
30179 getHMaxSize : function(){
30180 var cmax = this.config.maxSize || 10000;
30181 var center = this.mgr.getRegion("center");
30182 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30185 getVMaxSize : function(){
30186 var cmax = this.config.maxSize || 10000;
30187 var center = this.mgr.getRegion("center");
30188 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30191 onSplitMove : function(split, newSize){
30192 this.fireEvent("resized", this, newSize);
30196 * Returns the {@link Roo.SplitBar} for this region.
30197 * @return {Roo.SplitBar}
30199 getSplitBar : function(){
30204 this.hideSplitter();
30205 Roo.SplitLayoutRegion.superclass.hide.call(this);
30208 hideSplitter : function(){
30210 this.split.el.setLocation(-2000,-2000);
30211 this.split.el.hide();
30217 this.split.el.show();
30219 Roo.SplitLayoutRegion.superclass.show.call(this);
30222 beforeSlide: function(){
30223 if(Roo.isGecko){// firefox overflow auto bug workaround
30224 this.bodyEl.clip();
30226 this.tabs.bodyEl.clip();
30228 if(this.activePanel){
30229 this.activePanel.getEl().clip();
30231 if(this.activePanel.beforeSlide){
30232 this.activePanel.beforeSlide();
30238 afterSlide : function(){
30239 if(Roo.isGecko){// firefox overflow auto bug workaround
30240 this.bodyEl.unclip();
30242 this.tabs.bodyEl.unclip();
30244 if(this.activePanel){
30245 this.activePanel.getEl().unclip();
30246 if(this.activePanel.afterSlide){
30247 this.activePanel.afterSlide();
30253 initAutoHide : function(){
30254 if(this.autoHide !== false){
30255 if(!this.autoHideHd){
30256 var st = new Roo.util.DelayedTask(this.slideIn, this);
30257 this.autoHideHd = {
30258 "mouseout": function(e){
30259 if(!e.within(this.el, true)){
30263 "mouseover" : function(e){
30269 this.el.on(this.autoHideHd);
30273 clearAutoHide : function(){
30274 if(this.autoHide !== false){
30275 this.el.un("mouseout", this.autoHideHd.mouseout);
30276 this.el.un("mouseover", this.autoHideHd.mouseover);
30280 clearMonitor : function(){
30281 Roo.get(document).un("click", this.slideInIf, this);
30284 // these names are backwards but not changed for compat
30285 slideOut : function(){
30286 if(this.isSlid || this.el.hasActiveFx()){
30289 this.isSlid = true;
30290 if(this.collapseBtn){
30291 this.collapseBtn.hide();
30293 this.closeBtnState = this.closeBtn.getStyle('display');
30294 this.closeBtn.hide();
30296 this.stickBtn.show();
30299 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30300 this.beforeSlide();
30301 this.el.setStyle("z-index", 10001);
30302 this.el.slideIn(this.getSlideAnchor(), {
30303 callback: function(){
30305 this.initAutoHide();
30306 Roo.get(document).on("click", this.slideInIf, this);
30307 this.fireEvent("slideshow", this);
30314 afterSlideIn : function(){
30315 this.clearAutoHide();
30316 this.isSlid = false;
30317 this.clearMonitor();
30318 this.el.setStyle("z-index", "");
30319 if(this.collapseBtn){
30320 this.collapseBtn.show();
30322 this.closeBtn.setStyle('display', this.closeBtnState);
30324 this.stickBtn.hide();
30326 this.fireEvent("slidehide", this);
30329 slideIn : function(cb){
30330 if(!this.isSlid || this.el.hasActiveFx()){
30334 this.isSlid = false;
30335 this.beforeSlide();
30336 this.el.slideOut(this.getSlideAnchor(), {
30337 callback: function(){
30338 this.el.setLeftTop(-10000, -10000);
30340 this.afterSlideIn();
30348 slideInIf : function(e){
30349 if(!e.within(this.el)){
30354 animateCollapse : function(){
30355 this.beforeSlide();
30356 this.el.setStyle("z-index", 20000);
30357 var anchor = this.getSlideAnchor();
30358 this.el.slideOut(anchor, {
30359 callback : function(){
30360 this.el.setStyle("z-index", "");
30361 this.collapsedEl.slideIn(anchor, {duration:.3});
30363 this.el.setLocation(-10000,-10000);
30365 this.fireEvent("collapsed", this);
30372 animateExpand : function(){
30373 this.beforeSlide();
30374 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30375 this.el.setStyle("z-index", 20000);
30376 this.collapsedEl.hide({
30379 this.el.slideIn(this.getSlideAnchor(), {
30380 callback : function(){
30381 this.el.setStyle("z-index", "");
30384 this.split.el.show();
30386 this.fireEvent("invalidated", this);
30387 this.fireEvent("expanded", this);
30415 getAnchor : function(){
30416 return this.anchors[this.position];
30419 getCollapseAnchor : function(){
30420 return this.canchors[this.position];
30423 getSlideAnchor : function(){
30424 return this.sanchors[this.position];
30427 getAlignAdj : function(){
30428 var cm = this.cmargins;
30429 switch(this.position){
30445 getExpandAdj : function(){
30446 var c = this.collapsedEl, cm = this.cmargins;
30447 switch(this.position){
30449 return [-(cm.right+c.getWidth()+cm.left), 0];
30452 return [cm.right+c.getWidth()+cm.left, 0];
30455 return [0, -(cm.top+cm.bottom+c.getHeight())];
30458 return [0, cm.top+cm.bottom+c.getHeight()];
30464 * Ext JS Library 1.1.1
30465 * Copyright(c) 2006-2007, Ext JS, LLC.
30467 * Originally Released Under LGPL - original licence link has changed is not relivant.
30470 * <script type="text/javascript">
30473 * These classes are private internal classes
30475 Roo.CenterLayoutRegion = function(mgr, config){
30476 Roo.LayoutRegion.call(this, mgr, config, "center");
30477 this.visible = true;
30478 this.minWidth = config.minWidth || 20;
30479 this.minHeight = config.minHeight || 20;
30482 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30484 // center panel can't be hidden
30488 // center panel can't be hidden
30491 getMinWidth: function(){
30492 return this.minWidth;
30495 getMinHeight: function(){
30496 return this.minHeight;
30501 Roo.NorthLayoutRegion = function(mgr, config){
30502 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30504 this.split.placement = Roo.SplitBar.TOP;
30505 this.split.orientation = Roo.SplitBar.VERTICAL;
30506 this.split.el.addClass("x-layout-split-v");
30508 var size = config.initialSize || config.height;
30509 if(typeof size != "undefined"){
30510 this.el.setHeight(size);
30513 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30514 orientation: Roo.SplitBar.VERTICAL,
30515 getBox : function(){
30516 if(this.collapsed){
30517 return this.collapsedEl.getBox();
30519 var box = this.el.getBox();
30521 box.height += this.split.el.getHeight();
30526 updateBox : function(box){
30527 if(this.split && !this.collapsed){
30528 box.height -= this.split.el.getHeight();
30529 this.split.el.setLeft(box.x);
30530 this.split.el.setTop(box.y+box.height);
30531 this.split.el.setWidth(box.width);
30533 if(this.collapsed){
30534 this.updateBody(box.width, null);
30536 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30540 Roo.SouthLayoutRegion = function(mgr, config){
30541 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30543 this.split.placement = Roo.SplitBar.BOTTOM;
30544 this.split.orientation = Roo.SplitBar.VERTICAL;
30545 this.split.el.addClass("x-layout-split-v");
30547 var size = config.initialSize || config.height;
30548 if(typeof size != "undefined"){
30549 this.el.setHeight(size);
30552 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30553 orientation: Roo.SplitBar.VERTICAL,
30554 getBox : function(){
30555 if(this.collapsed){
30556 return this.collapsedEl.getBox();
30558 var box = this.el.getBox();
30560 var sh = this.split.el.getHeight();
30567 updateBox : function(box){
30568 if(this.split && !this.collapsed){
30569 var sh = this.split.el.getHeight();
30572 this.split.el.setLeft(box.x);
30573 this.split.el.setTop(box.y-sh);
30574 this.split.el.setWidth(box.width);
30576 if(this.collapsed){
30577 this.updateBody(box.width, null);
30579 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30583 Roo.EastLayoutRegion = function(mgr, config){
30584 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30586 this.split.placement = Roo.SplitBar.RIGHT;
30587 this.split.orientation = Roo.SplitBar.HORIZONTAL;
30588 this.split.el.addClass("x-layout-split-h");
30590 var size = config.initialSize || config.width;
30591 if(typeof size != "undefined"){
30592 this.el.setWidth(size);
30595 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30596 orientation: Roo.SplitBar.HORIZONTAL,
30597 getBox : function(){
30598 if(this.collapsed){
30599 return this.collapsedEl.getBox();
30601 var box = this.el.getBox();
30603 var sw = this.split.el.getWidth();
30610 updateBox : function(box){
30611 if(this.split && !this.collapsed){
30612 var sw = this.split.el.getWidth();
30614 this.split.el.setLeft(box.x);
30615 this.split.el.setTop(box.y);
30616 this.split.el.setHeight(box.height);
30619 if(this.collapsed){
30620 this.updateBody(null, box.height);
30622 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30626 Roo.WestLayoutRegion = function(mgr, config){
30627 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30629 this.split.placement = Roo.SplitBar.LEFT;
30630 this.split.orientation = Roo.SplitBar.HORIZONTAL;
30631 this.split.el.addClass("x-layout-split-h");
30633 var size = config.initialSize || config.width;
30634 if(typeof size != "undefined"){
30635 this.el.setWidth(size);
30638 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30639 orientation: Roo.SplitBar.HORIZONTAL,
30640 getBox : function(){
30641 if(this.collapsed){
30642 return this.collapsedEl.getBox();
30644 var box = this.el.getBox();
30646 box.width += this.split.el.getWidth();
30651 updateBox : function(box){
30652 if(this.split && !this.collapsed){
30653 var sw = this.split.el.getWidth();
30655 this.split.el.setLeft(box.x+box.width);
30656 this.split.el.setTop(box.y);
30657 this.split.el.setHeight(box.height);
30659 if(this.collapsed){
30660 this.updateBody(null, box.height);
30662 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30667 * Ext JS Library 1.1.1
30668 * Copyright(c) 2006-2007, Ext JS, LLC.
30670 * Originally Released Under LGPL - original licence link has changed is not relivant.
30673 * <script type="text/javascript">
30678 * Private internal class for reading and applying state
30680 Roo.LayoutStateManager = function(layout){
30681 // default empty state
30690 Roo.LayoutStateManager.prototype = {
30691 init : function(layout, provider){
30692 this.provider = provider;
30693 var state = provider.get(layout.id+"-layout-state");
30695 var wasUpdating = layout.isUpdating();
30697 layout.beginUpdate();
30699 for(var key in state){
30700 if(typeof state[key] != "function"){
30701 var rstate = state[key];
30702 var r = layout.getRegion(key);
30705 r.resizeTo(rstate.size);
30707 if(rstate.collapsed == true){
30710 r.expand(null, true);
30716 layout.endUpdate();
30718 this.state = state;
30720 this.layout = layout;
30721 layout.on("regionresized", this.onRegionResized, this);
30722 layout.on("regioncollapsed", this.onRegionCollapsed, this);
30723 layout.on("regionexpanded", this.onRegionExpanded, this);
30726 storeState : function(){
30727 this.provider.set(this.layout.id+"-layout-state", this.state);
30730 onRegionResized : function(region, newSize){
30731 this.state[region.getPosition()].size = newSize;
30735 onRegionCollapsed : function(region){
30736 this.state[region.getPosition()].collapsed = true;
30740 onRegionExpanded : function(region){
30741 this.state[region.getPosition()].collapsed = false;
30746 * Ext JS Library 1.1.1
30747 * Copyright(c) 2006-2007, Ext JS, LLC.
30749 * Originally Released Under LGPL - original licence link has changed is not relivant.
30752 * <script type="text/javascript">
30755 * @class Roo.ContentPanel
30756 * @extends Roo.util.Observable
30757 * A basic ContentPanel element.
30758 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
30759 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
30760 * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
30761 * @cfg {Boolean} closable True if the panel can be closed/removed
30762 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30763 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30764 * @cfg {Toolbar} toolbar A toolbar for this panel
30765 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30766 * @cfg {String} title The title for this panel
30767 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30768 * @cfg {String} url Calls {@link #setUrl} with this value
30769 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30770 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30771 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30772 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
30775 * Create a new ContentPanel.
30776 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30777 * @param {String/Object} config A string to set only the title or a config object
30778 * @param {String} content (optional) Set the HTML content for this panel
30779 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30781 Roo.ContentPanel = function(el, config, content){
30785 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30789 if (config && config.parentLayout) {
30790 el = config.parentLayout.el.createChild();
30793 if(el.autoCreate){ // xtype is available if this is called from factory
30797 this.el = Roo.get(el);
30798 if(!this.el && config && config.autoCreate){
30799 if(typeof config.autoCreate == "object"){
30800 if(!config.autoCreate.id){
30801 config.autoCreate.id = config.id||el;
30803 this.el = Roo.DomHelper.append(document.body,
30804 config.autoCreate, true);
30806 this.el = Roo.DomHelper.append(document.body,
30807 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30810 this.closable = false;
30811 this.loaded = false;
30812 this.active = false;
30813 if(typeof config == "string"){
30814 this.title = config;
30816 Roo.apply(this, config);
30819 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30820 this.wrapEl = this.el.wrap();
30821 this.toolbar.container = this.el.insertSibling(false, 'before');
30822 this.toolbar = new Roo.Toolbar(this.toolbar);
30825 // xtype created footer. - not sure if will work as we normally have to render first..
30826 if (this.footer && !this.footer.el && this.footer.xtype) {
30827 if (!this.wrapEl) {
30828 this.wrapEl = this.el.wrap();
30831 this.footer.container = this.wrapEl.createChild();
30833 this.footer = Roo.factory(this.footer, Roo);
30838 this.resizeEl = Roo.get(this.resizeEl, true);
30840 this.resizeEl = this.el;
30842 // handle view.xtype
30850 * Fires when this panel is activated.
30851 * @param {Roo.ContentPanel} this
30855 * @event deactivate
30856 * Fires when this panel is activated.
30857 * @param {Roo.ContentPanel} this
30859 "deactivate" : true,
30863 * Fires when this panel is resized if fitToFrame is true.
30864 * @param {Roo.ContentPanel} this
30865 * @param {Number} width The width after any component adjustments
30866 * @param {Number} height The height after any component adjustments
30872 * Fires when this tab is created
30873 * @param {Roo.ContentPanel} this
30884 if(this.autoScroll){
30885 this.resizeEl.setStyle("overflow", "auto");
30887 // fix randome scrolling
30888 this.el.on('scroll', function() {
30889 Roo.log('fix random scolling');
30890 this.scrollTo('top',0);
30893 content = content || this.content;
30895 this.setContent(content);
30897 if(config && config.url){
30898 this.setUrl(this.url, this.params, this.loadOnce);
30903 Roo.ContentPanel.superclass.constructor.call(this);
30905 if (this.view && typeof(this.view.xtype) != 'undefined') {
30906 this.view.el = this.el.appendChild(document.createElement("div"));
30907 this.view = Roo.factory(this.view);
30908 this.view.render && this.view.render(false, '');
30912 this.fireEvent('render', this);
30915 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30917 setRegion : function(region){
30918 this.region = region;
30920 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30922 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30927 * Returns the toolbar for this Panel if one was configured.
30928 * @return {Roo.Toolbar}
30930 getToolbar : function(){
30931 return this.toolbar;
30934 setActiveState : function(active){
30935 this.active = active;
30937 this.fireEvent("deactivate", this);
30939 this.fireEvent("activate", this);
30943 * Updates this panel's element
30944 * @param {String} content The new content
30945 * @param {Boolean} loadScripts (optional) true to look for and process scripts
30947 setContent : function(content, loadScripts){
30948 this.el.update(content, loadScripts);
30951 ignoreResize : function(w, h){
30952 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30955 this.lastSize = {width: w, height: h};
30960 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30961 * @return {Roo.UpdateManager} The UpdateManager
30963 getUpdateManager : function(){
30964 return this.el.getUpdateManager();
30967 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30968 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
30971 url: "your-url.php",
30972 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30973 callback: yourFunction,
30974 scope: yourObject, //(optional scope)
30977 text: "Loading...",
30982 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30983 * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
30984 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
30985 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30986 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
30987 * @return {Roo.ContentPanel} this
30990 var um = this.el.getUpdateManager();
30991 um.update.apply(um, arguments);
30997 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
30998 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30999 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
31000 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
31001 * @return {Roo.UpdateManager} The UpdateManager
31003 setUrl : function(url, params, loadOnce){
31004 if(this.refreshDelegate){
31005 this.removeListener("activate", this.refreshDelegate);
31007 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31008 this.on("activate", this.refreshDelegate);
31009 return this.el.getUpdateManager();
31012 _handleRefresh : function(url, params, loadOnce){
31013 if(!loadOnce || !this.loaded){
31014 var updater = this.el.getUpdateManager();
31015 updater.update(url, params, this._setLoaded.createDelegate(this));
31019 _setLoaded : function(){
31020 this.loaded = true;
31024 * Returns this panel's id
31027 getId : function(){
31032 * Returns this panel's element - used by regiosn to add.
31033 * @return {Roo.Element}
31035 getEl : function(){
31036 return this.wrapEl || this.el;
31039 adjustForComponents : function(width, height)
31041 //Roo.log('adjustForComponents ');
31042 if(this.resizeEl != this.el){
31043 width -= this.el.getFrameWidth('lr');
31044 height -= this.el.getFrameWidth('tb');
31047 var te = this.toolbar.getEl();
31048 height -= te.getHeight();
31049 te.setWidth(width);
31052 var te = this.footer.getEl();
31053 Roo.log("footer:" + te.getHeight());
31055 height -= te.getHeight();
31056 te.setWidth(width);
31060 if(this.adjustments){
31061 width += this.adjustments[0];
31062 height += this.adjustments[1];
31064 return {"width": width, "height": height};
31067 setSize : function(width, height){
31068 if(this.fitToFrame && !this.ignoreResize(width, height)){
31069 if(this.fitContainer && this.resizeEl != this.el){
31070 this.el.setSize(width, height);
31072 var size = this.adjustForComponents(width, height);
31073 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31074 this.fireEvent('resize', this, size.width, size.height);
31079 * Returns this panel's title
31082 getTitle : function(){
31087 * Set this panel's title
31088 * @param {String} title
31090 setTitle : function(title){
31091 this.title = title;
31093 this.region.updatePanelTitle(this, title);
31098 * Returns true is this panel was configured to be closable
31099 * @return {Boolean}
31101 isClosable : function(){
31102 return this.closable;
31105 beforeSlide : function(){
31107 this.resizeEl.clip();
31110 afterSlide : function(){
31112 this.resizeEl.unclip();
31116 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31117 * Will fail silently if the {@link #setUrl} method has not been called.
31118 * This does not activate the panel, just updates its content.
31120 refresh : function(){
31121 if(this.refreshDelegate){
31122 this.loaded = false;
31123 this.refreshDelegate();
31128 * Destroys this panel
31130 destroy : function(){
31131 this.el.removeAllListeners();
31132 var tempEl = document.createElement("span");
31133 tempEl.appendChild(this.el.dom);
31134 tempEl.innerHTML = "";
31140 * form - if the content panel contains a form - this is a reference to it.
31141 * @type {Roo.form.Form}
31145 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31146 * This contains a reference to it.
31152 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31162 * @param {Object} cfg Xtype definition of item to add.
31165 addxtype : function(cfg) {
31167 if (cfg.xtype.match(/^Form$/)) {
31170 //if (this.footer) {
31171 // el = this.footer.container.insertSibling(false, 'before');
31173 el = this.el.createChild();
31176 this.form = new Roo.form.Form(cfg);
31179 if ( this.form.allItems.length) {
31180 this.form.render(el.dom);
31184 // should only have one of theses..
31185 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31186 // views.. should not be just added - used named prop 'view''
31188 cfg.el = this.el.appendChild(document.createElement("div"));
31191 var ret = new Roo.factory(cfg);
31193 ret.render && ret.render(false, ''); // render blank..
31202 * @class Roo.GridPanel
31203 * @extends Roo.ContentPanel
31205 * Create a new GridPanel.
31206 * @param {Roo.grid.Grid} grid The grid for this panel
31207 * @param {String/Object} config A string to set only the panel's title, or a config object
31209 Roo.GridPanel = function(grid, config){
31212 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31213 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31215 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31217 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31220 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31222 // xtype created footer. - not sure if will work as we normally have to render first..
31223 if (this.footer && !this.footer.el && this.footer.xtype) {
31225 this.footer.container = this.grid.getView().getFooterPanel(true);
31226 this.footer.dataSource = this.grid.dataSource;
31227 this.footer = Roo.factory(this.footer, Roo);
31231 grid.monitorWindowResize = false; // turn off autosizing
31232 grid.autoHeight = false;
31233 grid.autoWidth = false;
31235 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31238 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31239 getId : function(){
31240 return this.grid.id;
31244 * Returns the grid for this panel
31245 * @return {Roo.grid.Grid}
31247 getGrid : function(){
31251 setSize : function(width, height){
31252 if(!this.ignoreResize(width, height)){
31253 var grid = this.grid;
31254 var size = this.adjustForComponents(width, height);
31255 grid.getGridEl().setSize(size.width, size.height);
31260 beforeSlide : function(){
31261 this.grid.getView().scroller.clip();
31264 afterSlide : function(){
31265 this.grid.getView().scroller.unclip();
31268 destroy : function(){
31269 this.grid.destroy();
31271 Roo.GridPanel.superclass.destroy.call(this);
31277 * @class Roo.NestedLayoutPanel
31278 * @extends Roo.ContentPanel
31280 * Create a new NestedLayoutPanel.
31283 * @param {Roo.BorderLayout} layout The layout for this panel
31284 * @param {String/Object} config A string to set only the title or a config object
31286 Roo.NestedLayoutPanel = function(layout, config)
31288 // construct with only one argument..
31289 /* FIXME - implement nicer consturctors
31290 if (layout.layout) {
31292 layout = config.layout;
31293 delete config.layout;
31295 if (layout.xtype && !layout.getEl) {
31296 // then layout needs constructing..
31297 layout = Roo.factory(layout, Roo);
31302 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31304 layout.monitorWindowResize = false; // turn off autosizing
31305 this.layout = layout;
31306 this.layout.getEl().addClass("x-layout-nested-layout");
31313 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31315 setSize : function(width, height){
31316 if(!this.ignoreResize(width, height)){
31317 var size = this.adjustForComponents(width, height);
31318 var el = this.layout.getEl();
31319 el.setSize(size.width, size.height);
31320 var touch = el.dom.offsetWidth;
31321 this.layout.layout();
31322 // ie requires a double layout on the first pass
31323 if(Roo.isIE && !this.initialized){
31324 this.initialized = true;
31325 this.layout.layout();
31330 // activate all subpanels if not currently active..
31332 setActiveState : function(active){
31333 this.active = active;
31335 this.fireEvent("deactivate", this);
31339 this.fireEvent("activate", this);
31340 // not sure if this should happen before or after..
31341 if (!this.layout) {
31342 return; // should not happen..
31345 for (var r in this.layout.regions) {
31346 reg = this.layout.getRegion(r);
31347 if (reg.getActivePanel()) {
31348 //reg.showPanel(reg.getActivePanel()); // force it to activate..
31349 reg.setActivePanel(reg.getActivePanel());
31352 if (!reg.panels.length) {
31355 reg.showPanel(reg.getPanel(0));
31364 * Returns the nested BorderLayout for this panel
31365 * @return {Roo.BorderLayout}
31367 getLayout : function(){
31368 return this.layout;
31372 * Adds a xtype elements to the layout of the nested panel
31376 xtype : 'ContentPanel',
31383 xtype : 'NestedLayoutPanel',
31389 items : [ ... list of content panels or nested layout panels.. ]
31393 * @param {Object} cfg Xtype definition of item to add.
31395 addxtype : function(cfg) {
31396 return this.layout.addxtype(cfg);
31401 Roo.ScrollPanel = function(el, config, content){
31402 config = config || {};
31403 config.fitToFrame = true;
31404 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31406 this.el.dom.style.overflow = "hidden";
31407 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31408 this.el.removeClass("x-layout-inactive-content");
31409 this.el.on("mousewheel", this.onWheel, this);
31411 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
31412 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
31413 up.unselectable(); down.unselectable();
31414 up.on("click", this.scrollUp, this);
31415 down.on("click", this.scrollDown, this);
31416 up.addClassOnOver("x-scroller-btn-over");
31417 down.addClassOnOver("x-scroller-btn-over");
31418 up.addClassOnClick("x-scroller-btn-click");
31419 down.addClassOnClick("x-scroller-btn-click");
31420 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31422 this.resizeEl = this.el;
31423 this.el = wrap; this.up = up; this.down = down;
31426 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31428 wheelIncrement : 5,
31429 scrollUp : function(){
31430 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31433 scrollDown : function(){
31434 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31437 afterScroll : function(){
31438 var el = this.resizeEl;
31439 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31440 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31441 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31444 setSize : function(){
31445 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31446 this.afterScroll();
31449 onWheel : function(e){
31450 var d = e.getWheelDelta();
31451 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31452 this.afterScroll();
31456 setContent : function(content, loadScripts){
31457 this.resizeEl.update(content, loadScripts);
31471 * @class Roo.TreePanel
31472 * @extends Roo.ContentPanel
31474 * Create a new TreePanel. - defaults to fit/scoll contents.
31475 * @param {String/Object} config A string to set only the panel's title, or a config object
31476 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31478 Roo.TreePanel = function(config){
31479 var el = config.el;
31480 var tree = config.tree;
31481 delete config.tree;
31482 delete config.el; // hopefull!
31484 // wrapper for IE7 strict & safari scroll issue
31486 var treeEl = el.createChild();
31487 config.resizeEl = treeEl;
31491 Roo.TreePanel.superclass.constructor.call(this, el, config);
31494 this.tree = new Roo.tree.TreePanel(treeEl , tree);
31495 //console.log(tree);
31496 this.on('activate', function()
31498 if (this.tree.rendered) {
31501 //console.log('render tree');
31502 this.tree.render();
31504 // this should not be needed.. - it's actually the 'el' that resizes?
31505 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31507 //this.on('resize', function (cp, w, h) {
31508 // this.tree.innerCt.setWidth(w);
31509 // this.tree.innerCt.setHeight(h);
31510 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
31517 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
31534 * Ext JS Library 1.1.1
31535 * Copyright(c) 2006-2007, Ext JS, LLC.
31537 * Originally Released Under LGPL - original licence link has changed is not relivant.
31540 * <script type="text/javascript">
31545 * @class Roo.ReaderLayout
31546 * @extends Roo.BorderLayout
31547 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
31548 * center region containing two nested regions (a top one for a list view and one for item preview below),
31549 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31550 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31551 * expedites the setup of the overall layout and regions for this common application style.
31554 var reader = new Roo.ReaderLayout();
31555 var CP = Roo.ContentPanel; // shortcut for adding
31557 reader.beginUpdate();
31558 reader.add("north", new CP("north", "North"));
31559 reader.add("west", new CP("west", {title: "West"}));
31560 reader.add("east", new CP("east", {title: "East"}));
31562 reader.regions.listView.add(new CP("listView", "List"));
31563 reader.regions.preview.add(new CP("preview", "Preview"));
31564 reader.endUpdate();
31567 * Create a new ReaderLayout
31568 * @param {Object} config Configuration options
31569 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31570 * document.body if omitted)
31572 Roo.ReaderLayout = function(config, renderTo){
31573 var c = config || {size:{}};
31574 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31575 north: c.north !== false ? Roo.apply({
31579 }, c.north) : false,
31580 west: c.west !== false ? Roo.apply({
31588 margins:{left:5,right:0,bottom:5,top:5},
31589 cmargins:{left:5,right:5,bottom:5,top:5}
31590 }, c.west) : false,
31591 east: c.east !== false ? Roo.apply({
31599 margins:{left:0,right:5,bottom:5,top:5},
31600 cmargins:{left:5,right:5,bottom:5,top:5}
31601 }, c.east) : false,
31602 center: Roo.apply({
31603 tabPosition: 'top',
31607 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31611 this.el.addClass('x-reader');
31613 this.beginUpdate();
31615 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31616 south: c.preview !== false ? Roo.apply({
31623 cmargins:{top:5,left:0, right:0, bottom:0}
31624 }, c.preview) : false,
31625 center: Roo.apply({
31631 this.add('center', new Roo.NestedLayoutPanel(inner,
31632 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31636 this.regions.preview = inner.getRegion('south');
31637 this.regions.listView = inner.getRegion('center');
31640 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31642 * Ext JS Library 1.1.1
31643 * Copyright(c) 2006-2007, Ext JS, LLC.
31645 * Originally Released Under LGPL - original licence link has changed is not relivant.
31648 * <script type="text/javascript">
31652 * @class Roo.grid.Grid
31653 * @extends Roo.util.Observable
31654 * This class represents the primary interface of a component based grid control.
31655 * <br><br>Usage:<pre><code>
31656 var grid = new Roo.grid.Grid("my-container-id", {
31659 selModel: mySelectionModel,
31660 autoSizeColumns: true,
31661 monitorWindowResize: false,
31662 trackMouseOver: true
31667 * <b>Common Problems:</b><br/>
31668 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31669 * element will correct this<br/>
31670 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31671 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31672 * are unpredictable.<br/>
31673 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31674 * grid to calculate dimensions/offsets.<br/>
31676 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31677 * The container MUST have some type of size defined for the grid to fill. The container will be
31678 * automatically set to position relative if it isn't already.
31679 * @param {Object} config A config object that sets properties on this grid.
31681 Roo.grid.Grid = function(container, config){
31682 // initialize the container
31683 this.container = Roo.get(container);
31684 this.container.update("");
31685 this.container.setStyle("overflow", "hidden");
31686 this.container.addClass('x-grid-container');
31688 this.id = this.container.id;
31690 Roo.apply(this, config);
31691 // check and correct shorthanded configs
31693 this.dataSource = this.ds;
31697 this.colModel = this.cm;
31701 this.selModel = this.sm;
31705 if (this.selModel) {
31706 this.selModel = Roo.factory(this.selModel, Roo.grid);
31707 this.sm = this.selModel;
31708 this.sm.xmodule = this.xmodule || false;
31710 if (typeof(this.colModel.config) == 'undefined') {
31711 this.colModel = new Roo.grid.ColumnModel(this.colModel);
31712 this.cm = this.colModel;
31713 this.cm.xmodule = this.xmodule || false;
31715 if (this.dataSource) {
31716 this.dataSource= Roo.factory(this.dataSource, Roo.data);
31717 this.ds = this.dataSource;
31718 this.ds.xmodule = this.xmodule || false;
31725 this.container.setWidth(this.width);
31729 this.container.setHeight(this.height);
31736 * The raw click event for the entire grid.
31737 * @param {Roo.EventObject} e
31742 * The raw dblclick event for the entire grid.
31743 * @param {Roo.EventObject} e
31747 * @event contextmenu
31748 * The raw contextmenu event for the entire grid.
31749 * @param {Roo.EventObject} e
31751 "contextmenu" : true,
31754 * The raw mousedown event for the entire grid.
31755 * @param {Roo.EventObject} e
31757 "mousedown" : true,
31760 * The raw mouseup event for the entire grid.
31761 * @param {Roo.EventObject} e
31766 * The raw mouseover event for the entire grid.
31767 * @param {Roo.EventObject} e
31769 "mouseover" : true,
31772 * The raw mouseout event for the entire grid.
31773 * @param {Roo.EventObject} e
31778 * The raw keypress event for the entire grid.
31779 * @param {Roo.EventObject} e
31784 * The raw keydown event for the entire grid.
31785 * @param {Roo.EventObject} e
31793 * Fires when a cell is clicked
31794 * @param {Grid} this
31795 * @param {Number} rowIndex
31796 * @param {Number} columnIndex
31797 * @param {Roo.EventObject} e
31799 "cellclick" : true,
31801 * @event celldblclick
31802 * Fires when a cell is double clicked
31803 * @param {Grid} this
31804 * @param {Number} rowIndex
31805 * @param {Number} columnIndex
31806 * @param {Roo.EventObject} e
31808 "celldblclick" : true,
31811 * Fires when a row is clicked
31812 * @param {Grid} this
31813 * @param {Number} rowIndex
31814 * @param {Roo.EventObject} e
31818 * @event rowdblclick
31819 * Fires when a row is double clicked
31820 * @param {Grid} this
31821 * @param {Number} rowIndex
31822 * @param {Roo.EventObject} e
31824 "rowdblclick" : true,
31826 * @event headerclick
31827 * Fires when a header is clicked
31828 * @param {Grid} this
31829 * @param {Number} columnIndex
31830 * @param {Roo.EventObject} e
31832 "headerclick" : true,
31834 * @event headerdblclick
31835 * Fires when a header cell is double clicked
31836 * @param {Grid} this
31837 * @param {Number} columnIndex
31838 * @param {Roo.EventObject} e
31840 "headerdblclick" : true,
31842 * @event rowcontextmenu
31843 * Fires when a row is right clicked
31844 * @param {Grid} this
31845 * @param {Number} rowIndex
31846 * @param {Roo.EventObject} e
31848 "rowcontextmenu" : true,
31850 * @event cellcontextmenu
31851 * Fires when a cell is right clicked
31852 * @param {Grid} this
31853 * @param {Number} rowIndex
31854 * @param {Number} cellIndex
31855 * @param {Roo.EventObject} e
31857 "cellcontextmenu" : true,
31859 * @event headercontextmenu
31860 * Fires when a header is right clicked
31861 * @param {Grid} this
31862 * @param {Number} columnIndex
31863 * @param {Roo.EventObject} e
31865 "headercontextmenu" : true,
31867 * @event bodyscroll
31868 * Fires when the body element is scrolled
31869 * @param {Number} scrollLeft
31870 * @param {Number} scrollTop
31872 "bodyscroll" : true,
31874 * @event columnresize
31875 * Fires when the user resizes a column
31876 * @param {Number} columnIndex
31877 * @param {Number} newSize
31879 "columnresize" : true,
31881 * @event columnmove
31882 * Fires when the user moves a column
31883 * @param {Number} oldIndex
31884 * @param {Number} newIndex
31886 "columnmove" : true,
31889 * Fires when row(s) start being dragged
31890 * @param {Grid} this
31891 * @param {Roo.GridDD} dd The drag drop object
31892 * @param {event} e The raw browser event
31894 "startdrag" : true,
31897 * Fires when a drag operation is complete
31898 * @param {Grid} this
31899 * @param {Roo.GridDD} dd The drag drop object
31900 * @param {event} e The raw browser event
31905 * Fires when dragged row(s) are dropped on a valid DD target
31906 * @param {Grid} this
31907 * @param {Roo.GridDD} dd The drag drop object
31908 * @param {String} targetId The target drag drop object
31909 * @param {event} e The raw browser event
31914 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31915 * @param {Grid} this
31916 * @param {Roo.GridDD} dd The drag drop object
31917 * @param {String} targetId The target drag drop object
31918 * @param {event} e The raw browser event
31923 * Fires when the dragged row(s) first cross another DD target while being dragged
31924 * @param {Grid} this
31925 * @param {Roo.GridDD} dd The drag drop object
31926 * @param {String} targetId The target drag drop object
31927 * @param {event} e The raw browser event
31929 "dragenter" : true,
31932 * Fires when the dragged row(s) leave another DD target while being dragged
31933 * @param {Grid} this
31934 * @param {Roo.GridDD} dd The drag drop object
31935 * @param {String} targetId The target drag drop object
31936 * @param {event} e The raw browser event
31941 * Fires when a row is rendered, so you can change add a style to it.
31942 * @param {GridView} gridview The grid view
31943 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
31949 * Fires when the grid is rendered
31950 * @param {Grid} grid
31955 Roo.grid.Grid.superclass.constructor.call(this);
31957 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31960 * @cfg {String} ddGroup - drag drop group.
31964 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31966 minColumnWidth : 25,
31969 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31970 * <b>on initial render.</b> It is more efficient to explicitly size the columns
31971 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
31973 autoSizeColumns : false,
31976 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31978 autoSizeHeaders : true,
31981 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31983 monitorWindowResize : true,
31986 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31987 * rows measured to get a columns size. Default is 0 (all rows).
31989 maxRowsToMeasure : 0,
31992 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31994 trackMouseOver : true,
31997 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32001 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32003 enableDragDrop : false,
32006 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32008 enableColumnMove : true,
32011 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32013 enableColumnHide : true,
32016 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32018 enableRowHeightSync : false,
32021 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32026 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32028 autoHeight : false,
32031 * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
32033 autoExpandColumn : false,
32036 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32039 autoExpandMin : 50,
32042 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32044 autoExpandMax : 1000,
32047 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32052 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32056 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32066 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32067 * of a fixed width. Default is false.
32070 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32073 * Called once after all setup has been completed and the grid is ready to be rendered.
32074 * @return {Roo.grid.Grid} this
32076 render : function()
32078 var c = this.container;
32079 // try to detect autoHeight/width mode
32080 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32081 this.autoHeight = true;
32083 var view = this.getView();
32086 c.on("click", this.onClick, this);
32087 c.on("dblclick", this.onDblClick, this);
32088 c.on("contextmenu", this.onContextMenu, this);
32089 c.on("keydown", this.onKeyDown, this);
32091 c.on("touchstart", this.onTouchStart, this);
32094 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32096 this.getSelectionModel().init(this);
32101 this.loadMask = new Roo.LoadMask(this.container,
32102 Roo.apply({store:this.dataSource}, this.loadMask));
32106 if (this.toolbar && this.toolbar.xtype) {
32107 this.toolbar.container = this.getView().getHeaderPanel(true);
32108 this.toolbar = new Roo.Toolbar(this.toolbar);
32110 if (this.footer && this.footer.xtype) {
32111 this.footer.dataSource = this.getDataSource();
32112 this.footer.container = this.getView().getFooterPanel(true);
32113 this.footer = Roo.factory(this.footer, Roo);
32115 if (this.dropTarget && this.dropTarget.xtype) {
32116 delete this.dropTarget.xtype;
32117 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32121 this.rendered = true;
32122 this.fireEvent('render', this);
32127 * Reconfigures the grid to use a different Store and Column Model.
32128 * The View will be bound to the new objects and refreshed.
32129 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32130 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32132 reconfigure : function(dataSource, colModel){
32134 this.loadMask.destroy();
32135 this.loadMask = new Roo.LoadMask(this.container,
32136 Roo.apply({store:dataSource}, this.loadMask));
32138 this.view.bind(dataSource, colModel);
32139 this.dataSource = dataSource;
32140 this.colModel = colModel;
32141 this.view.refresh(true);
32145 onKeyDown : function(e){
32146 this.fireEvent("keydown", e);
32150 * Destroy this grid.
32151 * @param {Boolean} removeEl True to remove the element
32153 destroy : function(removeEl, keepListeners){
32155 this.loadMask.destroy();
32157 var c = this.container;
32158 c.removeAllListeners();
32159 this.view.destroy();
32160 this.colModel.purgeListeners();
32161 if(!keepListeners){
32162 this.purgeListeners();
32165 if(removeEl === true){
32171 processEvent : function(name, e){
32172 // does this fire select???
32173 //Roo.log('grid:processEvent ' + name);
32175 if (name != 'touchstart' ) {
32176 this.fireEvent(name, e);
32179 var t = e.getTarget();
32181 var header = v.findHeaderIndex(t);
32182 if(header !== false){
32183 var ename = name == 'touchstart' ? 'click' : name;
32185 this.fireEvent("header" + ename, this, header, e);
32187 var row = v.findRowIndex(t);
32188 var cell = v.findCellIndex(t);
32189 if (name == 'touchstart') {
32190 // first touch is always a click.
32191 // hopefull this happens after selection is updated.?
32194 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32195 var cs = this.selModel.getSelectedCell();
32196 if (row == cs[0] && cell == cs[1]){
32200 if (typeof(this.selModel.getSelections) != 'undefined') {
32201 var cs = this.selModel.getSelections();
32202 var ds = this.dataSource;
32203 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32214 this.fireEvent("row" + name, this, row, e);
32215 if(cell !== false){
32216 this.fireEvent("cell" + name, this, row, cell, e);
32223 onClick : function(e){
32224 this.processEvent("click", e);
32227 onTouchStart : function(e){
32228 this.processEvent("touchstart", e);
32232 onContextMenu : function(e, t){
32233 this.processEvent("contextmenu", e);
32237 onDblClick : function(e){
32238 this.processEvent("dblclick", e);
32242 walkCells : function(row, col, step, fn, scope){
32243 var cm = this.colModel, clen = cm.getColumnCount();
32244 var ds = this.dataSource, rlen = ds.getCount(), first = true;
32256 if(fn.call(scope || this, row, col, cm) === true){
32274 if(fn.call(scope || this, row, col, cm) === true){
32286 getSelections : function(){
32287 return this.selModel.getSelections();
32291 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32292 * but if manual update is required this method will initiate it.
32294 autoSize : function(){
32296 this.view.layout();
32297 if(this.view.adjustForScroll){
32298 this.view.adjustForScroll();
32304 * Returns the grid's underlying element.
32305 * @return {Element} The element
32307 getGridEl : function(){
32308 return this.container;
32311 // private for compatibility, overridden by editor grid
32312 stopEditing : function(){},
32315 * Returns the grid's SelectionModel.
32316 * @return {SelectionModel}
32318 getSelectionModel : function(){
32319 if(!this.selModel){
32320 this.selModel = new Roo.grid.RowSelectionModel();
32322 return this.selModel;
32326 * Returns the grid's DataSource.
32327 * @return {DataSource}
32329 getDataSource : function(){
32330 return this.dataSource;
32334 * Returns the grid's ColumnModel.
32335 * @return {ColumnModel}
32337 getColumnModel : function(){
32338 return this.colModel;
32342 * Returns the grid's GridView object.
32343 * @return {GridView}
32345 getView : function(){
32347 this.view = new Roo.grid.GridView(this.viewConfig);
32352 * Called to get grid's drag proxy text, by default returns this.ddText.
32355 getDragDropText : function(){
32356 var count = this.selModel.getCount();
32357 return String.format(this.ddText, count, count == 1 ? '' : 's');
32361 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32362 * %0 is replaced with the number of selected rows.
32365 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32367 * Ext JS Library 1.1.1
32368 * Copyright(c) 2006-2007, Ext JS, LLC.
32370 * Originally Released Under LGPL - original licence link has changed is not relivant.
32373 * <script type="text/javascript">
32376 Roo.grid.AbstractGridView = function(){
32380 "beforerowremoved" : true,
32381 "beforerowsinserted" : true,
32382 "beforerefresh" : true,
32383 "rowremoved" : true,
32384 "rowsinserted" : true,
32385 "rowupdated" : true,
32388 Roo.grid.AbstractGridView.superclass.constructor.call(this);
32391 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32392 rowClass : "x-grid-row",
32393 cellClass : "x-grid-cell",
32394 tdClass : "x-grid-td",
32395 hdClass : "x-grid-hd",
32396 splitClass : "x-grid-hd-split",
32398 init: function(grid){
32400 var cid = this.grid.getGridEl().id;
32401 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32402 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32403 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32404 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32407 getColumnRenderers : function(){
32408 var renderers = [];
32409 var cm = this.grid.colModel;
32410 var colCount = cm.getColumnCount();
32411 for(var i = 0; i < colCount; i++){
32412 renderers[i] = cm.getRenderer(i);
32417 getColumnIds : function(){
32419 var cm = this.grid.colModel;
32420 var colCount = cm.getColumnCount();
32421 for(var i = 0; i < colCount; i++){
32422 ids[i] = cm.getColumnId(i);
32427 getDataIndexes : function(){
32428 if(!this.indexMap){
32429 this.indexMap = this.buildIndexMap();
32431 return this.indexMap.colToData;
32434 getColumnIndexByDataIndex : function(dataIndex){
32435 if(!this.indexMap){
32436 this.indexMap = this.buildIndexMap();
32438 return this.indexMap.dataToCol[dataIndex];
32442 * Set a css style for a column dynamically.
32443 * @param {Number} colIndex The index of the column
32444 * @param {String} name The css property name
32445 * @param {String} value The css value
32447 setCSSStyle : function(colIndex, name, value){
32448 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32449 Roo.util.CSS.updateRule(selector, name, value);
32452 generateRules : function(cm){
32453 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32454 Roo.util.CSS.removeStyleSheet(rulesId);
32455 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32456 var cid = cm.getColumnId(i);
32457 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32458 this.tdSelector, cid, " {\n}\n",
32459 this.hdSelector, cid, " {\n}\n",
32460 this.splitSelector, cid, " {\n}\n");
32462 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32466 * Ext JS Library 1.1.1
32467 * Copyright(c) 2006-2007, Ext JS, LLC.
32469 * Originally Released Under LGPL - original licence link has changed is not relivant.
32472 * <script type="text/javascript">
32476 // This is a support class used internally by the Grid components
32477 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32479 this.view = grid.getView();
32480 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32481 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32483 this.setHandleElId(Roo.id(hd));
32484 this.setOuterHandleElId(Roo.id(hd2));
32486 this.scroll = false;
32488 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32490 getDragData : function(e){
32491 var t = Roo.lib.Event.getTarget(e);
32492 var h = this.view.findHeaderCell(t);
32494 return {ddel: h.firstChild, header:h};
32499 onInitDrag : function(e){
32500 this.view.headersDisabled = true;
32501 var clone = this.dragData.ddel.cloneNode(true);
32502 clone.id = Roo.id();
32503 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32504 this.proxy.update(clone);
32508 afterValidDrop : function(){
32510 setTimeout(function(){
32511 v.headersDisabled = false;
32515 afterInvalidDrop : function(){
32517 setTimeout(function(){
32518 v.headersDisabled = false;
32524 * Ext JS Library 1.1.1
32525 * Copyright(c) 2006-2007, Ext JS, LLC.
32527 * Originally Released Under LGPL - original licence link has changed is not relivant.
32530 * <script type="text/javascript">
32533 // This is a support class used internally by the Grid components
32534 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32536 this.view = grid.getView();
32537 // split the proxies so they don't interfere with mouse events
32538 this.proxyTop = Roo.DomHelper.append(document.body, {
32539 cls:"col-move-top", html:" "
32541 this.proxyBottom = Roo.DomHelper.append(document.body, {
32542 cls:"col-move-bottom", html:" "
32544 this.proxyTop.hide = this.proxyBottom.hide = function(){
32545 this.setLeftTop(-100,-100);
32546 this.setStyle("visibility", "hidden");
32548 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32549 // temporarily disabled
32550 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32551 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32553 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32554 proxyOffsets : [-4, -9],
32555 fly: Roo.Element.fly,
32557 getTargetFromEvent : function(e){
32558 var t = Roo.lib.Event.getTarget(e);
32559 var cindex = this.view.findCellIndex(t);
32560 if(cindex !== false){
32561 return this.view.getHeaderCell(cindex);
32566 nextVisible : function(h){
32567 var v = this.view, cm = this.grid.colModel;
32570 if(!cm.isHidden(v.getCellIndex(h))){
32578 prevVisible : function(h){
32579 var v = this.view, cm = this.grid.colModel;
32582 if(!cm.isHidden(v.getCellIndex(h))){
32590 positionIndicator : function(h, n, e){
32591 var x = Roo.lib.Event.getPageX(e);
32592 var r = Roo.lib.Dom.getRegion(n.firstChild);
32593 var px, pt, py = r.top + this.proxyOffsets[1];
32594 if((r.right - x) <= (r.right-r.left)/2){
32595 px = r.right+this.view.borderWidth;
32601 var oldIndex = this.view.getCellIndex(h);
32602 var newIndex = this.view.getCellIndex(n);
32604 if(this.grid.colModel.isFixed(newIndex)){
32608 var locked = this.grid.colModel.isLocked(newIndex);
32613 if(oldIndex < newIndex){
32616 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32619 px += this.proxyOffsets[0];
32620 this.proxyTop.setLeftTop(px, py);
32621 this.proxyTop.show();
32622 if(!this.bottomOffset){
32623 this.bottomOffset = this.view.mainHd.getHeight();
32625 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32626 this.proxyBottom.show();
32630 onNodeEnter : function(n, dd, e, data){
32631 if(data.header != n){
32632 this.positionIndicator(data.header, n, e);
32636 onNodeOver : function(n, dd, e, data){
32637 var result = false;
32638 if(data.header != n){
32639 result = this.positionIndicator(data.header, n, e);
32642 this.proxyTop.hide();
32643 this.proxyBottom.hide();
32645 return result ? this.dropAllowed : this.dropNotAllowed;
32648 onNodeOut : function(n, dd, e, data){
32649 this.proxyTop.hide();
32650 this.proxyBottom.hide();
32653 onNodeDrop : function(n, dd, e, data){
32654 var h = data.header;
32656 var cm = this.grid.colModel;
32657 var x = Roo.lib.Event.getPageX(e);
32658 var r = Roo.lib.Dom.getRegion(n.firstChild);
32659 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32660 var oldIndex = this.view.getCellIndex(h);
32661 var newIndex = this.view.getCellIndex(n);
32662 var locked = cm.isLocked(newIndex);
32666 if(oldIndex < newIndex){
32669 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32672 cm.setLocked(oldIndex, locked, true);
32673 cm.moveColumn(oldIndex, newIndex);
32674 this.grid.fireEvent("columnmove", oldIndex, newIndex);
32682 * Ext JS Library 1.1.1
32683 * Copyright(c) 2006-2007, Ext JS, LLC.
32685 * Originally Released Under LGPL - original licence link has changed is not relivant.
32688 * <script type="text/javascript">
32692 * @class Roo.grid.GridView
32693 * @extends Roo.util.Observable
32696 * @param {Object} config
32698 Roo.grid.GridView = function(config){
32699 Roo.grid.GridView.superclass.constructor.call(this);
32702 Roo.apply(this, config);
32705 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32707 unselectable : 'unselectable="on"',
32708 unselectableCls : 'x-unselectable',
32711 rowClass : "x-grid-row",
32713 cellClass : "x-grid-col",
32715 tdClass : "x-grid-td",
32717 hdClass : "x-grid-hd",
32719 splitClass : "x-grid-split",
32721 sortClasses : ["sort-asc", "sort-desc"],
32723 enableMoveAnim : false,
32727 dh : Roo.DomHelper,
32729 fly : Roo.Element.fly,
32731 css : Roo.util.CSS,
32737 scrollIncrement : 22,
32739 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32741 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32743 bind : function(ds, cm){
32745 this.ds.un("load", this.onLoad, this);
32746 this.ds.un("datachanged", this.onDataChange, this);
32747 this.ds.un("add", this.onAdd, this);
32748 this.ds.un("remove", this.onRemove, this);
32749 this.ds.un("update", this.onUpdate, this);
32750 this.ds.un("clear", this.onClear, this);
32753 ds.on("load", this.onLoad, this);
32754 ds.on("datachanged", this.onDataChange, this);
32755 ds.on("add", this.onAdd, this);
32756 ds.on("remove", this.onRemove, this);
32757 ds.on("update", this.onUpdate, this);
32758 ds.on("clear", this.onClear, this);
32763 this.cm.un("widthchange", this.onColWidthChange, this);
32764 this.cm.un("headerchange", this.onHeaderChange, this);
32765 this.cm.un("hiddenchange", this.onHiddenChange, this);
32766 this.cm.un("columnmoved", this.onColumnMove, this);
32767 this.cm.un("columnlockchange", this.onColumnLock, this);
32770 this.generateRules(cm);
32771 cm.on("widthchange", this.onColWidthChange, this);
32772 cm.on("headerchange", this.onHeaderChange, this);
32773 cm.on("hiddenchange", this.onHiddenChange, this);
32774 cm.on("columnmoved", this.onColumnMove, this);
32775 cm.on("columnlockchange", this.onColumnLock, this);
32780 init: function(grid){
32781 Roo.grid.GridView.superclass.init.call(this, grid);
32783 this.bind(grid.dataSource, grid.colModel);
32785 grid.on("headerclick", this.handleHeaderClick, this);
32787 if(grid.trackMouseOver){
32788 grid.on("mouseover", this.onRowOver, this);
32789 grid.on("mouseout", this.onRowOut, this);
32791 grid.cancelTextSelection = function(){};
32792 this.gridId = grid.id;
32794 var tpls = this.templates || {};
32797 tpls.master = new Roo.Template(
32798 '<div class="x-grid" hidefocus="true">',
32799 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32800 '<div class="x-grid-topbar"></div>',
32801 '<div class="x-grid-scroller"><div></div></div>',
32802 '<div class="x-grid-locked">',
32803 '<div class="x-grid-header">{lockedHeader}</div>',
32804 '<div class="x-grid-body">{lockedBody}</div>',
32806 '<div class="x-grid-viewport">',
32807 '<div class="x-grid-header">{header}</div>',
32808 '<div class="x-grid-body">{body}</div>',
32810 '<div class="x-grid-bottombar"></div>',
32812 '<div class="x-grid-resize-proxy"> </div>',
32815 tpls.master.disableformats = true;
32819 tpls.header = new Roo.Template(
32820 '<table border="0" cellspacing="0" cellpadding="0">',
32821 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32824 tpls.header.disableformats = true;
32826 tpls.header.compile();
32829 tpls.hcell = new Roo.Template(
32830 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32831 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32834 tpls.hcell.disableFormats = true;
32836 tpls.hcell.compile();
32839 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32840 this.unselectableCls + '" ' + this.unselectable +'> </div>');
32841 tpls.hsplit.disableFormats = true;
32843 tpls.hsplit.compile();
32846 tpls.body = new Roo.Template(
32847 '<table border="0" cellspacing="0" cellpadding="0">',
32848 "<tbody>{rows}</tbody>",
32851 tpls.body.disableFormats = true;
32853 tpls.body.compile();
32856 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32857 tpls.row.disableFormats = true;
32859 tpls.row.compile();
32862 tpls.cell = new Roo.Template(
32863 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32864 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32865 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32868 tpls.cell.disableFormats = true;
32870 tpls.cell.compile();
32872 this.templates = tpls;
32875 // remap these for backwards compat
32876 onColWidthChange : function(){
32877 this.updateColumns.apply(this, arguments);
32879 onHeaderChange : function(){
32880 this.updateHeaders.apply(this, arguments);
32882 onHiddenChange : function(){
32883 this.handleHiddenChange.apply(this, arguments);
32885 onColumnMove : function(){
32886 this.handleColumnMove.apply(this, arguments);
32888 onColumnLock : function(){
32889 this.handleLockChange.apply(this, arguments);
32892 onDataChange : function(){
32894 this.updateHeaderSortState();
32897 onClear : function(){
32901 onUpdate : function(ds, record){
32902 this.refreshRow(record);
32905 refreshRow : function(record){
32906 var ds = this.ds, index;
32907 if(typeof record == 'number'){
32909 record = ds.getAt(index);
32911 index = ds.indexOf(record);
32913 this.insertRows(ds, index, index, true);
32914 this.onRemove(ds, record, index+1, true);
32915 this.syncRowHeights(index, index);
32917 this.fireEvent("rowupdated", this, index, record);
32920 onAdd : function(ds, records, index){
32921 this.insertRows(ds, index, index + (records.length-1));
32924 onRemove : function(ds, record, index, isUpdate){
32925 if(isUpdate !== true){
32926 this.fireEvent("beforerowremoved", this, index, record);
32928 var bt = this.getBodyTable(), lt = this.getLockedTable();
32929 if(bt.rows[index]){
32930 bt.firstChild.removeChild(bt.rows[index]);
32932 if(lt.rows[index]){
32933 lt.firstChild.removeChild(lt.rows[index]);
32935 if(isUpdate !== true){
32936 this.stripeRows(index);
32937 this.syncRowHeights(index, index);
32939 this.fireEvent("rowremoved", this, index, record);
32943 onLoad : function(){
32944 this.scrollToTop();
32948 * Scrolls the grid to the top
32950 scrollToTop : function(){
32952 this.scroller.dom.scrollTop = 0;
32958 * Gets a panel in the header of the grid that can be used for toolbars etc.
32959 * After modifying the contents of this panel a call to grid.autoSize() may be
32960 * required to register any changes in size.
32961 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32962 * @return Roo.Element
32964 getHeaderPanel : function(doShow){
32966 this.headerPanel.show();
32968 return this.headerPanel;
32972 * Gets a panel in the footer of the grid that can be used for toolbars etc.
32973 * After modifying the contents of this panel a call to grid.autoSize() may be
32974 * required to register any changes in size.
32975 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32976 * @return Roo.Element
32978 getFooterPanel : function(doShow){
32980 this.footerPanel.show();
32982 return this.footerPanel;
32985 initElements : function(){
32986 var E = Roo.Element;
32987 var el = this.grid.getGridEl().dom.firstChild;
32988 var cs = el.childNodes;
32990 this.el = new E(el);
32992 this.focusEl = new E(el.firstChild);
32993 this.focusEl.swallowEvent("click", true);
32995 this.headerPanel = new E(cs[1]);
32996 this.headerPanel.enableDisplayMode("block");
32998 this.scroller = new E(cs[2]);
32999 this.scrollSizer = new E(this.scroller.dom.firstChild);
33001 this.lockedWrap = new E(cs[3]);
33002 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33003 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33005 this.mainWrap = new E(cs[4]);
33006 this.mainHd = new E(this.mainWrap.dom.firstChild);
33007 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33009 this.footerPanel = new E(cs[5]);
33010 this.footerPanel.enableDisplayMode("block");
33012 this.resizeProxy = new E(cs[6]);
33014 this.headerSelector = String.format(
33015 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33016 this.lockedHd.id, this.mainHd.id
33019 this.splitterSelector = String.format(
33020 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33021 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33024 idToCssName : function(s)
33026 return s.replace(/[^a-z0-9]+/ig, '-');
33029 getHeaderCell : function(index){
33030 return Roo.DomQuery.select(this.headerSelector)[index];
33033 getHeaderCellMeasure : function(index){
33034 return this.getHeaderCell(index).firstChild;
33037 getHeaderCellText : function(index){
33038 return this.getHeaderCell(index).firstChild.firstChild;
33041 getLockedTable : function(){
33042 return this.lockedBody.dom.firstChild;
33045 getBodyTable : function(){
33046 return this.mainBody.dom.firstChild;
33049 getLockedRow : function(index){
33050 return this.getLockedTable().rows[index];
33053 getRow : function(index){
33054 return this.getBodyTable().rows[index];
33057 getRowComposite : function(index){
33059 this.rowEl = new Roo.CompositeElementLite();
33061 var els = [], lrow, mrow;
33062 if(lrow = this.getLockedRow(index)){
33065 if(mrow = this.getRow(index)){
33068 this.rowEl.elements = els;
33072 * Gets the 'td' of the cell
33074 * @param {Integer} rowIndex row to select
33075 * @param {Integer} colIndex column to select
33079 getCell : function(rowIndex, colIndex){
33080 var locked = this.cm.getLockedCount();
33082 if(colIndex < locked){
33083 source = this.lockedBody.dom.firstChild;
33085 source = this.mainBody.dom.firstChild;
33086 colIndex -= locked;
33088 return source.rows[rowIndex].childNodes[colIndex];
33091 getCellText : function(rowIndex, colIndex){
33092 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33095 getCellBox : function(cell){
33096 var b = this.fly(cell).getBox();
33097 if(Roo.isOpera){ // opera fails to report the Y
33098 b.y = cell.offsetTop + this.mainBody.getY();
33103 getCellIndex : function(cell){
33104 var id = String(cell.className).match(this.cellRE);
33106 return parseInt(id[1], 10);
33111 findHeaderIndex : function(n){
33112 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33113 return r ? this.getCellIndex(r) : false;
33116 findHeaderCell : function(n){
33117 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33118 return r ? r : false;
33121 findRowIndex : function(n){
33125 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33126 return r ? r.rowIndex : false;
33129 findCellIndex : function(node){
33130 var stop = this.el.dom;
33131 while(node && node != stop){
33132 if(this.findRE.test(node.className)){
33133 return this.getCellIndex(node);
33135 node = node.parentNode;
33140 getColumnId : function(index){
33141 return this.cm.getColumnId(index);
33144 getSplitters : function()
33146 if(this.splitterSelector){
33147 return Roo.DomQuery.select(this.splitterSelector);
33153 getSplitter : function(index){
33154 return this.getSplitters()[index];
33157 onRowOver : function(e, t){
33159 if((row = this.findRowIndex(t)) !== false){
33160 this.getRowComposite(row).addClass("x-grid-row-over");
33164 onRowOut : function(e, t){
33166 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33167 this.getRowComposite(row).removeClass("x-grid-row-over");
33171 renderHeaders : function(){
33173 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33174 var cb = [], lb = [], sb = [], lsb = [], p = {};
33175 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33176 p.cellId = "x-grid-hd-0-" + i;
33177 p.splitId = "x-grid-csplit-0-" + i;
33178 p.id = cm.getColumnId(i);
33179 p.value = cm.getColumnHeader(i) || "";
33180 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33181 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33182 if(!cm.isLocked(i)){
33183 cb[cb.length] = ct.apply(p);
33184 sb[sb.length] = st.apply(p);
33186 lb[lb.length] = ct.apply(p);
33187 lsb[lsb.length] = st.apply(p);
33190 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33191 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33194 updateHeaders : function(){
33195 var html = this.renderHeaders();
33196 this.lockedHd.update(html[0]);
33197 this.mainHd.update(html[1]);
33201 * Focuses the specified row.
33202 * @param {Number} row The row index
33204 focusRow : function(row)
33206 //Roo.log('GridView.focusRow');
33207 var x = this.scroller.dom.scrollLeft;
33208 this.focusCell(row, 0, false);
33209 this.scroller.dom.scrollLeft = x;
33213 * Focuses the specified cell.
33214 * @param {Number} row The row index
33215 * @param {Number} col The column index
33216 * @param {Boolean} hscroll false to disable horizontal scrolling
33218 focusCell : function(row, col, hscroll)
33220 //Roo.log('GridView.focusCell');
33221 var el = this.ensureVisible(row, col, hscroll);
33222 this.focusEl.alignTo(el, "tl-tl");
33224 this.focusEl.focus();
33226 this.focusEl.focus.defer(1, this.focusEl);
33231 * Scrolls the specified cell into view
33232 * @param {Number} row The row index
33233 * @param {Number} col The column index
33234 * @param {Boolean} hscroll false to disable horizontal scrolling
33236 ensureVisible : function(row, col, hscroll)
33238 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33239 //return null; //disable for testing.
33240 if(typeof row != "number"){
33241 row = row.rowIndex;
33243 if(row < 0 && row >= this.ds.getCount()){
33246 col = (col !== undefined ? col : 0);
33247 var cm = this.grid.colModel;
33248 while(cm.isHidden(col)){
33252 var el = this.getCell(row, col);
33256 var c = this.scroller.dom;
33258 var ctop = parseInt(el.offsetTop, 10);
33259 var cleft = parseInt(el.offsetLeft, 10);
33260 var cbot = ctop + el.offsetHeight;
33261 var cright = cleft + el.offsetWidth;
33263 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33264 var stop = parseInt(c.scrollTop, 10);
33265 var sleft = parseInt(c.scrollLeft, 10);
33266 var sbot = stop + ch;
33267 var sright = sleft + c.clientWidth;
33269 Roo.log('GridView.ensureVisible:' +
33271 ' c.clientHeight:' + c.clientHeight +
33272 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33280 c.scrollTop = ctop;
33281 //Roo.log("set scrolltop to ctop DISABLE?");
33282 }else if(cbot > sbot){
33283 //Roo.log("set scrolltop to cbot-ch");
33284 c.scrollTop = cbot-ch;
33287 if(hscroll !== false){
33289 c.scrollLeft = cleft;
33290 }else if(cright > sright){
33291 c.scrollLeft = cright-c.clientWidth;
33298 updateColumns : function(){
33299 this.grid.stopEditing();
33300 var cm = this.grid.colModel, colIds = this.getColumnIds();
33301 //var totalWidth = cm.getTotalWidth();
33303 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33304 //if(cm.isHidden(i)) continue;
33305 var w = cm.getColumnWidth(i);
33306 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33307 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33309 this.updateSplitters();
33312 generateRules : function(cm){
33313 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33314 Roo.util.CSS.removeStyleSheet(rulesId);
33315 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33316 var cid = cm.getColumnId(i);
33318 if(cm.config[i].align){
33319 align = 'text-align:'+cm.config[i].align+';';
33322 if(cm.isHidden(i)){
33323 hidden = 'display:none;';
33325 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33327 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33328 this.hdSelector, cid, " {\n", align, width, "}\n",
33329 this.tdSelector, cid, " {\n",hidden,"\n}\n",
33330 this.splitSelector, cid, " {\n", hidden , "\n}\n");
33332 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33335 updateSplitters : function(){
33336 var cm = this.cm, s = this.getSplitters();
33337 if(s){ // splitters not created yet
33338 var pos = 0, locked = true;
33339 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33340 if(cm.isHidden(i)) {
33343 var w = cm.getColumnWidth(i); // make sure it's a number
33344 if(!cm.isLocked(i) && locked){
33349 s[i].style.left = (pos-this.splitOffset) + "px";
33354 handleHiddenChange : function(colModel, colIndex, hidden){
33356 this.hideColumn(colIndex);
33358 this.unhideColumn(colIndex);
33362 hideColumn : function(colIndex){
33363 var cid = this.getColumnId(colIndex);
33364 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33365 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33367 this.updateHeaders();
33369 this.updateSplitters();
33373 unhideColumn : function(colIndex){
33374 var cid = this.getColumnId(colIndex);
33375 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33376 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33379 this.updateHeaders();
33381 this.updateSplitters();
33385 insertRows : function(dm, firstRow, lastRow, isUpdate){
33386 if(firstRow == 0 && lastRow == dm.getCount()-1){
33390 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33392 var s = this.getScrollState();
33393 var markup = this.renderRows(firstRow, lastRow);
33394 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33395 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33396 this.restoreScroll(s);
33398 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33399 this.syncRowHeights(firstRow, lastRow);
33400 this.stripeRows(firstRow);
33406 bufferRows : function(markup, target, index){
33407 var before = null, trows = target.rows, tbody = target.tBodies[0];
33408 if(index < trows.length){
33409 before = trows[index];
33411 var b = document.createElement("div");
33412 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33413 var rows = b.firstChild.rows;
33414 for(var i = 0, len = rows.length; i < len; i++){
33416 tbody.insertBefore(rows[0], before);
33418 tbody.appendChild(rows[0]);
33425 deleteRows : function(dm, firstRow, lastRow){
33426 if(dm.getRowCount()<1){
33427 this.fireEvent("beforerefresh", this);
33428 this.mainBody.update("");
33429 this.lockedBody.update("");
33430 this.fireEvent("refresh", this);
33432 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33433 var bt = this.getBodyTable();
33434 var tbody = bt.firstChild;
33435 var rows = bt.rows;
33436 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33437 tbody.removeChild(rows[firstRow]);
33439 this.stripeRows(firstRow);
33440 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33444 updateRows : function(dataSource, firstRow, lastRow){
33445 var s = this.getScrollState();
33447 this.restoreScroll(s);
33450 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33454 this.updateHeaderSortState();
33457 getScrollState : function(){
33459 var sb = this.scroller.dom;
33460 return {left: sb.scrollLeft, top: sb.scrollTop};
33463 stripeRows : function(startRow){
33464 if(!this.grid.stripeRows || this.ds.getCount() < 1){
33467 startRow = startRow || 0;
33468 var rows = this.getBodyTable().rows;
33469 var lrows = this.getLockedTable().rows;
33470 var cls = ' x-grid-row-alt ';
33471 for(var i = startRow, len = rows.length; i < len; i++){
33472 var row = rows[i], lrow = lrows[i];
33473 var isAlt = ((i+1) % 2 == 0);
33474 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33475 if(isAlt == hasAlt){
33479 row.className += " x-grid-row-alt";
33481 row.className = row.className.replace("x-grid-row-alt", "");
33484 lrow.className = row.className;
33489 restoreScroll : function(state){
33490 //Roo.log('GridView.restoreScroll');
33491 var sb = this.scroller.dom;
33492 sb.scrollLeft = state.left;
33493 sb.scrollTop = state.top;
33497 syncScroll : function(){
33498 //Roo.log('GridView.syncScroll');
33499 var sb = this.scroller.dom;
33500 var sh = this.mainHd.dom;
33501 var bs = this.mainBody.dom;
33502 var lv = this.lockedBody.dom;
33503 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33504 lv.scrollTop = bs.scrollTop = sb.scrollTop;
33507 handleScroll : function(e){
33509 var sb = this.scroller.dom;
33510 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33514 handleWheel : function(e){
33515 var d = e.getWheelDelta();
33516 this.scroller.dom.scrollTop -= d*22;
33517 // set this here to prevent jumpy scrolling on large tables
33518 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33522 renderRows : function(startRow, endRow){
33523 // pull in all the crap needed to render rows
33524 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33525 var colCount = cm.getColumnCount();
33527 if(ds.getCount() < 1){
33531 // build a map for all the columns
33533 for(var i = 0; i < colCount; i++){
33534 var name = cm.getDataIndex(i);
33536 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33537 renderer : cm.getRenderer(i),
33538 id : cm.getColumnId(i),
33539 locked : cm.isLocked(i),
33540 has_editor : cm.isCellEditable(i)
33544 startRow = startRow || 0;
33545 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33547 // records to render
33548 var rs = ds.getRange(startRow, endRow);
33550 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33553 // As much as I hate to duplicate code, this was branched because FireFox really hates
33554 // [].join("") on strings. The performance difference was substantial enough to
33555 // branch this function
33556 doRender : Roo.isGecko ?
33557 function(cs, rs, ds, startRow, colCount, stripe){
33558 var ts = this.templates, ct = ts.cell, rt = ts.row;
33560 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33562 var hasListener = this.grid.hasListener('rowclass');
33564 for(var j = 0, len = rs.length; j < len; j++){
33565 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33566 for(var i = 0; i < colCount; i++){
33568 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33570 p.css = p.attr = "";
33571 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33572 if(p.value == undefined || p.value === "") {
33573 p.value = " ";
33576 p.css += ' x-grid-editable-cell';
33578 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33579 p.css += ' x-grid-dirty-cell';
33581 var markup = ct.apply(p);
33589 if(stripe && ((rowIndex+1) % 2 == 0)){
33590 alt.push("x-grid-row-alt")
33593 alt.push( " x-grid-dirty-row");
33596 if(this.getRowClass){
33597 alt.push(this.getRowClass(r, rowIndex));
33603 rowIndex : rowIndex,
33606 this.grid.fireEvent('rowclass', this, rowcfg);
33607 alt.push(rowcfg.rowClass);
33609 rp.alt = alt.join(" ");
33610 lbuf+= rt.apply(rp);
33612 buf+= rt.apply(rp);
33614 return [lbuf, buf];
33616 function(cs, rs, ds, startRow, colCount, stripe){
33617 var ts = this.templates, ct = ts.cell, rt = ts.row;
33619 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33620 var hasListener = this.grid.hasListener('rowclass');
33623 for(var j = 0, len = rs.length; j < len; j++){
33624 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33625 for(var i = 0; i < colCount; i++){
33627 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33629 p.css = p.attr = "";
33630 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33631 if(p.value == undefined || p.value === "") {
33632 p.value = " ";
33636 p.css += ' x-grid-editable-cell';
33638 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33639 p.css += ' x-grid-dirty-cell'
33642 var markup = ct.apply(p);
33644 cb[cb.length] = markup;
33646 lcb[lcb.length] = markup;
33650 if(stripe && ((rowIndex+1) % 2 == 0)){
33651 alt.push( "x-grid-row-alt");
33654 alt.push(" x-grid-dirty-row");
33657 if(this.getRowClass){
33658 alt.push( this.getRowClass(r, rowIndex));
33664 rowIndex : rowIndex,
33667 this.grid.fireEvent('rowclass', this, rowcfg);
33668 alt.push(rowcfg.rowClass);
33671 rp.alt = alt.join(" ");
33672 rp.cells = lcb.join("");
33673 lbuf[lbuf.length] = rt.apply(rp);
33674 rp.cells = cb.join("");
33675 buf[buf.length] = rt.apply(rp);
33677 return [lbuf.join(""), buf.join("")];
33680 renderBody : function(){
33681 var markup = this.renderRows();
33682 var bt = this.templates.body;
33683 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33687 * Refreshes the grid
33688 * @param {Boolean} headersToo
33690 refresh : function(headersToo){
33691 this.fireEvent("beforerefresh", this);
33692 this.grid.stopEditing();
33693 var result = this.renderBody();
33694 this.lockedBody.update(result[0]);
33695 this.mainBody.update(result[1]);
33696 if(headersToo === true){
33697 this.updateHeaders();
33698 this.updateColumns();
33699 this.updateSplitters();
33700 this.updateHeaderSortState();
33702 this.syncRowHeights();
33704 this.fireEvent("refresh", this);
33707 handleColumnMove : function(cm, oldIndex, newIndex){
33708 this.indexMap = null;
33709 var s = this.getScrollState();
33710 this.refresh(true);
33711 this.restoreScroll(s);
33712 this.afterMove(newIndex);
33715 afterMove : function(colIndex){
33716 if(this.enableMoveAnim && Roo.enableFx){
33717 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33719 // if multisort - fix sortOrder, and reload..
33720 if (this.grid.dataSource.multiSort) {
33721 // the we can call sort again..
33722 var dm = this.grid.dataSource;
33723 var cm = this.grid.colModel;
33725 for(var i = 0; i < cm.config.length; i++ ) {
33727 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33728 continue; // dont' bother, it's not in sort list or being set.
33731 so.push(cm.config[i].dataIndex);
33734 dm.load(dm.lastOptions);
33741 updateCell : function(dm, rowIndex, dataIndex){
33742 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33743 if(typeof colIndex == "undefined"){ // not present in grid
33746 var cm = this.grid.colModel;
33747 var cell = this.getCell(rowIndex, colIndex);
33748 var cellText = this.getCellText(rowIndex, colIndex);
33751 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33752 id : cm.getColumnId(colIndex),
33753 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33755 var renderer = cm.getRenderer(colIndex);
33756 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33757 if(typeof val == "undefined" || val === "") {
33760 cellText.innerHTML = val;
33761 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33762 this.syncRowHeights(rowIndex, rowIndex);
33765 calcColumnWidth : function(colIndex, maxRowsToMeasure){
33767 if(this.grid.autoSizeHeaders){
33768 var h = this.getHeaderCellMeasure(colIndex);
33769 maxWidth = Math.max(maxWidth, h.scrollWidth);
33772 if(this.cm.isLocked(colIndex)){
33773 tb = this.getLockedTable();
33776 tb = this.getBodyTable();
33777 index = colIndex - this.cm.getLockedCount();
33780 var rows = tb.rows;
33781 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33782 for(var i = 0; i < stopIndex; i++){
33783 var cell = rows[i].childNodes[index].firstChild;
33784 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33787 return maxWidth + /*margin for error in IE*/ 5;
33790 * Autofit a column to its content.
33791 * @param {Number} colIndex
33792 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33794 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33795 if(this.cm.isHidden(colIndex)){
33796 return; // can't calc a hidden column
33799 var cid = this.cm.getColumnId(colIndex);
33800 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33801 if(this.grid.autoSizeHeaders){
33802 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33805 var newWidth = this.calcColumnWidth(colIndex);
33806 this.cm.setColumnWidth(colIndex,
33807 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33808 if(!suppressEvent){
33809 this.grid.fireEvent("columnresize", colIndex, newWidth);
33814 * Autofits all columns to their content and then expands to fit any extra space in the grid
33816 autoSizeColumns : function(){
33817 var cm = this.grid.colModel;
33818 var colCount = cm.getColumnCount();
33819 for(var i = 0; i < colCount; i++){
33820 this.autoSizeColumn(i, true, true);
33822 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33825 this.updateColumns();
33831 * Autofits all columns to the grid's width proportionate with their current size
33832 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33834 fitColumns : function(reserveScrollSpace){
33835 var cm = this.grid.colModel;
33836 var colCount = cm.getColumnCount();
33840 for (i = 0; i < colCount; i++){
33841 if(!cm.isHidden(i) && !cm.isFixed(i)){
33842 w = cm.getColumnWidth(i);
33848 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33849 if(reserveScrollSpace){
33852 var frac = (avail - cm.getTotalWidth())/width;
33853 while (cols.length){
33856 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33858 this.updateColumns();
33862 onRowSelect : function(rowIndex){
33863 var row = this.getRowComposite(rowIndex);
33864 row.addClass("x-grid-row-selected");
33867 onRowDeselect : function(rowIndex){
33868 var row = this.getRowComposite(rowIndex);
33869 row.removeClass("x-grid-row-selected");
33872 onCellSelect : function(row, col){
33873 var cell = this.getCell(row, col);
33875 Roo.fly(cell).addClass("x-grid-cell-selected");
33879 onCellDeselect : function(row, col){
33880 var cell = this.getCell(row, col);
33882 Roo.fly(cell).removeClass("x-grid-cell-selected");
33886 updateHeaderSortState : function(){
33888 // sort state can be single { field: xxx, direction : yyy}
33889 // or { xxx=>ASC , yyy : DESC ..... }
33892 if (!this.ds.multiSort) {
33893 var state = this.ds.getSortState();
33897 mstate[state.field] = state.direction;
33898 // FIXME... - this is not used here.. but might be elsewhere..
33899 this.sortState = state;
33902 mstate = this.ds.sortToggle;
33904 //remove existing sort classes..
33906 var sc = this.sortClasses;
33907 var hds = this.el.select(this.headerSelector).removeClass(sc);
33909 for(var f in mstate) {
33911 var sortColumn = this.cm.findColumnIndex(f);
33913 if(sortColumn != -1){
33914 var sortDir = mstate[f];
33915 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33924 handleHeaderClick : function(g, index,e){
33926 Roo.log("header click");
33929 // touch events on header are handled by context
33930 this.handleHdCtx(g,index,e);
33935 if(this.headersDisabled){
33938 var dm = g.dataSource, cm = g.colModel;
33939 if(!cm.isSortable(index)){
33944 if (dm.multiSort) {
33945 // update the sortOrder
33947 for(var i = 0; i < cm.config.length; i++ ) {
33949 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33950 continue; // dont' bother, it's not in sort list or being set.
33953 so.push(cm.config[i].dataIndex);
33959 dm.sort(cm.getDataIndex(index));
33963 destroy : function(){
33965 this.colMenu.removeAll();
33966 Roo.menu.MenuMgr.unregister(this.colMenu);
33967 this.colMenu.getEl().remove();
33968 delete this.colMenu;
33971 this.hmenu.removeAll();
33972 Roo.menu.MenuMgr.unregister(this.hmenu);
33973 this.hmenu.getEl().remove();
33976 if(this.grid.enableColumnMove){
33977 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33979 for(var dd in dds){
33980 if(!dds[dd].config.isTarget && dds[dd].dragElId){
33981 var elid = dds[dd].dragElId;
33983 Roo.get(elid).remove();
33984 } else if(dds[dd].config.isTarget){
33985 dds[dd].proxyTop.remove();
33986 dds[dd].proxyBottom.remove();
33989 if(Roo.dd.DDM.locationCache[dd]){
33990 delete Roo.dd.DDM.locationCache[dd];
33993 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33996 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33997 this.bind(null, null);
33998 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34001 handleLockChange : function(){
34002 this.refresh(true);
34005 onDenyColumnLock : function(){
34009 onDenyColumnHide : function(){
34013 handleHdMenuClick : function(item){
34014 var index = this.hdCtxIndex;
34015 var cm = this.cm, ds = this.ds;
34018 ds.sort(cm.getDataIndex(index), "ASC");
34021 ds.sort(cm.getDataIndex(index), "DESC");
34024 var lc = cm.getLockedCount();
34025 if(cm.getColumnCount(true) <= lc+1){
34026 this.onDenyColumnLock();
34030 cm.setLocked(index, true, true);
34031 cm.moveColumn(index, lc);
34032 this.grid.fireEvent("columnmove", index, lc);
34034 cm.setLocked(index, true);
34038 var lc = cm.getLockedCount();
34039 if((lc-1) != index){
34040 cm.setLocked(index, false, true);
34041 cm.moveColumn(index, lc-1);
34042 this.grid.fireEvent("columnmove", index, lc-1);
34044 cm.setLocked(index, false);
34047 case 'wider': // used to expand cols on touch..
34049 var cw = cm.getColumnWidth(index);
34050 cw += (item.id == 'wider' ? 1 : -1) * 50;
34051 cw = Math.max(0, cw);
34052 cw = Math.min(cw,4000);
34053 cm.setColumnWidth(index, cw);
34057 index = cm.getIndexById(item.id.substr(4));
34059 if(item.checked && cm.getColumnCount(true) <= 1){
34060 this.onDenyColumnHide();
34063 cm.setHidden(index, item.checked);
34069 beforeColMenuShow : function(){
34070 var cm = this.cm, colCount = cm.getColumnCount();
34071 this.colMenu.removeAll();
34072 for(var i = 0; i < colCount; i++){
34073 this.colMenu.add(new Roo.menu.CheckItem({
34074 id: "col-"+cm.getColumnId(i),
34075 text: cm.getColumnHeader(i),
34076 checked: !cm.isHidden(i),
34082 handleHdCtx : function(g, index, e){
34084 var hd = this.getHeaderCell(index);
34085 this.hdCtxIndex = index;
34086 var ms = this.hmenu.items, cm = this.cm;
34087 ms.get("asc").setDisabled(!cm.isSortable(index));
34088 ms.get("desc").setDisabled(!cm.isSortable(index));
34089 if(this.grid.enableColLock !== false){
34090 ms.get("lock").setDisabled(cm.isLocked(index));
34091 ms.get("unlock").setDisabled(!cm.isLocked(index));
34093 this.hmenu.show(hd, "tl-bl");
34096 handleHdOver : function(e){
34097 var hd = this.findHeaderCell(e.getTarget());
34098 if(hd && !this.headersDisabled){
34099 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34100 this.fly(hd).addClass("x-grid-hd-over");
34105 handleHdOut : function(e){
34106 var hd = this.findHeaderCell(e.getTarget());
34108 this.fly(hd).removeClass("x-grid-hd-over");
34112 handleSplitDblClick : function(e, t){
34113 var i = this.getCellIndex(t);
34114 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34115 this.autoSizeColumn(i, true);
34120 render : function(){
34123 var colCount = cm.getColumnCount();
34125 if(this.grid.monitorWindowResize === true){
34126 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34128 var header = this.renderHeaders();
34129 var body = this.templates.body.apply({rows:""});
34130 var html = this.templates.master.apply({
34133 lockedHeader: header[0],
34137 //this.updateColumns();
34139 this.grid.getGridEl().dom.innerHTML = html;
34141 this.initElements();
34143 // a kludge to fix the random scolling effect in webkit
34144 this.el.on("scroll", function() {
34145 this.el.dom.scrollTop=0; // hopefully not recursive..
34148 this.scroller.on("scroll", this.handleScroll, this);
34149 this.lockedBody.on("mousewheel", this.handleWheel, this);
34150 this.mainBody.on("mousewheel", this.handleWheel, this);
34152 this.mainHd.on("mouseover", this.handleHdOver, this);
34153 this.mainHd.on("mouseout", this.handleHdOut, this);
34154 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34155 {delegate: "."+this.splitClass});
34157 this.lockedHd.on("mouseover", this.handleHdOver, this);
34158 this.lockedHd.on("mouseout", this.handleHdOut, this);
34159 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34160 {delegate: "."+this.splitClass});
34162 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34163 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34166 this.updateSplitters();
34168 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34169 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34170 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34173 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34174 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34176 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34177 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34179 if(this.grid.enableColLock !== false){
34180 this.hmenu.add('-',
34181 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34182 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34186 this.hmenu.add('-',
34187 {id:"wider", text: this.columnsWiderText},
34188 {id:"narrow", text: this.columnsNarrowText }
34194 if(this.grid.enableColumnHide !== false){
34196 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34197 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34198 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34200 this.hmenu.add('-',
34201 {id:"columns", text: this.columnsText, menu: this.colMenu}
34204 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34206 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34209 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34210 this.dd = new Roo.grid.GridDragZone(this.grid, {
34211 ddGroup : this.grid.ddGroup || 'GridDD'
34217 for(var i = 0; i < colCount; i++){
34218 if(cm.isHidden(i)){
34219 this.hideColumn(i);
34221 if(cm.config[i].align){
34222 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34223 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34227 this.updateHeaderSortState();
34229 this.beforeInitialResize();
34232 // two part rendering gives faster view to the user
34233 this.renderPhase2.defer(1, this);
34236 renderPhase2 : function(){
34237 // render the rows now
34239 if(this.grid.autoSizeColumns){
34240 this.autoSizeColumns();
34244 beforeInitialResize : function(){
34248 onColumnSplitterMoved : function(i, w){
34249 this.userResized = true;
34250 var cm = this.grid.colModel;
34251 cm.setColumnWidth(i, w, true);
34252 var cid = cm.getColumnId(i);
34253 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34254 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34255 this.updateSplitters();
34257 this.grid.fireEvent("columnresize", i, w);
34260 syncRowHeights : function(startIndex, endIndex){
34261 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34262 startIndex = startIndex || 0;
34263 var mrows = this.getBodyTable().rows;
34264 var lrows = this.getLockedTable().rows;
34265 var len = mrows.length-1;
34266 endIndex = Math.min(endIndex || len, len);
34267 for(var i = startIndex; i <= endIndex; i++){
34268 var m = mrows[i], l = lrows[i];
34269 var h = Math.max(m.offsetHeight, l.offsetHeight);
34270 m.style.height = l.style.height = h + "px";
34275 layout : function(initialRender, is2ndPass){
34277 var auto = g.autoHeight;
34278 var scrollOffset = 16;
34279 var c = g.getGridEl(), cm = this.cm,
34280 expandCol = g.autoExpandColumn,
34282 //c.beginMeasure();
34284 if(!c.dom.offsetWidth){ // display:none?
34286 this.lockedWrap.show();
34287 this.mainWrap.show();
34292 var hasLock = this.cm.isLocked(0);
34294 var tbh = this.headerPanel.getHeight();
34295 var bbh = this.footerPanel.getHeight();
34298 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34299 var newHeight = ch + c.getBorderWidth("tb");
34301 newHeight = Math.min(g.maxHeight, newHeight);
34303 c.setHeight(newHeight);
34307 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34310 var s = this.scroller;
34312 var csize = c.getSize(true);
34314 this.el.setSize(csize.width, csize.height);
34316 this.headerPanel.setWidth(csize.width);
34317 this.footerPanel.setWidth(csize.width);
34319 var hdHeight = this.mainHd.getHeight();
34320 var vw = csize.width;
34321 var vh = csize.height - (tbh + bbh);
34325 var bt = this.getBodyTable();
34327 if(cm.getLockedCount() == cm.config.length){
34328 bt = this.getLockedTable();
34331 var ltWidth = hasLock ?
34332 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34334 var scrollHeight = bt.offsetHeight;
34335 var scrollWidth = ltWidth + bt.offsetWidth;
34336 var vscroll = false, hscroll = false;
34338 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34340 var lw = this.lockedWrap, mw = this.mainWrap;
34341 var lb = this.lockedBody, mb = this.mainBody;
34343 setTimeout(function(){
34344 var t = s.dom.offsetTop;
34345 var w = s.dom.clientWidth,
34346 h = s.dom.clientHeight;
34349 lw.setSize(ltWidth, h);
34351 mw.setLeftTop(ltWidth, t);
34352 mw.setSize(w-ltWidth, h);
34354 lb.setHeight(h-hdHeight);
34355 mb.setHeight(h-hdHeight);
34357 if(is2ndPass !== true && !gv.userResized && expandCol){
34358 // high speed resize without full column calculation
34360 var ci = cm.getIndexById(expandCol);
34362 ci = cm.findColumnIndex(expandCol);
34364 ci = Math.max(0, ci); // make sure it's got at least the first col.
34365 var expandId = cm.getColumnId(ci);
34366 var tw = cm.getTotalWidth(false);
34367 var currentWidth = cm.getColumnWidth(ci);
34368 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34369 if(currentWidth != cw){
34370 cm.setColumnWidth(ci, cw, true);
34371 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34372 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34373 gv.updateSplitters();
34374 gv.layout(false, true);
34386 onWindowResize : function(){
34387 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34393 appendFooter : function(parentEl){
34397 sortAscText : "Sort Ascending",
34398 sortDescText : "Sort Descending",
34399 lockText : "Lock Column",
34400 unlockText : "Unlock Column",
34401 columnsText : "Columns",
34403 columnsWiderText : "Wider",
34404 columnsNarrowText : "Thinner"
34408 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34409 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34410 this.proxy.el.addClass('x-grid3-col-dd');
34413 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34414 handleMouseDown : function(e){
34418 callHandleMouseDown : function(e){
34419 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34424 * Ext JS Library 1.1.1
34425 * Copyright(c) 2006-2007, Ext JS, LLC.
34427 * Originally Released Under LGPL - original licence link has changed is not relivant.
34430 * <script type="text/javascript">
34434 // This is a support class used internally by the Grid components
34435 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34437 this.view = grid.getView();
34438 this.proxy = this.view.resizeProxy;
34439 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34440 "gridSplitters" + this.grid.getGridEl().id, {
34441 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34443 this.setHandleElId(Roo.id(hd));
34444 this.setOuterHandleElId(Roo.id(hd2));
34445 this.scroll = false;
34447 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34448 fly: Roo.Element.fly,
34450 b4StartDrag : function(x, y){
34451 this.view.headersDisabled = true;
34452 this.proxy.setHeight(this.view.mainWrap.getHeight());
34453 var w = this.cm.getColumnWidth(this.cellIndex);
34454 var minw = Math.max(w-this.grid.minColumnWidth, 0);
34455 this.resetConstraints();
34456 this.setXConstraint(minw, 1000);
34457 this.setYConstraint(0, 0);
34458 this.minX = x - minw;
34459 this.maxX = x + 1000;
34461 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34465 handleMouseDown : function(e){
34466 ev = Roo.EventObject.setEvent(e);
34467 var t = this.fly(ev.getTarget());
34468 if(t.hasClass("x-grid-split")){
34469 this.cellIndex = this.view.getCellIndex(t.dom);
34470 this.split = t.dom;
34471 this.cm = this.grid.colModel;
34472 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34473 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34478 endDrag : function(e){
34479 this.view.headersDisabled = false;
34480 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34481 var diff = endX - this.startPos;
34482 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34485 autoOffset : function(){
34486 this.setDelta(0,0);
34490 * Ext JS Library 1.1.1
34491 * Copyright(c) 2006-2007, Ext JS, LLC.
34493 * Originally Released Under LGPL - original licence link has changed is not relivant.
34496 * <script type="text/javascript">
34500 // This is a support class used internally by the Grid components
34501 Roo.grid.GridDragZone = function(grid, config){
34502 this.view = grid.getView();
34503 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34504 if(this.view.lockedBody){
34505 this.setHandleElId(Roo.id(this.view.mainBody.dom));
34506 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34508 this.scroll = false;
34510 this.ddel = document.createElement('div');
34511 this.ddel.className = 'x-grid-dd-wrap';
34514 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34515 ddGroup : "GridDD",
34517 getDragData : function(e){
34518 var t = Roo.lib.Event.getTarget(e);
34519 var rowIndex = this.view.findRowIndex(t);
34520 var sm = this.grid.selModel;
34522 //Roo.log(rowIndex);
34524 if (sm.getSelectedCell) {
34525 // cell selection..
34526 if (!sm.getSelectedCell()) {
34529 if (rowIndex != sm.getSelectedCell()[0]) {
34535 if(rowIndex !== false){
34540 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34542 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34545 if (e.hasModifier()){
34546 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34549 Roo.log("getDragData");
34554 rowIndex: rowIndex,
34555 selections:sm.getSelections ? sm.getSelections() : (
34556 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34563 onInitDrag : function(e){
34564 var data = this.dragData;
34565 this.ddel.innerHTML = this.grid.getDragDropText();
34566 this.proxy.update(this.ddel);
34567 // fire start drag?
34570 afterRepair : function(){
34571 this.dragging = false;
34574 getRepairXY : function(e, data){
34578 onEndDrag : function(data, e){
34582 onValidDrop : function(dd, e, id){
34587 beforeInvalidDrop : function(e, id){
34592 * Ext JS Library 1.1.1
34593 * Copyright(c) 2006-2007, Ext JS, LLC.
34595 * Originally Released Under LGPL - original licence link has changed is not relivant.
34598 * <script type="text/javascript">
34603 * @class Roo.grid.ColumnModel
34604 * @extends Roo.util.Observable
34605 * This is the default implementation of a ColumnModel used by the Grid. It defines
34606 * the columns in the grid.
34609 var colModel = new Roo.grid.ColumnModel([
34610 {header: "Ticker", width: 60, sortable: true, locked: true},
34611 {header: "Company Name", width: 150, sortable: true},
34612 {header: "Market Cap.", width: 100, sortable: true},
34613 {header: "$ Sales", width: 100, sortable: true, renderer: money},
34614 {header: "Employees", width: 100, sortable: true, resizable: false}
34619 * The config options listed for this class are options which may appear in each
34620 * individual column definition.
34621 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34623 * @param {Object} config An Array of column config objects. See this class's
34624 * config objects for details.
34626 Roo.grid.ColumnModel = function(config){
34628 * The config passed into the constructor
34630 this.config = config;
34633 // if no id, create one
34634 // if the column does not have a dataIndex mapping,
34635 // map it to the order it is in the config
34636 for(var i = 0, len = config.length; i < len; i++){
34638 if(typeof c.dataIndex == "undefined"){
34641 if(typeof c.renderer == "string"){
34642 c.renderer = Roo.util.Format[c.renderer];
34644 if(typeof c.id == "undefined"){
34647 if(c.editor && c.editor.xtype){
34648 c.editor = Roo.factory(c.editor, Roo.grid);
34650 if(c.editor && c.editor.isFormField){
34651 c.editor = new Roo.grid.GridEditor(c.editor);
34653 this.lookup[c.id] = c;
34657 * The width of columns which have no width specified (defaults to 100)
34660 this.defaultWidth = 100;
34663 * Default sortable of columns which have no sortable specified (defaults to false)
34666 this.defaultSortable = false;
34670 * @event widthchange
34671 * Fires when the width of a column changes.
34672 * @param {ColumnModel} this
34673 * @param {Number} columnIndex The column index
34674 * @param {Number} newWidth The new width
34676 "widthchange": true,
34678 * @event headerchange
34679 * Fires when the text of a header changes.
34680 * @param {ColumnModel} this
34681 * @param {Number} columnIndex The column index
34682 * @param {Number} newText The new header text
34684 "headerchange": true,
34686 * @event hiddenchange
34687 * Fires when a column is hidden or "unhidden".
34688 * @param {ColumnModel} this
34689 * @param {Number} columnIndex The column index
34690 * @param {Boolean} hidden true if hidden, false otherwise
34692 "hiddenchange": true,
34694 * @event columnmoved
34695 * Fires when a column is moved.
34696 * @param {ColumnModel} this
34697 * @param {Number} oldIndex
34698 * @param {Number} newIndex
34700 "columnmoved" : true,
34702 * @event columlockchange
34703 * Fires when a column's locked state is changed
34704 * @param {ColumnModel} this
34705 * @param {Number} colIndex
34706 * @param {Boolean} locked true if locked
34708 "columnlockchange" : true
34710 Roo.grid.ColumnModel.superclass.constructor.call(this);
34712 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34714 * @cfg {String} header The header text to display in the Grid view.
34717 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34718 * {@link Roo.data.Record} definition from which to draw the column's value. If not
34719 * specified, the column's index is used as an index into the Record's data Array.
34722 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34723 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34726 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34727 * Defaults to the value of the {@link #defaultSortable} property.
34728 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34731 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
34734 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
34737 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34740 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34743 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34744 * given the cell's data value. See {@link #setRenderer}. If not specified, the
34745 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34746 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34749 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
34752 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
34755 * @cfg {String} cursor (Optional)
34758 * @cfg {String} tooltip (Optional)
34761 * @cfg {Number} xs (Optional)
34764 * @cfg {Number} sm (Optional)
34767 * @cfg {Number} md (Optional)
34770 * @cfg {Number} lg (Optional)
34773 * Returns the id of the column at the specified index.
34774 * @param {Number} index The column index
34775 * @return {String} the id
34777 getColumnId : function(index){
34778 return this.config[index].id;
34782 * Returns the column for a specified id.
34783 * @param {String} id The column id
34784 * @return {Object} the column
34786 getColumnById : function(id){
34787 return this.lookup[id];
34792 * Returns the column for a specified dataIndex.
34793 * @param {String} dataIndex The column dataIndex
34794 * @return {Object|Boolean} the column or false if not found
34796 getColumnByDataIndex: function(dataIndex){
34797 var index = this.findColumnIndex(dataIndex);
34798 return index > -1 ? this.config[index] : false;
34802 * Returns the index for a specified column id.
34803 * @param {String} id The column id
34804 * @return {Number} the index, or -1 if not found
34806 getIndexById : function(id){
34807 for(var i = 0, len = this.config.length; i < len; i++){
34808 if(this.config[i].id == id){
34816 * Returns the index for a specified column dataIndex.
34817 * @param {String} dataIndex The column dataIndex
34818 * @return {Number} the index, or -1 if not found
34821 findColumnIndex : function(dataIndex){
34822 for(var i = 0, len = this.config.length; i < len; i++){
34823 if(this.config[i].dataIndex == dataIndex){
34831 moveColumn : function(oldIndex, newIndex){
34832 var c = this.config[oldIndex];
34833 this.config.splice(oldIndex, 1);
34834 this.config.splice(newIndex, 0, c);
34835 this.dataMap = null;
34836 this.fireEvent("columnmoved", this, oldIndex, newIndex);
34839 isLocked : function(colIndex){
34840 return this.config[colIndex].locked === true;
34843 setLocked : function(colIndex, value, suppressEvent){
34844 if(this.isLocked(colIndex) == value){
34847 this.config[colIndex].locked = value;
34848 if(!suppressEvent){
34849 this.fireEvent("columnlockchange", this, colIndex, value);
34853 getTotalLockedWidth : function(){
34854 var totalWidth = 0;
34855 for(var i = 0; i < this.config.length; i++){
34856 if(this.isLocked(i) && !this.isHidden(i)){
34857 this.totalWidth += this.getColumnWidth(i);
34863 getLockedCount : function(){
34864 for(var i = 0, len = this.config.length; i < len; i++){
34865 if(!this.isLocked(i)){
34870 return this.config.length;
34874 * Returns the number of columns.
34877 getColumnCount : function(visibleOnly){
34878 if(visibleOnly === true){
34880 for(var i = 0, len = this.config.length; i < len; i++){
34881 if(!this.isHidden(i)){
34887 return this.config.length;
34891 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34892 * @param {Function} fn
34893 * @param {Object} scope (optional)
34894 * @return {Array} result
34896 getColumnsBy : function(fn, scope){
34898 for(var i = 0, len = this.config.length; i < len; i++){
34899 var c = this.config[i];
34900 if(fn.call(scope||this, c, i) === true){
34908 * Returns true if the specified column is sortable.
34909 * @param {Number} col The column index
34910 * @return {Boolean}
34912 isSortable : function(col){
34913 if(typeof this.config[col].sortable == "undefined"){
34914 return this.defaultSortable;
34916 return this.config[col].sortable;
34920 * Returns the rendering (formatting) function defined for the column.
34921 * @param {Number} col The column index.
34922 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34924 getRenderer : function(col){
34925 if(!this.config[col].renderer){
34926 return Roo.grid.ColumnModel.defaultRenderer;
34928 return this.config[col].renderer;
34932 * Sets the rendering (formatting) function for a column.
34933 * @param {Number} col The column index
34934 * @param {Function} fn The function to use to process the cell's raw data
34935 * to return HTML markup for the grid view. The render function is called with
34936 * the following parameters:<ul>
34937 * <li>Data value.</li>
34938 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34939 * <li>css A CSS style string to apply to the table cell.</li>
34940 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34941 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34942 * <li>Row index</li>
34943 * <li>Column index</li>
34944 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34946 setRenderer : function(col, fn){
34947 this.config[col].renderer = fn;
34951 * Returns the width for the specified column.
34952 * @param {Number} col The column index
34955 getColumnWidth : function(col){
34956 return this.config[col].width * 1 || this.defaultWidth;
34960 * Sets the width for a column.
34961 * @param {Number} col The column index
34962 * @param {Number} width The new width
34964 setColumnWidth : function(col, width, suppressEvent){
34965 this.config[col].width = width;
34966 this.totalWidth = null;
34967 if(!suppressEvent){
34968 this.fireEvent("widthchange", this, col, width);
34973 * Returns the total width of all columns.
34974 * @param {Boolean} includeHidden True to include hidden column widths
34977 getTotalWidth : function(includeHidden){
34978 if(!this.totalWidth){
34979 this.totalWidth = 0;
34980 for(var i = 0, len = this.config.length; i < len; i++){
34981 if(includeHidden || !this.isHidden(i)){
34982 this.totalWidth += this.getColumnWidth(i);
34986 return this.totalWidth;
34990 * Returns the header for the specified column.
34991 * @param {Number} col The column index
34994 getColumnHeader : function(col){
34995 return this.config[col].header;
34999 * Sets the header for a column.
35000 * @param {Number} col The column index
35001 * @param {String} header The new header
35003 setColumnHeader : function(col, header){
35004 this.config[col].header = header;
35005 this.fireEvent("headerchange", this, col, header);
35009 * Returns the tooltip for the specified column.
35010 * @param {Number} col The column index
35013 getColumnTooltip : function(col){
35014 return this.config[col].tooltip;
35017 * Sets the tooltip for a column.
35018 * @param {Number} col The column index
35019 * @param {String} tooltip The new tooltip
35021 setColumnTooltip : function(col, tooltip){
35022 this.config[col].tooltip = tooltip;
35026 * Returns the dataIndex for the specified column.
35027 * @param {Number} col The column index
35030 getDataIndex : function(col){
35031 return this.config[col].dataIndex;
35035 * Sets the dataIndex for a column.
35036 * @param {Number} col The column index
35037 * @param {Number} dataIndex The new dataIndex
35039 setDataIndex : function(col, dataIndex){
35040 this.config[col].dataIndex = dataIndex;
35046 * Returns true if the cell is editable.
35047 * @param {Number} colIndex The column index
35048 * @param {Number} rowIndex The row index - this is nto actually used..?
35049 * @return {Boolean}
35051 isCellEditable : function(colIndex, rowIndex){
35052 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35056 * Returns the editor defined for the cell/column.
35057 * return false or null to disable editing.
35058 * @param {Number} colIndex The column index
35059 * @param {Number} rowIndex The row index
35062 getCellEditor : function(colIndex, rowIndex){
35063 return this.config[colIndex].editor;
35067 * Sets if a column is editable.
35068 * @param {Number} col The column index
35069 * @param {Boolean} editable True if the column is editable
35071 setEditable : function(col, editable){
35072 this.config[col].editable = editable;
35077 * Returns true if the column is hidden.
35078 * @param {Number} colIndex The column index
35079 * @return {Boolean}
35081 isHidden : function(colIndex){
35082 return this.config[colIndex].hidden;
35087 * Returns true if the column width cannot be changed
35089 isFixed : function(colIndex){
35090 return this.config[colIndex].fixed;
35094 * Returns true if the column can be resized
35095 * @return {Boolean}
35097 isResizable : function(colIndex){
35098 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35101 * Sets if a column is hidden.
35102 * @param {Number} colIndex The column index
35103 * @param {Boolean} hidden True if the column is hidden
35105 setHidden : function(colIndex, hidden){
35106 this.config[colIndex].hidden = hidden;
35107 this.totalWidth = null;
35108 this.fireEvent("hiddenchange", this, colIndex, hidden);
35112 * Sets the editor for a column.
35113 * @param {Number} col The column index
35114 * @param {Object} editor The editor object
35116 setEditor : function(col, editor){
35117 this.config[col].editor = editor;
35121 Roo.grid.ColumnModel.defaultRenderer = function(value)
35123 if(typeof value == "object") {
35126 if(typeof value == "string" && value.length < 1){
35130 return String.format("{0}", value);
35133 // Alias for backwards compatibility
35134 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35137 * Ext JS Library 1.1.1
35138 * Copyright(c) 2006-2007, Ext JS, LLC.
35140 * Originally Released Under LGPL - original licence link has changed is not relivant.
35143 * <script type="text/javascript">
35147 * @class Roo.grid.AbstractSelectionModel
35148 * @extends Roo.util.Observable
35149 * Abstract base class for grid SelectionModels. It provides the interface that should be
35150 * implemented by descendant classes. This class should not be directly instantiated.
35153 Roo.grid.AbstractSelectionModel = function(){
35154 this.locked = false;
35155 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35158 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35159 /** @ignore Called by the grid automatically. Do not call directly. */
35160 init : function(grid){
35166 * Locks the selections.
35169 this.locked = true;
35173 * Unlocks the selections.
35175 unlock : function(){
35176 this.locked = false;
35180 * Returns true if the selections are locked.
35181 * @return {Boolean}
35183 isLocked : function(){
35184 return this.locked;
35188 * Ext JS Library 1.1.1
35189 * Copyright(c) 2006-2007, Ext JS, LLC.
35191 * Originally Released Under LGPL - original licence link has changed is not relivant.
35194 * <script type="text/javascript">
35197 * @extends Roo.grid.AbstractSelectionModel
35198 * @class Roo.grid.RowSelectionModel
35199 * The default SelectionModel used by {@link Roo.grid.Grid}.
35200 * It supports multiple selections and keyboard selection/navigation.
35202 * @param {Object} config
35204 Roo.grid.RowSelectionModel = function(config){
35205 Roo.apply(this, config);
35206 this.selections = new Roo.util.MixedCollection(false, function(o){
35211 this.lastActive = false;
35215 * @event selectionchange
35216 * Fires when the selection changes
35217 * @param {SelectionModel} this
35219 "selectionchange" : true,
35221 * @event afterselectionchange
35222 * Fires after the selection changes (eg. by key press or clicking)
35223 * @param {SelectionModel} this
35225 "afterselectionchange" : true,
35227 * @event beforerowselect
35228 * Fires when a row is selected being selected, return false to cancel.
35229 * @param {SelectionModel} this
35230 * @param {Number} rowIndex The selected index
35231 * @param {Boolean} keepExisting False if other selections will be cleared
35233 "beforerowselect" : true,
35236 * Fires when a row is selected.
35237 * @param {SelectionModel} this
35238 * @param {Number} rowIndex The selected index
35239 * @param {Roo.data.Record} r The record
35241 "rowselect" : true,
35243 * @event rowdeselect
35244 * Fires when a row is deselected.
35245 * @param {SelectionModel} this
35246 * @param {Number} rowIndex The selected index
35248 "rowdeselect" : true
35250 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35251 this.locked = false;
35254 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
35256 * @cfg {Boolean} singleSelect
35257 * True to allow selection of only one row at a time (defaults to false)
35259 singleSelect : false,
35262 initEvents : function(){
35264 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35265 this.grid.on("mousedown", this.handleMouseDown, this);
35266 }else{ // allow click to work like normal
35267 this.grid.on("rowclick", this.handleDragableRowClick, this);
35270 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35271 "up" : function(e){
35273 this.selectPrevious(e.shiftKey);
35274 }else if(this.last !== false && this.lastActive !== false){
35275 var last = this.last;
35276 this.selectRange(this.last, this.lastActive-1);
35277 this.grid.getView().focusRow(this.lastActive);
35278 if(last !== false){
35282 this.selectFirstRow();
35284 this.fireEvent("afterselectionchange", this);
35286 "down" : function(e){
35288 this.selectNext(e.shiftKey);
35289 }else if(this.last !== false && this.lastActive !== false){
35290 var last = this.last;
35291 this.selectRange(this.last, this.lastActive+1);
35292 this.grid.getView().focusRow(this.lastActive);
35293 if(last !== false){
35297 this.selectFirstRow();
35299 this.fireEvent("afterselectionchange", this);
35304 var view = this.grid.view;
35305 view.on("refresh", this.onRefresh, this);
35306 view.on("rowupdated", this.onRowUpdated, this);
35307 view.on("rowremoved", this.onRemove, this);
35311 onRefresh : function(){
35312 var ds = this.grid.dataSource, i, v = this.grid.view;
35313 var s = this.selections;
35314 s.each(function(r){
35315 if((i = ds.indexOfId(r.id)) != -1){
35317 s.add(ds.getAt(i)); // updating the selection relate data
35325 onRemove : function(v, index, r){
35326 this.selections.remove(r);
35330 onRowUpdated : function(v, index, r){
35331 if(this.isSelected(r)){
35332 v.onRowSelect(index);
35338 * @param {Array} records The records to select
35339 * @param {Boolean} keepExisting (optional) True to keep existing selections
35341 selectRecords : function(records, keepExisting){
35343 this.clearSelections();
35345 var ds = this.grid.dataSource;
35346 for(var i = 0, len = records.length; i < len; i++){
35347 this.selectRow(ds.indexOf(records[i]), true);
35352 * Gets the number of selected rows.
35355 getCount : function(){
35356 return this.selections.length;
35360 * Selects the first row in the grid.
35362 selectFirstRow : function(){
35367 * Select the last row.
35368 * @param {Boolean} keepExisting (optional) True to keep existing selections
35370 selectLastRow : function(keepExisting){
35371 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35375 * Selects the row immediately following the last selected row.
35376 * @param {Boolean} keepExisting (optional) True to keep existing selections
35378 selectNext : function(keepExisting){
35379 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35380 this.selectRow(this.last+1, keepExisting);
35381 this.grid.getView().focusRow(this.last);
35386 * Selects the row that precedes the last selected row.
35387 * @param {Boolean} keepExisting (optional) True to keep existing selections
35389 selectPrevious : function(keepExisting){
35391 this.selectRow(this.last-1, keepExisting);
35392 this.grid.getView().focusRow(this.last);
35397 * Returns the selected records
35398 * @return {Array} Array of selected records
35400 getSelections : function(){
35401 return [].concat(this.selections.items);
35405 * Returns the first selected record.
35408 getSelected : function(){
35409 return this.selections.itemAt(0);
35414 * Clears all selections.
35416 clearSelections : function(fast){
35421 var ds = this.grid.dataSource;
35422 var s = this.selections;
35423 s.each(function(r){
35424 this.deselectRow(ds.indexOfId(r.id));
35428 this.selections.clear();
35435 * Selects all rows.
35437 selectAll : function(){
35441 this.selections.clear();
35442 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35443 this.selectRow(i, true);
35448 * Returns True if there is a selection.
35449 * @return {Boolean}
35451 hasSelection : function(){
35452 return this.selections.length > 0;
35456 * Returns True if the specified row is selected.
35457 * @param {Number/Record} record The record or index of the record to check
35458 * @return {Boolean}
35460 isSelected : function(index){
35461 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35462 return (r && this.selections.key(r.id) ? true : false);
35466 * Returns True if the specified record id is selected.
35467 * @param {String} id The id of record to check
35468 * @return {Boolean}
35470 isIdSelected : function(id){
35471 return (this.selections.key(id) ? true : false);
35475 handleMouseDown : function(e, t){
35476 var view = this.grid.getView(), rowIndex;
35477 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35480 if(e.shiftKey && this.last !== false){
35481 var last = this.last;
35482 this.selectRange(last, rowIndex, e.ctrlKey);
35483 this.last = last; // reset the last
35484 view.focusRow(rowIndex);
35486 var isSelected = this.isSelected(rowIndex);
35487 if(e.button !== 0 && isSelected){
35488 view.focusRow(rowIndex);
35489 }else if(e.ctrlKey && isSelected){
35490 this.deselectRow(rowIndex);
35491 }else if(!isSelected){
35492 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35493 view.focusRow(rowIndex);
35496 this.fireEvent("afterselectionchange", this);
35499 handleDragableRowClick : function(grid, rowIndex, e)
35501 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35502 this.selectRow(rowIndex, false);
35503 grid.view.focusRow(rowIndex);
35504 this.fireEvent("afterselectionchange", this);
35509 * Selects multiple rows.
35510 * @param {Array} rows Array of the indexes of the row to select
35511 * @param {Boolean} keepExisting (optional) True to keep existing selections
35513 selectRows : function(rows, keepExisting){
35515 this.clearSelections();
35517 for(var i = 0, len = rows.length; i < len; i++){
35518 this.selectRow(rows[i], true);
35523 * Selects a range of rows. All rows in between startRow and endRow are also selected.
35524 * @param {Number} startRow The index of the first row in the range
35525 * @param {Number} endRow The index of the last row in the range
35526 * @param {Boolean} keepExisting (optional) True to retain existing selections
35528 selectRange : function(startRow, endRow, keepExisting){
35533 this.clearSelections();
35535 if(startRow <= endRow){
35536 for(var i = startRow; i <= endRow; i++){
35537 this.selectRow(i, true);
35540 for(var i = startRow; i >= endRow; i--){
35541 this.selectRow(i, true);
35547 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35548 * @param {Number} startRow The index of the first row in the range
35549 * @param {Number} endRow The index of the last row in the range
35551 deselectRange : function(startRow, endRow, preventViewNotify){
35555 for(var i = startRow; i <= endRow; i++){
35556 this.deselectRow(i, preventViewNotify);
35562 * @param {Number} row The index of the row to select
35563 * @param {Boolean} keepExisting (optional) True to keep existing selections
35565 selectRow : function(index, keepExisting, preventViewNotify){
35566 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35569 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35570 if(!keepExisting || this.singleSelect){
35571 this.clearSelections();
35573 var r = this.grid.dataSource.getAt(index);
35574 this.selections.add(r);
35575 this.last = this.lastActive = index;
35576 if(!preventViewNotify){
35577 this.grid.getView().onRowSelect(index);
35579 this.fireEvent("rowselect", this, index, r);
35580 this.fireEvent("selectionchange", this);
35586 * @param {Number} row The index of the row to deselect
35588 deselectRow : function(index, preventViewNotify){
35592 if(this.last == index){
35595 if(this.lastActive == index){
35596 this.lastActive = false;
35598 var r = this.grid.dataSource.getAt(index);
35599 this.selections.remove(r);
35600 if(!preventViewNotify){
35601 this.grid.getView().onRowDeselect(index);
35603 this.fireEvent("rowdeselect", this, index);
35604 this.fireEvent("selectionchange", this);
35608 restoreLast : function(){
35610 this.last = this._last;
35615 acceptsNav : function(row, col, cm){
35616 return !cm.isHidden(col) && cm.isCellEditable(col, row);
35620 onEditorKey : function(field, e){
35621 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35626 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35628 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35630 }else if(k == e.ENTER && !e.ctrlKey){
35634 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35636 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35638 }else if(k == e.ESC){
35642 g.startEditing(newCell[0], newCell[1]);
35647 * Ext JS Library 1.1.1
35648 * Copyright(c) 2006-2007, Ext JS, LLC.
35650 * Originally Released Under LGPL - original licence link has changed is not relivant.
35653 * <script type="text/javascript">
35656 * @class Roo.grid.CellSelectionModel
35657 * @extends Roo.grid.AbstractSelectionModel
35658 * This class provides the basic implementation for cell selection in a grid.
35660 * @param {Object} config The object containing the configuration of this model.
35661 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35663 Roo.grid.CellSelectionModel = function(config){
35664 Roo.apply(this, config);
35666 this.selection = null;
35670 * @event beforerowselect
35671 * Fires before a cell is selected.
35672 * @param {SelectionModel} this
35673 * @param {Number} rowIndex The selected row index
35674 * @param {Number} colIndex The selected cell index
35676 "beforecellselect" : true,
35678 * @event cellselect
35679 * Fires when a cell is selected.
35680 * @param {SelectionModel} this
35681 * @param {Number} rowIndex The selected row index
35682 * @param {Number} colIndex The selected cell index
35684 "cellselect" : true,
35686 * @event selectionchange
35687 * Fires when the active selection changes.
35688 * @param {SelectionModel} this
35689 * @param {Object} selection null for no selection or an object (o) with two properties
35691 <li>o.record: the record object for the row the selection is in</li>
35692 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35695 "selectionchange" : true,
35698 * Fires when the tab (or enter) was pressed on the last editable cell
35699 * You can use this to trigger add new row.
35700 * @param {SelectionModel} this
35704 * @event beforeeditnext
35705 * Fires before the next editable sell is made active
35706 * You can use this to skip to another cell or fire the tabend
35707 * if you set cell to false
35708 * @param {Object} eventdata object : { cell : [ row, col ] }
35710 "beforeeditnext" : true
35712 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35715 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
35717 enter_is_tab: false,
35720 initEvents : function(){
35721 this.grid.on("mousedown", this.handleMouseDown, this);
35722 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35723 var view = this.grid.view;
35724 view.on("refresh", this.onViewChange, this);
35725 view.on("rowupdated", this.onRowUpdated, this);
35726 view.on("beforerowremoved", this.clearSelections, this);
35727 view.on("beforerowsinserted", this.clearSelections, this);
35728 if(this.grid.isEditor){
35729 this.grid.on("beforeedit", this.beforeEdit, this);
35734 beforeEdit : function(e){
35735 this.select(e.row, e.column, false, true, e.record);
35739 onRowUpdated : function(v, index, r){
35740 if(this.selection && this.selection.record == r){
35741 v.onCellSelect(index, this.selection.cell[1]);
35746 onViewChange : function(){
35747 this.clearSelections(true);
35751 * Returns the currently selected cell,.
35752 * @return {Array} The selected cell (row, column) or null if none selected.
35754 getSelectedCell : function(){
35755 return this.selection ? this.selection.cell : null;
35759 * Clears all selections.
35760 * @param {Boolean} true to prevent the gridview from being notified about the change.
35762 clearSelections : function(preventNotify){
35763 var s = this.selection;
35765 if(preventNotify !== true){
35766 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35768 this.selection = null;
35769 this.fireEvent("selectionchange", this, null);
35774 * Returns true if there is a selection.
35775 * @return {Boolean}
35777 hasSelection : function(){
35778 return this.selection ? true : false;
35782 handleMouseDown : function(e, t){
35783 var v = this.grid.getView();
35784 if(this.isLocked()){
35787 var row = v.findRowIndex(t);
35788 var cell = v.findCellIndex(t);
35789 if(row !== false && cell !== false){
35790 this.select(row, cell);
35796 * @param {Number} rowIndex
35797 * @param {Number} collIndex
35799 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35800 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35801 this.clearSelections();
35802 r = r || this.grid.dataSource.getAt(rowIndex);
35805 cell : [rowIndex, colIndex]
35807 if(!preventViewNotify){
35808 var v = this.grid.getView();
35809 v.onCellSelect(rowIndex, colIndex);
35810 if(preventFocus !== true){
35811 v.focusCell(rowIndex, colIndex);
35814 this.fireEvent("cellselect", this, rowIndex, colIndex);
35815 this.fireEvent("selectionchange", this, this.selection);
35820 isSelectable : function(rowIndex, colIndex, cm){
35821 return !cm.isHidden(colIndex);
35825 handleKeyDown : function(e){
35826 //Roo.log('Cell Sel Model handleKeyDown');
35827 if(!e.isNavKeyPress()){
35830 var g = this.grid, s = this.selection;
35833 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
35835 this.select(cell[0], cell[1]);
35840 var walk = function(row, col, step){
35841 return g.walkCells(row, col, step, sm.isSelectable, sm);
35843 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35850 // handled by onEditorKey
35851 if (g.isEditor && g.editing) {
35855 newCell = walk(r, c-1, -1);
35857 newCell = walk(r, c+1, 1);
35862 newCell = walk(r+1, c, 1);
35866 newCell = walk(r-1, c, -1);
35870 newCell = walk(r, c+1, 1);
35874 newCell = walk(r, c-1, -1);
35879 if(g.isEditor && !g.editing){
35880 g.startEditing(r, c);
35889 this.select(newCell[0], newCell[1]);
35895 acceptsNav : function(row, col, cm){
35896 return !cm.isHidden(col) && cm.isCellEditable(col, row);
35900 * @param {Number} field (not used) - as it's normally used as a listener
35901 * @param {Number} e - event - fake it by using
35903 * var e = Roo.EventObjectImpl.prototype;
35904 * e.keyCode = e.TAB
35908 onEditorKey : function(field, e){
35910 var k = e.getKey(),
35913 ed = g.activeEditor,
35915 ///Roo.log('onEditorKey' + k);
35918 if (this.enter_is_tab && k == e.ENTER) {
35924 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35926 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35932 } else if(k == e.ENTER && !e.ctrlKey){
35935 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35937 } else if(k == e.ESC){
35942 var ecall = { cell : newCell, forward : forward };
35943 this.fireEvent('beforeeditnext', ecall );
35944 newCell = ecall.cell;
35945 forward = ecall.forward;
35949 //Roo.log('next cell after edit');
35950 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35951 } else if (forward) {
35952 // tabbed past last
35953 this.fireEvent.defer(100, this, ['tabend',this]);
35958 * Ext JS Library 1.1.1
35959 * Copyright(c) 2006-2007, Ext JS, LLC.
35961 * Originally Released Under LGPL - original licence link has changed is not relivant.
35964 * <script type="text/javascript">
35968 * @class Roo.grid.EditorGrid
35969 * @extends Roo.grid.Grid
35970 * Class for creating and editable grid.
35971 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35972 * The container MUST have some type of size defined for the grid to fill. The container will be
35973 * automatically set to position relative if it isn't already.
35974 * @param {Object} dataSource The data model to bind to
35975 * @param {Object} colModel The column model with info about this grid's columns
35977 Roo.grid.EditorGrid = function(container, config){
35978 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35979 this.getGridEl().addClass("xedit-grid");
35981 if(!this.selModel){
35982 this.selModel = new Roo.grid.CellSelectionModel();
35985 this.activeEditor = null;
35989 * @event beforeedit
35990 * Fires before cell editing is triggered. The edit event object has the following properties <br />
35991 * <ul style="padding:5px;padding-left:16px;">
35992 * <li>grid - This grid</li>
35993 * <li>record - The record being edited</li>
35994 * <li>field - The field name being edited</li>
35995 * <li>value - The value for the field being edited.</li>
35996 * <li>row - The grid row index</li>
35997 * <li>column - The grid column index</li>
35998 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36000 * @param {Object} e An edit event (see above for description)
36002 "beforeedit" : true,
36005 * Fires after a cell is edited. <br />
36006 * <ul style="padding:5px;padding-left:16px;">
36007 * <li>grid - This grid</li>
36008 * <li>record - The record being edited</li>
36009 * <li>field - The field name being edited</li>
36010 * <li>value - The value being set</li>
36011 * <li>originalValue - The original value for the field, before the edit.</li>
36012 * <li>row - The grid row index</li>
36013 * <li>column - The grid column index</li>
36015 * @param {Object} e An edit event (see above for description)
36017 "afteredit" : true,
36019 * @event validateedit
36020 * Fires after a cell is edited, but before the value is set in the record.
36021 * You can use this to modify the value being set in the field, Return false
36022 * to cancel the change. The edit event object has the following properties <br />
36023 * <ul style="padding:5px;padding-left:16px;">
36024 * <li>editor - This editor</li>
36025 * <li>grid - This grid</li>
36026 * <li>record - The record being edited</li>
36027 * <li>field - The field name being edited</li>
36028 * <li>value - The value being set</li>
36029 * <li>originalValue - The original value for the field, before the edit.</li>
36030 * <li>row - The grid row index</li>
36031 * <li>column - The grid column index</li>
36032 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36034 * @param {Object} e An edit event (see above for description)
36036 "validateedit" : true
36038 this.on("bodyscroll", this.stopEditing, this);
36039 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36042 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36044 * @cfg {Number} clicksToEdit
36045 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36052 trackMouseOver: false, // causes very odd FF errors
36054 onCellDblClick : function(g, row, col){
36055 this.startEditing(row, col);
36058 onEditComplete : function(ed, value, startValue){
36059 this.editing = false;
36060 this.activeEditor = null;
36061 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36063 var field = this.colModel.getDataIndex(ed.col);
36068 originalValue: startValue,
36075 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36078 if(String(value) !== String(startValue)){
36080 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36081 r.set(field, e.value);
36082 // if we are dealing with a combo box..
36083 // then we also set the 'name' colum to be the displayField
36084 if (ed.field.displayField && ed.field.name) {
36085 r.set(ed.field.name, ed.field.el.dom.value);
36088 delete e.cancel; //?? why!!!
36089 this.fireEvent("afteredit", e);
36092 this.fireEvent("afteredit", e); // always fire it!
36094 this.view.focusCell(ed.row, ed.col);
36098 * Starts editing the specified for the specified row/column
36099 * @param {Number} rowIndex
36100 * @param {Number} colIndex
36102 startEditing : function(row, col){
36103 this.stopEditing();
36104 if(this.colModel.isCellEditable(col, row)){
36105 this.view.ensureVisible(row, col, true);
36107 var r = this.dataSource.getAt(row);
36108 var field = this.colModel.getDataIndex(col);
36109 var cell = Roo.get(this.view.getCell(row,col));
36114 value: r.data[field],
36119 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36120 this.editing = true;
36121 var ed = this.colModel.getCellEditor(col, row);
36127 ed.render(ed.parentEl || document.body);
36133 (function(){ // complex but required for focus issues in safari, ie and opera
36137 ed.on("complete", this.onEditComplete, this, {single: true});
36138 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36139 this.activeEditor = ed;
36140 var v = r.data[field];
36141 ed.startEdit(this.view.getCell(row, col), v);
36142 // combo's with 'displayField and name set
36143 if (ed.field.displayField && ed.field.name) {
36144 ed.field.el.dom.value = r.data[ed.field.name];
36148 }).defer(50, this);
36154 * Stops any active editing
36156 stopEditing : function(){
36157 if(this.activeEditor){
36158 this.activeEditor.completeEdit();
36160 this.activeEditor = null;
36164 * Called to get grid's drag proxy text, by default returns this.ddText.
36167 getDragDropText : function(){
36168 var count = this.selModel.getSelectedCell() ? 1 : 0;
36169 return String.format(this.ddText, count, count == 1 ? '' : 's');
36174 * Ext JS Library 1.1.1
36175 * Copyright(c) 2006-2007, Ext JS, LLC.
36177 * Originally Released Under LGPL - original licence link has changed is not relivant.
36180 * <script type="text/javascript">
36183 // private - not really -- you end up using it !
36184 // This is a support class used internally by the Grid components
36187 * @class Roo.grid.GridEditor
36188 * @extends Roo.Editor
36189 * Class for creating and editable grid elements.
36190 * @param {Object} config any settings (must include field)
36192 Roo.grid.GridEditor = function(field, config){
36193 if (!config && field.field) {
36195 field = Roo.factory(config.field, Roo.form);
36197 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36198 field.monitorTab = false;
36201 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36204 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36207 alignment: "tl-tl",
36210 cls: "x-small-editor x-grid-editor",
36215 * Ext JS Library 1.1.1
36216 * Copyright(c) 2006-2007, Ext JS, LLC.
36218 * Originally Released Under LGPL - original licence link has changed is not relivant.
36221 * <script type="text/javascript">
36226 Roo.grid.PropertyRecord = Roo.data.Record.create([
36227 {name:'name',type:'string'}, 'value'
36231 Roo.grid.PropertyStore = function(grid, source){
36233 this.store = new Roo.data.Store({
36234 recordType : Roo.grid.PropertyRecord
36236 this.store.on('update', this.onUpdate, this);
36238 this.setSource(source);
36240 Roo.grid.PropertyStore.superclass.constructor.call(this);
36245 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36246 setSource : function(o){
36248 this.store.removeAll();
36251 if(this.isEditableValue(o[k])){
36252 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36255 this.store.loadRecords({records: data}, {}, true);
36258 onUpdate : function(ds, record, type){
36259 if(type == Roo.data.Record.EDIT){
36260 var v = record.data['value'];
36261 var oldValue = record.modified['value'];
36262 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36263 this.source[record.id] = v;
36265 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36272 getProperty : function(row){
36273 return this.store.getAt(row);
36276 isEditableValue: function(val){
36277 if(val && val instanceof Date){
36279 }else if(typeof val == 'object' || typeof val == 'function'){
36285 setValue : function(prop, value){
36286 this.source[prop] = value;
36287 this.store.getById(prop).set('value', value);
36290 getSource : function(){
36291 return this.source;
36295 Roo.grid.PropertyColumnModel = function(grid, store){
36298 g.PropertyColumnModel.superclass.constructor.call(this, [
36299 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36300 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36302 this.store = store;
36303 this.bselect = Roo.DomHelper.append(document.body, {
36304 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36305 {tag: 'option', value: 'true', html: 'true'},
36306 {tag: 'option', value: 'false', html: 'false'}
36309 Roo.id(this.bselect);
36312 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36313 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36314 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36315 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36316 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36318 this.renderCellDelegate = this.renderCell.createDelegate(this);
36319 this.renderPropDelegate = this.renderProp.createDelegate(this);
36322 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36326 valueText : 'Value',
36328 dateFormat : 'm/j/Y',
36331 renderDate : function(dateVal){
36332 return dateVal.dateFormat(this.dateFormat);
36335 renderBool : function(bVal){
36336 return bVal ? 'true' : 'false';
36339 isCellEditable : function(colIndex, rowIndex){
36340 return colIndex == 1;
36343 getRenderer : function(col){
36345 this.renderCellDelegate : this.renderPropDelegate;
36348 renderProp : function(v){
36349 return this.getPropertyName(v);
36352 renderCell : function(val){
36354 if(val instanceof Date){
36355 rv = this.renderDate(val);
36356 }else if(typeof val == 'boolean'){
36357 rv = this.renderBool(val);
36359 return Roo.util.Format.htmlEncode(rv);
36362 getPropertyName : function(name){
36363 var pn = this.grid.propertyNames;
36364 return pn && pn[name] ? pn[name] : name;
36367 getCellEditor : function(colIndex, rowIndex){
36368 var p = this.store.getProperty(rowIndex);
36369 var n = p.data['name'], val = p.data['value'];
36371 if(typeof(this.grid.customEditors[n]) == 'string'){
36372 return this.editors[this.grid.customEditors[n]];
36374 if(typeof(this.grid.customEditors[n]) != 'undefined'){
36375 return this.grid.customEditors[n];
36377 if(val instanceof Date){
36378 return this.editors['date'];
36379 }else if(typeof val == 'number'){
36380 return this.editors['number'];
36381 }else if(typeof val == 'boolean'){
36382 return this.editors['boolean'];
36384 return this.editors['string'];
36390 * @class Roo.grid.PropertyGrid
36391 * @extends Roo.grid.EditorGrid
36392 * This class represents the interface of a component based property grid control.
36393 * <br><br>Usage:<pre><code>
36394 var grid = new Roo.grid.PropertyGrid("my-container-id", {
36402 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36403 * The container MUST have some type of size defined for the grid to fill. The container will be
36404 * automatically set to position relative if it isn't already.
36405 * @param {Object} config A config object that sets properties on this grid.
36407 Roo.grid.PropertyGrid = function(container, config){
36408 config = config || {};
36409 var store = new Roo.grid.PropertyStore(this);
36410 this.store = store;
36411 var cm = new Roo.grid.PropertyColumnModel(this, store);
36412 store.store.sort('name', 'ASC');
36413 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36416 enableColLock:false,
36417 enableColumnMove:false,
36419 trackMouseOver: false,
36422 this.getGridEl().addClass('x-props-grid');
36423 this.lastEditRow = null;
36424 this.on('columnresize', this.onColumnResize, this);
36427 * @event beforepropertychange
36428 * Fires before a property changes (return false to stop?)
36429 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36430 * @param {String} id Record Id
36431 * @param {String} newval New Value
36432 * @param {String} oldval Old Value
36434 "beforepropertychange": true,
36436 * @event propertychange
36437 * Fires after a property changes
36438 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36439 * @param {String} id Record Id
36440 * @param {String} newval New Value
36441 * @param {String} oldval Old Value
36443 "propertychange": true
36445 this.customEditors = this.customEditors || {};
36447 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36450 * @cfg {Object} customEditors map of colnames=> custom editors.
36451 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36452 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36453 * false disables editing of the field.
36457 * @cfg {Object} propertyNames map of property Names to their displayed value
36460 render : function(){
36461 Roo.grid.PropertyGrid.superclass.render.call(this);
36462 this.autoSize.defer(100, this);
36465 autoSize : function(){
36466 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36468 this.view.fitColumns();
36472 onColumnResize : function(){
36473 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36477 * Sets the data for the Grid
36478 * accepts a Key => Value object of all the elements avaiable.
36479 * @param {Object} data to appear in grid.
36481 setSource : function(source){
36482 this.store.setSource(source);
36486 * Gets all the data from the grid.
36487 * @return {Object} data data stored in grid
36489 getSource : function(){
36490 return this.store.getSource();
36499 * @class Roo.grid.Calendar
36500 * @extends Roo.util.Grid
36501 * This class extends the Grid to provide a calendar widget
36502 * <br><br>Usage:<pre><code>
36503 var grid = new Roo.grid.Calendar("my-container-id", {
36506 selModel: mySelectionModel,
36507 autoSizeColumns: true,
36508 monitorWindowResize: false,
36509 trackMouseOver: true
36510 eventstore : real data store..
36516 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36517 * The container MUST have some type of size defined for the grid to fill. The container will be
36518 * automatically set to position relative if it isn't already.
36519 * @param {Object} config A config object that sets properties on this grid.
36521 Roo.grid.Calendar = function(container, config){
36522 // initialize the container
36523 this.container = Roo.get(container);
36524 this.container.update("");
36525 this.container.setStyle("overflow", "hidden");
36526 this.container.addClass('x-grid-container');
36528 this.id = this.container.id;
36530 Roo.apply(this, config);
36531 // check and correct shorthanded configs
36535 for (var r = 0;r < 6;r++) {
36538 for (var c =0;c < 7;c++) {
36542 if (this.eventStore) {
36543 this.eventStore= Roo.factory(this.eventStore, Roo.data);
36544 this.eventStore.on('load',this.onLoad, this);
36545 this.eventStore.on('beforeload',this.clearEvents, this);
36549 this.dataSource = new Roo.data.Store({
36550 proxy: new Roo.data.MemoryProxy(rows),
36551 reader: new Roo.data.ArrayReader({}, [
36552 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36555 this.dataSource.load();
36556 this.ds = this.dataSource;
36557 this.ds.xmodule = this.xmodule || false;
36560 var cellRender = function(v,x,r)
36562 return String.format(
36563 '<div class="fc-day fc-widget-content"><div>' +
36564 '<div class="fc-event-container"></div>' +
36565 '<div class="fc-day-number">{0}</div>'+
36567 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36568 '</div></div>', v);
36573 this.colModel = new Roo.grid.ColumnModel( [
36575 xtype: 'ColumnModel',
36577 dataIndex : 'weekday0',
36579 renderer : cellRender
36582 xtype: 'ColumnModel',
36584 dataIndex : 'weekday1',
36586 renderer : cellRender
36589 xtype: 'ColumnModel',
36591 dataIndex : 'weekday2',
36592 header : 'Tuesday',
36593 renderer : cellRender
36596 xtype: 'ColumnModel',
36598 dataIndex : 'weekday3',
36599 header : 'Wednesday',
36600 renderer : cellRender
36603 xtype: 'ColumnModel',
36605 dataIndex : 'weekday4',
36606 header : 'Thursday',
36607 renderer : cellRender
36610 xtype: 'ColumnModel',
36612 dataIndex : 'weekday5',
36614 renderer : cellRender
36617 xtype: 'ColumnModel',
36619 dataIndex : 'weekday6',
36620 header : 'Saturday',
36621 renderer : cellRender
36624 this.cm = this.colModel;
36625 this.cm.xmodule = this.xmodule || false;
36629 //this.selModel = new Roo.grid.CellSelectionModel();
36630 //this.sm = this.selModel;
36631 //this.selModel.init(this);
36635 this.container.setWidth(this.width);
36639 this.container.setHeight(this.height);
36646 * The raw click event for the entire grid.
36647 * @param {Roo.EventObject} e
36652 * The raw dblclick event for the entire grid.
36653 * @param {Roo.EventObject} e
36657 * @event contextmenu
36658 * The raw contextmenu event for the entire grid.
36659 * @param {Roo.EventObject} e
36661 "contextmenu" : true,
36664 * The raw mousedown event for the entire grid.
36665 * @param {Roo.EventObject} e
36667 "mousedown" : true,
36670 * The raw mouseup event for the entire grid.
36671 * @param {Roo.EventObject} e
36676 * The raw mouseover event for the entire grid.
36677 * @param {Roo.EventObject} e
36679 "mouseover" : true,
36682 * The raw mouseout event for the entire grid.
36683 * @param {Roo.EventObject} e
36688 * The raw keypress event for the entire grid.
36689 * @param {Roo.EventObject} e
36694 * The raw keydown event for the entire grid.
36695 * @param {Roo.EventObject} e
36703 * Fires when a cell is clicked
36704 * @param {Grid} this
36705 * @param {Number} rowIndex
36706 * @param {Number} columnIndex
36707 * @param {Roo.EventObject} e
36709 "cellclick" : true,
36711 * @event celldblclick
36712 * Fires when a cell is double clicked
36713 * @param {Grid} this
36714 * @param {Number} rowIndex
36715 * @param {Number} columnIndex
36716 * @param {Roo.EventObject} e
36718 "celldblclick" : true,
36721 * Fires when a row is clicked
36722 * @param {Grid} this
36723 * @param {Number} rowIndex
36724 * @param {Roo.EventObject} e
36728 * @event rowdblclick
36729 * Fires when a row is double clicked
36730 * @param {Grid} this
36731 * @param {Number} rowIndex
36732 * @param {Roo.EventObject} e
36734 "rowdblclick" : true,
36736 * @event headerclick
36737 * Fires when a header is clicked
36738 * @param {Grid} this
36739 * @param {Number} columnIndex
36740 * @param {Roo.EventObject} e
36742 "headerclick" : true,
36744 * @event headerdblclick
36745 * Fires when a header cell is double clicked
36746 * @param {Grid} this
36747 * @param {Number} columnIndex
36748 * @param {Roo.EventObject} e
36750 "headerdblclick" : true,
36752 * @event rowcontextmenu
36753 * Fires when a row is right clicked
36754 * @param {Grid} this
36755 * @param {Number} rowIndex
36756 * @param {Roo.EventObject} e
36758 "rowcontextmenu" : true,
36760 * @event cellcontextmenu
36761 * Fires when a cell is right clicked
36762 * @param {Grid} this
36763 * @param {Number} rowIndex
36764 * @param {Number} cellIndex
36765 * @param {Roo.EventObject} e
36767 "cellcontextmenu" : true,
36769 * @event headercontextmenu
36770 * Fires when a header is right clicked
36771 * @param {Grid} this
36772 * @param {Number} columnIndex
36773 * @param {Roo.EventObject} e
36775 "headercontextmenu" : true,
36777 * @event bodyscroll
36778 * Fires when the body element is scrolled
36779 * @param {Number} scrollLeft
36780 * @param {Number} scrollTop
36782 "bodyscroll" : true,
36784 * @event columnresize
36785 * Fires when the user resizes a column
36786 * @param {Number} columnIndex
36787 * @param {Number} newSize
36789 "columnresize" : true,
36791 * @event columnmove
36792 * Fires when the user moves a column
36793 * @param {Number} oldIndex
36794 * @param {Number} newIndex
36796 "columnmove" : true,
36799 * Fires when row(s) start being dragged
36800 * @param {Grid} this
36801 * @param {Roo.GridDD} dd The drag drop object
36802 * @param {event} e The raw browser event
36804 "startdrag" : true,
36807 * Fires when a drag operation is complete
36808 * @param {Grid} this
36809 * @param {Roo.GridDD} dd The drag drop object
36810 * @param {event} e The raw browser event
36815 * Fires when dragged row(s) are dropped on a valid DD target
36816 * @param {Grid} this
36817 * @param {Roo.GridDD} dd The drag drop object
36818 * @param {String} targetId The target drag drop object
36819 * @param {event} e The raw browser event
36824 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36825 * @param {Grid} this
36826 * @param {Roo.GridDD} dd The drag drop object
36827 * @param {String} targetId The target drag drop object
36828 * @param {event} e The raw browser event
36833 * Fires when the dragged row(s) first cross another DD target while being dragged
36834 * @param {Grid} this
36835 * @param {Roo.GridDD} dd The drag drop object
36836 * @param {String} targetId The target drag drop object
36837 * @param {event} e The raw browser event
36839 "dragenter" : true,
36842 * Fires when the dragged row(s) leave another DD target while being dragged
36843 * @param {Grid} this
36844 * @param {Roo.GridDD} dd The drag drop object
36845 * @param {String} targetId The target drag drop object
36846 * @param {event} e The raw browser event
36851 * Fires when a row is rendered, so you can change add a style to it.
36852 * @param {GridView} gridview The grid view
36853 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
36859 * Fires when the grid is rendered
36860 * @param {Grid} grid
36865 * Fires when a date is selected
36866 * @param {DatePicker} this
36867 * @param {Date} date The selected date
36871 * @event monthchange
36872 * Fires when the displayed month changes
36873 * @param {DatePicker} this
36874 * @param {Date} date The selected month
36876 'monthchange': true,
36878 * @event evententer
36879 * Fires when mouse over an event
36880 * @param {Calendar} this
36881 * @param {event} Event
36883 'evententer': true,
36885 * @event eventleave
36886 * Fires when the mouse leaves an
36887 * @param {Calendar} this
36890 'eventleave': true,
36892 * @event eventclick
36893 * Fires when the mouse click an
36894 * @param {Calendar} this
36897 'eventclick': true,
36899 * @event eventrender
36900 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36901 * @param {Calendar} this
36902 * @param {data} data to be modified
36904 'eventrender': true
36908 Roo.grid.Grid.superclass.constructor.call(this);
36909 this.on('render', function() {
36910 this.view.el.addClass('x-grid-cal');
36912 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36916 if (!Roo.grid.Calendar.style) {
36917 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36920 '.x-grid-cal .x-grid-col' : {
36921 height: 'auto !important',
36922 'vertical-align': 'top'
36924 '.x-grid-cal .fc-event-hori' : {
36935 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36937 * @cfg {Store} eventStore The store that loads events.
36942 activeDate : false,
36945 monitorWindowResize : false,
36948 resizeColumns : function() {
36949 var col = (this.view.el.getWidth() / 7) - 3;
36950 // loop through cols, and setWidth
36951 for(var i =0 ; i < 7 ; i++){
36952 this.cm.setColumnWidth(i, col);
36955 setDate :function(date) {
36957 Roo.log('setDate?');
36959 this.resizeColumns();
36960 var vd = this.activeDate;
36961 this.activeDate = date;
36962 // if(vd && this.el){
36963 // var t = date.getTime();
36964 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36965 // Roo.log('using add remove');
36967 // this.fireEvent('monthchange', this, date);
36969 // this.cells.removeClass("fc-state-highlight");
36970 // this.cells.each(function(c){
36971 // if(c.dateValue == t){
36972 // c.addClass("fc-state-highlight");
36973 // setTimeout(function(){
36974 // try{c.dom.firstChild.focus();}catch(e){}
36984 var days = date.getDaysInMonth();
36986 var firstOfMonth = date.getFirstDateOfMonth();
36987 var startingPos = firstOfMonth.getDay()-this.startDay;
36989 if(startingPos < this.startDay){
36993 var pm = date.add(Date.MONTH, -1);
36994 var prevStart = pm.getDaysInMonth()-startingPos;
36998 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37000 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37001 //this.cells.addClassOnOver('fc-state-hover');
37003 var cells = this.cells.elements;
37004 var textEls = this.textNodes;
37006 //Roo.each(cells, function(cell){
37007 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37010 days += startingPos;
37012 // convert everything to numbers so it's fast
37013 var day = 86400000;
37014 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37017 //Roo.log(prevStart);
37019 var today = new Date().clearTime().getTime();
37020 var sel = date.clearTime().getTime();
37021 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37022 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37023 var ddMatch = this.disabledDatesRE;
37024 var ddText = this.disabledDatesText;
37025 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37026 var ddaysText = this.disabledDaysText;
37027 var format = this.format;
37029 var setCellClass = function(cal, cell){
37031 //Roo.log('set Cell Class');
37033 var t = d.getTime();
37038 cell.dateValue = t;
37040 cell.className += " fc-today";
37041 cell.className += " fc-state-highlight";
37042 cell.title = cal.todayText;
37045 // disable highlight in other month..
37046 cell.className += " fc-state-highlight";
37051 //cell.className = " fc-state-disabled";
37052 cell.title = cal.minText;
37056 //cell.className = " fc-state-disabled";
37057 cell.title = cal.maxText;
37061 if(ddays.indexOf(d.getDay()) != -1){
37062 // cell.title = ddaysText;
37063 // cell.className = " fc-state-disabled";
37066 if(ddMatch && format){
37067 var fvalue = d.dateFormat(format);
37068 if(ddMatch.test(fvalue)){
37069 cell.title = ddText.replace("%0", fvalue);
37070 cell.className = " fc-state-disabled";
37074 if (!cell.initialClassName) {
37075 cell.initialClassName = cell.dom.className;
37078 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37083 for(; i < startingPos; i++) {
37084 cells[i].dayName = (++prevStart);
37085 Roo.log(textEls[i]);
37086 d.setDate(d.getDate()+1);
37088 //cells[i].className = "fc-past fc-other-month";
37089 setCellClass(this, cells[i]);
37094 for(; i < days; i++){
37095 intDay = i - startingPos + 1;
37096 cells[i].dayName = (intDay);
37097 d.setDate(d.getDate()+1);
37099 cells[i].className = ''; // "x-date-active";
37100 setCellClass(this, cells[i]);
37104 for(; i < 42; i++) {
37105 //textEls[i].innerHTML = (++extraDays);
37107 d.setDate(d.getDate()+1);
37108 cells[i].dayName = (++extraDays);
37109 cells[i].className = "fc-future fc-other-month";
37110 setCellClass(this, cells[i]);
37113 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37115 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37117 // this will cause all the cells to mis
37120 for (var r = 0;r < 6;r++) {
37121 for (var c =0;c < 7;c++) {
37122 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37126 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37127 for(i=0;i<cells.length;i++) {
37129 this.cells.elements[i].dayName = cells[i].dayName ;
37130 this.cells.elements[i].className = cells[i].className;
37131 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37132 this.cells.elements[i].title = cells[i].title ;
37133 this.cells.elements[i].dateValue = cells[i].dateValue ;
37139 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37140 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37142 ////if(totalRows != 6){
37143 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37144 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37147 this.fireEvent('monthchange', this, date);
37152 * Returns the grid's SelectionModel.
37153 * @return {SelectionModel}
37155 getSelectionModel : function(){
37156 if(!this.selModel){
37157 this.selModel = new Roo.grid.CellSelectionModel();
37159 return this.selModel;
37163 this.eventStore.load()
37169 findCell : function(dt) {
37170 dt = dt.clearTime().getTime();
37172 this.cells.each(function(c){
37173 //Roo.log("check " +c.dateValue + '?=' + dt);
37174 if(c.dateValue == dt){
37184 findCells : function(rec) {
37185 var s = rec.data.start_dt.clone().clearTime().getTime();
37187 var e= rec.data.end_dt.clone().clearTime().getTime();
37190 this.cells.each(function(c){
37191 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37193 if(c.dateValue > e){
37196 if(c.dateValue < s){
37205 findBestRow: function(cells)
37209 for (var i =0 ; i < cells.length;i++) {
37210 ret = Math.max(cells[i].rows || 0,ret);
37217 addItem : function(rec)
37219 // look for vertical location slot in
37220 var cells = this.findCells(rec);
37222 rec.row = this.findBestRow(cells);
37224 // work out the location.
37228 for(var i =0; i < cells.length; i++) {
37236 if (crow.start.getY() == cells[i].getY()) {
37238 crow.end = cells[i];
37254 for (var i = 0; i < cells.length;i++) {
37255 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37262 clearEvents: function() {
37264 if (!this.eventStore.getCount()) {
37267 // reset number of rows in cells.
37268 Roo.each(this.cells.elements, function(c){
37272 this.eventStore.each(function(e) {
37273 this.clearEvent(e);
37278 clearEvent : function(ev)
37281 Roo.each(ev.els, function(el) {
37282 el.un('mouseenter' ,this.onEventEnter, this);
37283 el.un('mouseleave' ,this.onEventLeave, this);
37291 renderEvent : function(ev,ctr) {
37293 ctr = this.view.el.select('.fc-event-container',true).first();
37297 this.clearEvent(ev);
37303 var cells = ev.cells;
37304 var rows = ev.rows;
37305 this.fireEvent('eventrender', this, ev);
37307 for(var i =0; i < rows.length; i++) {
37311 cls += ' fc-event-start';
37313 if ((i+1) == rows.length) {
37314 cls += ' fc-event-end';
37317 //Roo.log(ev.data);
37318 // how many rows should it span..
37319 var cg = this.eventTmpl.append(ctr,Roo.apply({
37322 }, ev.data) , true);
37325 cg.on('mouseenter' ,this.onEventEnter, this, ev);
37326 cg.on('mouseleave' ,this.onEventLeave, this, ev);
37327 cg.on('click', this.onEventClick, this, ev);
37331 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37332 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37335 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
37336 cg.setWidth(ebox.right - sbox.x -2);
37340 renderEvents: function()
37342 // first make sure there is enough space..
37344 if (!this.eventTmpl) {
37345 this.eventTmpl = new Roo.Template(
37346 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
37347 '<div class="fc-event-inner">' +
37348 '<span class="fc-event-time">{time}</span>' +
37349 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37351 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
37359 this.cells.each(function(c) {
37360 //Roo.log(c.select('.fc-day-content div',true).first());
37361 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37364 var ctr = this.view.el.select('.fc-event-container',true).first();
37367 this.eventStore.each(function(ev){
37369 this.renderEvent(ev);
37373 this.view.layout();
37377 onEventEnter: function (e, el,event,d) {
37378 this.fireEvent('evententer', this, el, event);
37381 onEventLeave: function (e, el,event,d) {
37382 this.fireEvent('eventleave', this, el, event);
37385 onEventClick: function (e, el,event,d) {
37386 this.fireEvent('eventclick', this, el, event);
37389 onMonthChange: function () {
37393 onLoad: function () {
37395 //Roo.log('calendar onload');
37397 if(this.eventStore.getCount() > 0){
37401 this.eventStore.each(function(d){
37406 if (typeof(add.end_dt) == 'undefined') {
37407 Roo.log("Missing End time in calendar data: ");
37411 if (typeof(add.start_dt) == 'undefined') {
37412 Roo.log("Missing Start time in calendar data: ");
37416 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37417 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37418 add.id = add.id || d.id;
37419 add.title = add.title || '??';
37427 this.renderEvents();
37437 render : function ()
37441 if (!this.view.el.hasClass('course-timesheet')) {
37442 this.view.el.addClass('course-timesheet');
37444 if (this.tsStyle) {
37449 Roo.log(_this.grid.view.el.getWidth());
37452 this.tsStyle = Roo.util.CSS.createStyleSheet({
37453 '.course-timesheet .x-grid-row' : {
37456 '.x-grid-row td' : {
37457 'vertical-align' : 0
37459 '.course-edit-link' : {
37461 'text-overflow' : 'ellipsis',
37462 'overflow' : 'hidden',
37463 'white-space' : 'nowrap',
37464 'cursor' : 'pointer'
37469 '.de-act-sup-link' : {
37470 'color' : 'purple',
37471 'text-decoration' : 'line-through'
37475 'text-decoration' : 'line-through'
37477 '.course-timesheet .course-highlight' : {
37478 'border-top-style': 'dashed !important',
37479 'border-bottom-bottom': 'dashed !important'
37481 '.course-timesheet .course-item' : {
37482 'font-family' : 'tahoma, arial, helvetica',
37483 'font-size' : '11px',
37484 'overflow' : 'hidden',
37485 'padding-left' : '10px',
37486 'padding-right' : '10px',
37487 'padding-top' : '10px'
37495 monitorWindowResize : false,
37496 cellrenderer : function(v,x,r)
37501 xtype: 'CellSelectionModel',
37508 beforeload : function (_self, options)
37510 options.params = options.params || {};
37511 options.params._month = _this.monthField.getValue();
37512 options.params.limit = 9999;
37513 options.params['sort'] = 'when_dt';
37514 options.params['dir'] = 'ASC';
37515 this.proxy.loadResponse = this.loadResponse;
37517 //this.addColumns();
37519 load : function (_self, records, options)
37521 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37522 // if you click on the translation.. you can edit it...
37523 var el = Roo.get(this);
37524 var id = el.dom.getAttribute('data-id');
37525 var d = el.dom.getAttribute('data-date');
37526 var t = el.dom.getAttribute('data-time');
37527 //var id = this.child('span').dom.textContent;
37530 Pman.Dialog.CourseCalendar.show({
37534 productitem_active : id ? 1 : 0
37536 _this.grid.ds.load({});
37541 _this.panel.fireEvent('resize', [ '', '' ]);
37544 loadResponse : function(o, success, response){
37545 // this is overridden on before load..
37547 Roo.log("our code?");
37548 //Roo.log(success);
37549 //Roo.log(response)
37550 delete this.activeRequest;
37552 this.fireEvent("loadexception", this, o, response);
37553 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37558 result = o.reader.read(response);
37560 Roo.log("load exception?");
37561 this.fireEvent("loadexception", this, o, response, e);
37562 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37565 Roo.log("ready...");
37566 // loop through result.records;
37567 // and set this.tdate[date] = [] << array of records..
37569 Roo.each(result.records, function(r){
37571 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37572 _this.tdata[r.data.when_dt.format('j')] = [];
37574 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37577 //Roo.log(_this.tdata);
37579 result.records = [];
37580 result.totalRecords = 6;
37582 // let's generate some duumy records for the rows.
37583 //var st = _this.dateField.getValue();
37585 // work out monday..
37586 //st = st.add(Date.DAY, -1 * st.format('w'));
37588 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37590 var firstOfMonth = date.getFirstDayOfMonth();
37591 var days = date.getDaysInMonth();
37593 var firstAdded = false;
37594 for (var i = 0; i < result.totalRecords ; i++) {
37595 //var d= st.add(Date.DAY, i);
37598 for(var w = 0 ; w < 7 ; w++){
37599 if(!firstAdded && firstOfMonth != w){
37606 var dd = (d > 0 && d < 10) ? "0"+d : d;
37607 row['weekday'+w] = String.format(
37608 '<span style="font-size: 16px;"><b>{0}</b></span>'+
37609 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37611 date.format('Y-m-')+dd
37614 if(typeof(_this.tdata[d]) != 'undefined'){
37615 Roo.each(_this.tdata[d], function(r){
37619 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37620 if(r.parent_id*1>0){
37621 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37624 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37625 deactive = 'de-act-link';
37628 row['weekday'+w] += String.format(
37629 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37631 r.product_id_name, //1
37632 r.when_dt.format('h:ia'), //2
37642 // only do this if something added..
37644 result.records.push(_this.grid.dataSource.reader.newRow(row));
37648 // push it twice. (second one with an hour..
37652 this.fireEvent("load", this, o, o.request.arg);
37653 o.request.callback.call(o.request.scope, result, o.request.arg, true);
37655 sortInfo : {field: 'when_dt', direction : 'ASC' },
37657 xtype: 'HttpProxy',
37660 url : baseURL + '/Roo/Shop_course.php'
37663 xtype: 'JsonReader',
37680 'name': 'parent_id',
37684 'name': 'product_id',
37688 'name': 'productitem_id',
37706 click : function (_self, e)
37708 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37709 sd.setMonth(sd.getMonth()-1);
37710 _this.monthField.setValue(sd.format('Y-m-d'));
37711 _this.grid.ds.load({});
37717 xtype: 'Separator',
37721 xtype: 'MonthField',
37724 render : function (_self)
37726 _this.monthField = _self;
37727 // _this.monthField.set today
37729 select : function (combo, date)
37731 _this.grid.ds.load({});
37734 value : (function() { return new Date(); })()
37737 xtype: 'Separator',
37743 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37753 click : function (_self, e)
37755 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37756 sd.setMonth(sd.getMonth()+1);
37757 _this.monthField.setValue(sd.format('Y-m-d'));
37758 _this.grid.ds.load({});
37771 * Ext JS Library 1.1.1
37772 * Copyright(c) 2006-2007, Ext JS, LLC.
37774 * Originally Released Under LGPL - original licence link has changed is not relivant.
37777 * <script type="text/javascript">
37781 * @class Roo.LoadMask
37782 * A simple utility class for generically masking elements while loading data. If the element being masked has
37783 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37784 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
37785 * element's UpdateManager load indicator and will be destroyed after the initial load.
37787 * Create a new LoadMask
37788 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37789 * @param {Object} config The config object
37791 Roo.LoadMask = function(el, config){
37792 this.el = Roo.get(el);
37793 Roo.apply(this, config);
37795 this.store.on('beforeload', this.onBeforeLoad, this);
37796 this.store.on('load', this.onLoad, this);
37797 this.store.on('loadexception', this.onLoadException, this);
37798 this.removeMask = false;
37800 var um = this.el.getUpdateManager();
37801 um.showLoadIndicator = false; // disable the default indicator
37802 um.on('beforeupdate', this.onBeforeLoad, this);
37803 um.on('update', this.onLoad, this);
37804 um.on('failure', this.onLoad, this);
37805 this.removeMask = true;
37809 Roo.LoadMask.prototype = {
37811 * @cfg {Boolean} removeMask
37812 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37813 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
37816 * @cfg {String} msg
37817 * The text to display in a centered loading message box (defaults to 'Loading...')
37819 msg : 'Loading...',
37821 * @cfg {String} msgCls
37822 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37824 msgCls : 'x-mask-loading',
37827 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37833 * Disables the mask to prevent it from being displayed
37835 disable : function(){
37836 this.disabled = true;
37840 * Enables the mask so that it can be displayed
37842 enable : function(){
37843 this.disabled = false;
37846 onLoadException : function()
37848 Roo.log(arguments);
37850 if (typeof(arguments[3]) != 'undefined') {
37851 Roo.MessageBox.alert("Error loading",arguments[3]);
37855 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37856 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37865 this.el.unmask(this.removeMask);
37868 onLoad : function()
37870 this.el.unmask(this.removeMask);
37874 onBeforeLoad : function(){
37875 if(!this.disabled){
37876 this.el.mask(this.msg, this.msgCls);
37881 destroy : function(){
37883 this.store.un('beforeload', this.onBeforeLoad, this);
37884 this.store.un('load', this.onLoad, this);
37885 this.store.un('loadexception', this.onLoadException, this);
37887 var um = this.el.getUpdateManager();
37888 um.un('beforeupdate', this.onBeforeLoad, this);
37889 um.un('update', this.onLoad, this);
37890 um.un('failure', this.onLoad, this);
37895 * Ext JS Library 1.1.1
37896 * Copyright(c) 2006-2007, Ext JS, LLC.
37898 * Originally Released Under LGPL - original licence link has changed is not relivant.
37901 * <script type="text/javascript">
37906 * @class Roo.XTemplate
37907 * @extends Roo.Template
37908 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37910 var t = new Roo.XTemplate(
37911 '<select name="{name}">',
37912 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
37916 // then append, applying the master template values
37919 * Supported features:
37924 {a_variable} - output encoded.
37925 {a_variable.format:("Y-m-d")} - call a method on the variable
37926 {a_variable:raw} - unencoded output
37927 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37928 {a_variable:this.method_on_template(...)} - call a method on the template object.
37933 <tpl for="a_variable or condition.."></tpl>
37934 <tpl if="a_variable or condition"></tpl>
37935 <tpl exec="some javascript"></tpl>
37936 <tpl name="named_template"></tpl> (experimental)
37938 <tpl for="."></tpl> - just iterate the property..
37939 <tpl for=".."></tpl> - iterates with the parent (probably the template)
37943 Roo.XTemplate = function()
37945 Roo.XTemplate.superclass.constructor.apply(this, arguments);
37952 Roo.extend(Roo.XTemplate, Roo.Template, {
37955 * The various sub templates
37960 * basic tag replacing syntax
37963 * // you can fake an object call by doing this
37967 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37970 * compile the template
37972 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37975 compile: function()
37979 s = ['<tpl>', s, '</tpl>'].join('');
37981 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
37982 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
37983 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
37984 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
37985 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
37990 while(true == !!(m = s.match(re))){
37991 var forMatch = m[0].match(nameRe),
37992 ifMatch = m[0].match(ifRe),
37993 execMatch = m[0].match(execRe),
37994 namedMatch = m[0].match(namedRe),
37999 name = forMatch && forMatch[1] ? forMatch[1] : '';
38002 // if - puts fn into test..
38003 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38005 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38010 // exec - calls a function... returns empty if true is returned.
38011 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38013 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38021 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38022 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38023 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38026 var uid = namedMatch ? namedMatch[1] : id;
38030 id: namedMatch ? namedMatch[1] : id,
38037 s = s.replace(m[0], '');
38039 s = s.replace(m[0], '{xtpl'+ id + '}');
38044 for(var i = tpls.length-1; i >= 0; --i){
38045 this.compileTpl(tpls[i]);
38046 this.tpls[tpls[i].id] = tpls[i];
38048 this.master = tpls[tpls.length-1];
38052 * same as applyTemplate, except it's done to one of the subTemplates
38053 * when using named templates, you can do:
38055 * var str = pl.applySubTemplate('your-name', values);
38058 * @param {Number} id of the template
38059 * @param {Object} values to apply to template
38060 * @param {Object} parent (normaly the instance of this object)
38062 applySubTemplate : function(id, values, parent)
38066 var t = this.tpls[id];
38070 if(t.test && !t.test.call(this, values, parent)){
38074 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38075 Roo.log(e.toString());
38081 if(t.exec && t.exec.call(this, values, parent)){
38085 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38086 Roo.log(e.toString());
38091 var vs = t.target ? t.target.call(this, values, parent) : values;
38092 parent = t.target ? values : parent;
38093 if(t.target && vs instanceof Array){
38095 for(var i = 0, len = vs.length; i < len; i++){
38096 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38098 return buf.join('');
38100 return t.compiled.call(this, vs, parent);
38102 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38103 Roo.log(e.toString());
38104 Roo.log(t.compiled);
38109 compileTpl : function(tpl)
38111 var fm = Roo.util.Format;
38112 var useF = this.disableFormats !== true;
38113 var sep = Roo.isGecko ? "+" : ",";
38114 var undef = function(str) {
38115 Roo.log("Property not found :" + str);
38119 var fn = function(m, name, format, args)
38121 //Roo.log(arguments);
38122 args = args ? args.replace(/\\'/g,"'") : args;
38123 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38124 if (typeof(format) == 'undefined') {
38125 format= 'htmlEncode';
38127 if (format == 'raw' ) {
38131 if(name.substr(0, 4) == 'xtpl'){
38132 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38135 // build an array of options to determine if value is undefined..
38137 // basically get 'xxxx.yyyy' then do
38138 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38139 // (function () { Roo.log("Property not found"); return ''; })() :
38144 Roo.each(name.split('.'), function(st) {
38145 lookfor += (lookfor.length ? '.': '') + st;
38146 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38149 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38152 if(format && useF){
38154 args = args ? ',' + args : "";
38156 if(format.substr(0, 5) != "this."){
38157 format = "fm." + format + '(';
38159 format = 'this.call("'+ format.substr(5) + '", ';
38163 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38167 // called with xxyx.yuu:(test,test)
38169 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38171 // raw.. - :raw modifier..
38172 return "'"+ sep + udef_st + name + ")"+sep+"'";
38176 // branched to use + in gecko and [].join() in others
38178 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38179 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38182 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38183 body.push(tpl.body.replace(/(\r\n|\n)/g,
38184 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38185 body.push("'].join('');};};");
38186 body = body.join('');
38189 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38191 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38197 applyTemplate : function(values){
38198 return this.master.compiled.call(this, values, {});
38199 //var s = this.subs;
38202 apply : function(){
38203 return this.applyTemplate.apply(this, arguments);
38208 Roo.XTemplate.from = function(el){
38209 el = Roo.getDom(el);
38210 return new Roo.XTemplate(el.value || el.innerHTML);