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);
571 if(this.pruneModifiedRecords){
572 this.modified.remove(record);
574 this.fireEvent("remove", this, record, index);
578 * Remove all Records from the Store and fires the clear event.
580 removeAll : function(){
582 if(this.pruneModifiedRecords){
585 this.fireEvent("clear", this);
589 * Inserts Records to the Store at the given index and fires the add event.
590 * @param {Number} index The start index at which to insert the passed Records.
591 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
593 insert : function(index, records){
594 records = [].concat(records);
595 for(var i = 0, len = records.length; i < len; i++){
596 this.data.insert(index, records[i]);
597 records[i].join(this);
599 this.fireEvent("add", this, records, index);
603 * Get the index within the cache of the passed Record.
604 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605 * @return {Number} The index of the passed Record. Returns -1 if not found.
607 indexOf : function(record){
608 return this.data.indexOf(record);
612 * Get the index within the cache of the Record with the passed id.
613 * @param {String} id The id of the Record to find.
614 * @return {Number} The index of the Record. Returns -1 if not found.
616 indexOfId : function(id){
617 return this.data.indexOfKey(id);
621 * Get the Record with the specified id.
622 * @param {String} id The id of the Record to find.
623 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
625 getById : function(id){
626 return this.data.key(id);
630 * Get the Record at the specified index.
631 * @param {Number} index The index of the Record to find.
632 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
634 getAt : function(index){
635 return this.data.itemAt(index);
639 * Returns a range of Records between specified indices.
640 * @param {Number} startIndex (optional) The starting index (defaults to 0)
641 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642 * @return {Roo.data.Record[]} An array of Records
644 getRange : function(start, end){
645 return this.data.getRange(start, end);
649 storeOptions : function(o){
650 o = Roo.apply({}, o);
653 this.lastOptions = o;
657 * Loads the Record cache from the configured Proxy using the configured Reader.
659 * If using remote paging, then the first load call must specify the <em>start</em>
660 * and <em>limit</em> properties in the options.params property to establish the initial
661 * position within the dataset, and the number of Records to cache on each read from the Proxy.
663 * <strong>It is important to note that for remote data sources, loading is asynchronous,
664 * and this call will return before the new data has been loaded. Perform any post-processing
665 * in a callback function, or in a "load" event handler.</strong>
667 * @param {Object} options An object containing properties which control loading options:<ul>
668 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670 * passed the following arguments:<ul>
671 * <li>r : Roo.data.Record[]</li>
672 * <li>options: Options object from the load call</li>
673 * <li>success: Boolean success indicator</li></ul></li>
674 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
678 load : function(options){
679 options = options || {};
680 if(this.fireEvent("beforeload", this, options) !== false){
681 this.storeOptions(options);
682 var p = Roo.apply(options.params || {}, this.baseParams);
683 // if meta was not loaded from remote source.. try requesting it.
684 if (!this.reader.metaFromRemote) {
687 if(this.sortInfo && this.remoteSort){
688 var pn = this.paramNames;
689 p[pn["sort"]] = this.sortInfo.field;
690 p[pn["dir"]] = this.sortInfo.direction;
692 if (this.multiSort) {
693 var pn = this.paramNames;
694 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
697 this.proxy.load(p, this.reader, this.loadRecords, this, options);
702 * Reloads the Record cache from the configured Proxy using the configured Reader and
703 * the options from the last load operation performed.
704 * @param {Object} options (optional) An object containing properties which may override the options
705 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706 * the most recently used options are reused).
708 reload : function(options){
709 this.load(Roo.applyIf(options||{}, this.lastOptions));
713 // Called as a callback by the Reader during a load operation.
714 loadRecords : function(o, options, success){
715 if(!o || success === false){
716 if(success !== false){
717 this.fireEvent("load", this, [], options, o);
719 if(options.callback){
720 options.callback.call(options.scope || this, [], options, false);
724 // if data returned failure - throw an exception.
725 if (o.success === false) {
726 // show a message if no listener is registered.
727 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
730 // loadmask wil be hooked into this..
731 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
734 var r = o.records, t = o.totalRecords || r.length;
736 this.fireEvent("beforeloadadd", this, r, options, o);
738 if(!options || options.add !== true){
739 if(this.pruneModifiedRecords){
742 for(var i = 0, len = r.length; i < len; i++){
746 this.data = this.snapshot;
747 delete this.snapshot;
751 this.totalLength = t;
753 this.fireEvent("datachanged", this);
755 this.totalLength = Math.max(t, this.data.length+r.length);
759 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
761 var e = new Roo.data.Record({});
763 e.set(this.parent.displayField, this.parent.emptyTitle);
764 e.set(this.parent.valueField, '');
769 this.fireEvent("load", this, r, options, o);
770 if(options.callback){
771 options.callback.call(options.scope || this, r, options, true);
777 * Loads data from a passed data block. A Reader which understands the format of the data
778 * must have been configured in the constructor.
779 * @param {Object} data The data block from which to read the Records. The format of the data expected
780 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
783 loadData : function(o, append){
784 var r = this.reader.readRecords(o);
785 this.loadRecords(r, {add: append}, true);
789 * Gets the number of cached records.
791 * <em>If using paging, this may not be the total size of the dataset. If the data object
792 * used by the Reader contains the dataset size, then the getTotalCount() function returns
793 * the data set size</em>
795 getCount : function(){
796 return this.data.length || 0;
800 * Gets the total number of records in the dataset as returned by the server.
802 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803 * the dataset size</em>
805 getTotalCount : function(){
806 return this.totalLength || 0;
810 * Returns the sort state of the Store as an object with two properties:
812 field {String} The name of the field by which the Records are sorted
813 direction {String} The sort order, "ASC" or "DESC"
816 getSortState : function(){
817 return this.sortInfo;
821 applySort : function(){
822 if(this.sortInfo && !this.remoteSort){
823 var s = this.sortInfo, f = s.field;
824 var st = this.fields.get(f).sortType;
825 var fn = function(r1, r2){
826 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
829 this.data.sort(s.direction, fn);
830 if(this.snapshot && this.snapshot != this.data){
831 this.snapshot.sort(s.direction, fn);
837 * Sets the default sort column and order to be used by the next load operation.
838 * @param {String} fieldName The name of the field to sort by.
839 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
841 setDefaultSort : function(field, dir){
842 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
847 * If remote sorting is used, the sort is performed on the server, and the cache is
848 * reloaded. If local sorting is used, the cache is sorted internally.
849 * @param {String} fieldName The name of the field to sort by.
850 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
852 sort : function(fieldName, dir){
853 var f = this.fields.get(fieldName);
855 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
857 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
863 this.sortToggle[f.name] = dir;
864 this.sortInfo = {field: f.name, direction: dir};
865 if(!this.remoteSort){
867 this.fireEvent("datachanged", this);
869 this.load(this.lastOptions);
874 * Calls the specified function for each of the Records in the cache.
875 * @param {Function} fn The function to call. The Record is passed as the first parameter.
876 * Returning <em>false</em> aborts and exits the iteration.
877 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
879 each : function(fn, scope){
880 this.data.each(fn, scope);
884 * Gets all records modified since the last commit. Modified records are persisted across load operations
885 * (e.g., during paging).
886 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
888 getModifiedRecords : function(){
889 return this.modified;
893 createFilterFn : function(property, value, anyMatch){
894 if(!value.exec){ // not a regex
895 value = String(value);
896 if(value.length == 0){
899 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
902 return value.test(r.data[property]);
907 * Sums the value of <i>property</i> for each record between start and end and returns the result.
908 * @param {String} property A field on your records
909 * @param {Number} start The record index to start at (defaults to 0)
910 * @param {Number} end The last record index to include (defaults to length - 1)
911 * @return {Number} The sum
913 sum : function(property, start, end){
914 var rs = this.data.items, v = 0;
916 end = (end || end === 0) ? end : rs.length-1;
918 for(var i = start; i <= end; i++){
919 v += (rs[i].data[property] || 0);
925 * Filter the records by a specified property.
926 * @param {String} field A field on your records
927 * @param {String/RegExp} value Either a string that the field
928 * should start with or a RegExp to test against the field
929 * @param {Boolean} anyMatch True to match any part not just the beginning
931 filter : function(property, value, anyMatch){
932 var fn = this.createFilterFn(property, value, anyMatch);
933 return fn ? this.filterBy(fn) : this.clearFilter();
937 * Filter by a function. The specified function will be called with each
938 * record in this data source. If the function returns true the record is included,
939 * otherwise it is filtered.
940 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941 * @param {Object} scope (optional) The scope of the function (defaults to this)
943 filterBy : function(fn, scope){
944 this.snapshot = this.snapshot || this.data;
945 this.data = this.queryBy(fn, scope||this);
946 this.fireEvent("datachanged", this);
950 * Query the records by a specified property.
951 * @param {String} field A field on your records
952 * @param {String/RegExp} value Either a string that the field
953 * should start with or a RegExp to test against the field
954 * @param {Boolean} anyMatch True to match any part not just the beginning
955 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
957 query : function(property, value, anyMatch){
958 var fn = this.createFilterFn(property, value, anyMatch);
959 return fn ? this.queryBy(fn) : this.data.clone();
963 * Query by a function. The specified function will be called with each
964 * record in this data source. If the function returns true the record is included
966 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967 * @param {Object} scope (optional) The scope of the function (defaults to this)
968 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
970 queryBy : function(fn, scope){
971 var data = this.snapshot || this.data;
972 return data.filterBy(fn, scope||this);
976 * Collects unique values for a particular dataIndex from this store.
977 * @param {String} dataIndex The property to collect
978 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980 * @return {Array} An array of the unique values
982 collect : function(dataIndex, allowNull, bypassFilter){
983 var d = (bypassFilter === true && this.snapshot) ?
984 this.snapshot.items : this.data.items;
985 var v, sv, r = [], l = {};
986 for(var i = 0, len = d.length; i < len; i++){
987 v = d[i].data[dataIndex];
989 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
998 * Revert to a view of the Record cache with no filtering applied.
999 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1001 clearFilter : function(suppressEvent){
1002 if(this.snapshot && this.snapshot != this.data){
1003 this.data = this.snapshot;
1004 delete this.snapshot;
1005 if(suppressEvent !== true){
1006 this.fireEvent("datachanged", this);
1012 afterEdit : function(record){
1013 if(this.modified.indexOf(record) == -1){
1014 this.modified.push(record);
1016 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1020 afterReject : function(record){
1021 this.modified.remove(record);
1022 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1026 afterCommit : function(record){
1027 this.modified.remove(record);
1028 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1032 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1035 commitChanges : function(){
1036 var m = this.modified.slice(0);
1038 for(var i = 0, len = m.length; i < len; i++){
1044 * Cancel outstanding changes on all changed records.
1046 rejectChanges : function(){
1047 var m = this.modified.slice(0);
1049 for(var i = 0, len = m.length; i < len; i++){
1054 onMetaChange : function(meta, rtype, o){
1055 this.recordType = rtype;
1056 this.fields = rtype.prototype.fields;
1057 delete this.snapshot;
1058 this.sortInfo = meta.sortInfo || this.sortInfo;
1060 this.fireEvent('metachange', this, this.reader.meta);
1063 moveIndex : function(data, type)
1065 var index = this.indexOf(data);
1067 var newIndex = index + type;
1071 this.insert(newIndex, data);
1076 * Ext JS Library 1.1.1
1077 * Copyright(c) 2006-2007, Ext JS, LLC.
1079 * Originally Released Under LGPL - original licence link has changed is not relivant.
1082 * <script type="text/javascript">
1086 * @class Roo.data.SimpleStore
1087 * @extends Roo.data.Store
1088 * Small helper class to make creating Stores from Array data easier.
1089 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090 * @cfg {Array} fields An array of field definition objects, or field name strings.
1091 * @cfg {Object} an existing reader (eg. copied from another store)
1092 * @cfg {Array} data The multi-dimensional array of data
1094 * @param {Object} config
1096 Roo.data.SimpleStore = function(config)
1098 Roo.data.SimpleStore.superclass.constructor.call(this, {
1100 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1103 Roo.data.Record.create(config.fields)
1105 proxy : new Roo.data.MemoryProxy(config.data)
1109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1111 * Ext JS Library 1.1.1
1112 * Copyright(c) 2006-2007, Ext JS, LLC.
1114 * Originally Released Under LGPL - original licence link has changed is not relivant.
1117 * <script type="text/javascript">
1122 * @extends Roo.data.Store
1123 * @class Roo.data.JsonStore
1124 * Small helper class to make creating Stores for JSON data easier. <br/>
1126 var store = new Roo.data.JsonStore({
1127 url: 'get-images.php',
1129 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1132 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1133 * JsonReader and HttpProxy (unless inline data is provided).</b>
1134 * @cfg {Array} fields An array of field definition objects, or field name strings.
1136 * @param {Object} config
1138 Roo.data.JsonStore = function(c){
1139 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1140 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1141 reader: new Roo.data.JsonReader(c, c.fields)
1144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1146 * Ext JS Library 1.1.1
1147 * Copyright(c) 2006-2007, Ext JS, LLC.
1149 * Originally Released Under LGPL - original licence link has changed is not relivant.
1152 * <script type="text/javascript">
1156 Roo.data.Field = function(config){
1157 if(typeof config == "string"){
1158 config = {name: config};
1160 Roo.apply(this, config);
1166 var st = Roo.data.SortTypes;
1167 // named sortTypes are supported, here we look them up
1168 if(typeof this.sortType == "string"){
1169 this.sortType = st[this.sortType];
1172 // set default sortType for strings and dates
1176 this.sortType = st.asUCString;
1179 this.sortType = st.asDate;
1182 this.sortType = st.none;
1187 var stripRe = /[\$,%]/g;
1189 // prebuilt conversion function for this field, instead of
1190 // switching every time we're reading a value
1192 var cv, dateFormat = this.dateFormat;
1197 cv = function(v){ return v; };
1200 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1204 return v !== undefined && v !== null && v !== '' ?
1205 parseInt(String(v).replace(stripRe, ""), 10) : '';
1210 return v !== undefined && v !== null && v !== '' ?
1211 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1216 cv = function(v){ return v === true || v === "true" || v == 1; };
1223 if(v instanceof Date){
1227 if(dateFormat == "timestamp"){
1228 return new Date(v*1000);
1230 return Date.parseDate(v, dateFormat);
1232 var parsed = Date.parse(v);
1233 return parsed ? new Date(parsed) : null;
1242 Roo.data.Field.prototype = {
1250 * Ext JS Library 1.1.1
1251 * Copyright(c) 2006-2007, Ext JS, LLC.
1253 * Originally Released Under LGPL - original licence link has changed is not relivant.
1256 * <script type="text/javascript">
1259 // Base class for reading structured data from a data source. This class is intended to be
1260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1263 * @class Roo.data.DataReader
1264 * Base class for reading structured data from a data source. This class is intended to be
1265 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1268 Roo.data.DataReader = function(meta, recordType){
1272 this.recordType = recordType instanceof Array ?
1273 Roo.data.Record.create(recordType) : recordType;
1276 Roo.data.DataReader.prototype = {
1279 readerType : 'Data',
1281 * Create an empty record
1282 * @param {Object} data (optional) - overlay some values
1283 * @return {Roo.data.Record} record created.
1285 newRow : function(d) {
1287 this.recordType.prototype.fields.each(function(c) {
1289 case 'int' : da[c.name] = 0; break;
1290 case 'date' : da[c.name] = new Date(); break;
1291 case 'float' : da[c.name] = 0.0; break;
1292 case 'boolean' : da[c.name] = false; break;
1293 default : da[c.name] = ""; break;
1297 return new this.recordType(Roo.apply(da, d));
1303 * Ext JS Library 1.1.1
1304 * Copyright(c) 2006-2007, Ext JS, LLC.
1306 * Originally Released Under LGPL - original licence link has changed is not relivant.
1309 * <script type="text/javascript">
1313 * @class Roo.data.DataProxy
1314 * @extends Roo.data.Observable
1315 * This class is an abstract base class for implementations which provide retrieval of
1316 * unformatted data objects.<br>
1318 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1319 * (of the appropriate type which knows how to parse the data object) to provide a block of
1320 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1322 * Custom implementations must implement the load method as described in
1323 * {@link Roo.data.HttpProxy#load}.
1325 Roo.data.DataProxy = function(){
1329 * Fires before a network request is made to retrieve a data object.
1330 * @param {Object} This DataProxy object.
1331 * @param {Object} params The params parameter to the load function.
1336 * Fires before the load method's callback is called.
1337 * @param {Object} This DataProxy object.
1338 * @param {Object} o The data object.
1339 * @param {Object} arg The callback argument object passed to the load function.
1343 * @event loadexception
1344 * Fires if an Exception occurs during data retrieval.
1345 * @param {Object} This DataProxy object.
1346 * @param {Object} o The data object.
1347 * @param {Object} arg The callback argument object passed to the load function.
1348 * @param {Object} e The Exception.
1350 loadexception : true
1352 Roo.data.DataProxy.superclass.constructor.call(this);
1355 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1358 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1362 * Ext JS Library 1.1.1
1363 * Copyright(c) 2006-2007, Ext JS, LLC.
1365 * Originally Released Under LGPL - original licence link has changed is not relivant.
1368 * <script type="text/javascript">
1371 * @class Roo.data.MemoryProxy
1372 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1373 * to the Reader when its load method is called.
1375 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1377 Roo.data.MemoryProxy = function(data){
1381 Roo.data.MemoryProxy.superclass.constructor.call(this);
1385 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1388 * Load data from the requested source (in this case an in-memory
1389 * data object passed to the constructor), read the data object into
1390 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1391 * process that block using the passed callback.
1392 * @param {Object} params This parameter is not used by the MemoryProxy class.
1393 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1394 * object into a block of Roo.data.Records.
1395 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1396 * The function must be passed <ul>
1397 * <li>The Record block object</li>
1398 * <li>The "arg" argument from the load function</li>
1399 * <li>A boolean success indicator</li>
1401 * @param {Object} scope The scope in which to call the callback
1402 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1404 load : function(params, reader, callback, scope, arg){
1405 params = params || {};
1408 result = reader.readRecords(params.data ? params.data :this.data);
1410 this.fireEvent("loadexception", this, arg, null, e);
1411 callback.call(scope, null, arg, false);
1414 callback.call(scope, result, arg, true);
1418 update : function(params, records){
1423 * Ext JS Library 1.1.1
1424 * Copyright(c) 2006-2007, Ext JS, LLC.
1426 * Originally Released Under LGPL - original licence link has changed is not relivant.
1429 * <script type="text/javascript">
1432 * @class Roo.data.HttpProxy
1433 * @extends Roo.data.DataProxy
1434 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1435 * configured to reference a certain URL.<br><br>
1437 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1438 * from which the running page was served.<br><br>
1440 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1442 * Be aware that to enable the browser to parse an XML document, the server must set
1443 * the Content-Type header in the HTTP response to "text/xml".
1445 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1446 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1447 * will be used to make the request.
1449 Roo.data.HttpProxy = function(conn){
1450 Roo.data.HttpProxy.superclass.constructor.call(this);
1451 // is conn a conn config or a real conn?
1453 this.useAjax = !conn || !conn.events;
1457 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1458 // thse are take from connection...
1461 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1464 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1465 * extra parameters to each request made by this object. (defaults to undefined)
1468 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1469 * to each request made by this object. (defaults to undefined)
1472 * @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)
1475 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1478 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1484 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1488 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1489 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1490 * a finer-grained basis than the DataProxy events.
1492 getConnection : function(){
1493 return this.useAjax ? Roo.Ajax : this.conn;
1497 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1498 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1499 * process that block using the passed callback.
1500 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1501 * for the request to the remote server.
1502 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1503 * object into a block of Roo.data.Records.
1504 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1505 * The function must be passed <ul>
1506 * <li>The Record block object</li>
1507 * <li>The "arg" argument from the load function</li>
1508 * <li>A boolean success indicator</li>
1510 * @param {Object} scope The scope in which to call the callback
1511 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1513 load : function(params, reader, callback, scope, arg){
1514 if(this.fireEvent("beforeload", this, params) !== false){
1516 params : params || {},
1518 callback : callback,
1523 callback : this.loadResponse,
1527 Roo.applyIf(o, this.conn);
1528 if(this.activeRequest){
1529 Roo.Ajax.abort(this.activeRequest);
1531 this.activeRequest = Roo.Ajax.request(o);
1533 this.conn.request(o);
1536 callback.call(scope||this, null, arg, false);
1541 loadResponse : function(o, success, response){
1542 delete this.activeRequest;
1544 this.fireEvent("loadexception", this, o, response);
1545 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1550 result = o.reader.read(response);
1552 this.fireEvent("loadexception", this, o, response, e);
1553 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1557 this.fireEvent("load", this, o, o.request.arg);
1558 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1562 update : function(dataSet){
1567 updateResponse : function(dataSet){
1572 * Ext JS Library 1.1.1
1573 * Copyright(c) 2006-2007, Ext JS, LLC.
1575 * Originally Released Under LGPL - original licence link has changed is not relivant.
1578 * <script type="text/javascript">
1582 * @class Roo.data.ScriptTagProxy
1583 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1584 * other than the originating domain of the running page.<br><br>
1586 * <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
1587 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1589 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1590 * source code that is used as the source inside a <script> tag.<br><br>
1592 * In order for the browser to process the returned data, the server must wrap the data object
1593 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1594 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1595 * depending on whether the callback name was passed:
1598 boolean scriptTag = false;
1599 String cb = request.getParameter("callback");
1602 response.setContentType("text/javascript");
1604 response.setContentType("application/x-json");
1606 Writer out = response.getWriter();
1608 out.write(cb + "(");
1610 out.print(dataBlock.toJsonString());
1617 * @param {Object} config A configuration object.
1619 Roo.data.ScriptTagProxy = function(config){
1620 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1621 Roo.apply(this, config);
1622 this.head = document.getElementsByTagName("head")[0];
1625 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1627 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1629 * @cfg {String} url The URL from which to request the data object.
1632 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1636 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1637 * the server the name of the callback function set up by the load call to process the returned data object.
1638 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1639 * javascript output which calls this named function passing the data object as its only parameter.
1641 callbackParam : "callback",
1643 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1644 * name to the request.
1649 * Load data from the configured URL, read the data object into
1650 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1651 * process that block using the passed callback.
1652 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1653 * for the request to the remote server.
1654 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1655 * object into a block of Roo.data.Records.
1656 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1657 * The function must be passed <ul>
1658 * <li>The Record block object</li>
1659 * <li>The "arg" argument from the load function</li>
1660 * <li>A boolean success indicator</li>
1662 * @param {Object} scope The scope in which to call the callback
1663 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1665 load : function(params, reader, callback, scope, arg){
1666 if(this.fireEvent("beforeload", this, params) !== false){
1668 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1671 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1673 url += "&_dc=" + (new Date().getTime());
1675 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1678 cb : "stcCallback"+transId,
1679 scriptId : "stcScript"+transId,
1683 callback : callback,
1689 window[trans.cb] = function(o){
1690 conn.handleResponse(o, trans);
1693 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1695 if(this.autoAbort !== false){
1699 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1701 var script = document.createElement("script");
1702 script.setAttribute("src", url);
1703 script.setAttribute("type", "text/javascript");
1704 script.setAttribute("id", trans.scriptId);
1705 this.head.appendChild(script);
1709 callback.call(scope||this, null, arg, false);
1714 isLoading : function(){
1715 return this.trans ? true : false;
1719 * Abort the current server request.
1722 if(this.isLoading()){
1723 this.destroyTrans(this.trans);
1728 destroyTrans : function(trans, isLoaded){
1729 this.head.removeChild(document.getElementById(trans.scriptId));
1730 clearTimeout(trans.timeoutId);
1732 window[trans.cb] = undefined;
1734 delete window[trans.cb];
1737 // if hasn't been loaded, wait for load to remove it to prevent script error
1738 window[trans.cb] = function(){
1739 window[trans.cb] = undefined;
1741 delete window[trans.cb];
1748 handleResponse : function(o, trans){
1750 this.destroyTrans(trans, true);
1753 result = trans.reader.readRecords(o);
1755 this.fireEvent("loadexception", this, o, trans.arg, e);
1756 trans.callback.call(trans.scope||window, null, trans.arg, false);
1759 this.fireEvent("load", this, o, trans.arg);
1760 trans.callback.call(trans.scope||window, result, trans.arg, true);
1764 handleFailure : function(trans){
1766 this.destroyTrans(trans, false);
1767 this.fireEvent("loadexception", this, null, trans.arg);
1768 trans.callback.call(trans.scope||window, null, trans.arg, false);
1772 * Ext JS Library 1.1.1
1773 * Copyright(c) 2006-2007, Ext JS, LLC.
1775 * Originally Released Under LGPL - original licence link has changed is not relivant.
1778 * <script type="text/javascript">
1782 * @class Roo.data.JsonReader
1783 * @extends Roo.data.DataReader
1784 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1785 * based on mappings in a provided Roo.data.Record constructor.
1787 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1788 * in the reply previously.
1793 var RecordDef = Roo.data.Record.create([
1794 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1795 {name: 'occupation'} // This field will use "occupation" as the mapping.
1797 var myReader = new Roo.data.JsonReader({
1798 totalProperty: "results", // The property which contains the total dataset size (optional)
1799 root: "rows", // The property which contains an Array of row objects
1800 id: "id" // The property within each row object that provides an ID for the record (optional)
1804 * This would consume a JSON file like this:
1806 { 'results': 2, 'rows': [
1807 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1808 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1811 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1812 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1813 * paged from the remote server.
1814 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1815 * @cfg {String} root name of the property which contains the Array of row objects.
1816 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1817 * @cfg {Array} fields Array of field definition objects
1819 * Create a new JsonReader
1820 * @param {Object} meta Metadata configuration options
1821 * @param {Object} recordType Either an Array of field definition objects,
1822 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1824 Roo.data.JsonReader = function(meta, recordType){
1827 // set some defaults:
1829 totalProperty: 'total',
1830 successProperty : 'success',
1835 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1837 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1839 readerType : 'Json',
1842 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1843 * Used by Store query builder to append _requestMeta to params.
1846 metaFromRemote : false,
1848 * This method is only used by a DataProxy which has retrieved data from a remote server.
1849 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1850 * @return {Object} data A data block which is used by an Roo.data.Store object as
1851 * a cache of Roo.data.Records.
1853 read : function(response){
1854 var json = response.responseText;
1856 var o = /* eval:var:o */ eval("("+json+")");
1858 throw {message: "JsonReader.read: Json object not found"};
1864 this.metaFromRemote = true;
1865 this.meta = o.metaData;
1866 this.recordType = Roo.data.Record.create(o.metaData.fields);
1867 this.onMetaChange(this.meta, this.recordType, o);
1869 return this.readRecords(o);
1872 // private function a store will implement
1873 onMetaChange : function(meta, recordType, o){
1880 simpleAccess: function(obj, subsc) {
1887 getJsonAccessor: function(){
1889 return function(expr) {
1891 return(re.test(expr))
1892 ? new Function("obj", "return obj." + expr)
1902 * Create a data block containing Roo.data.Records from an XML document.
1903 * @param {Object} o An object which contains an Array of row objects in the property specified
1904 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1905 * which contains the total size of the dataset.
1906 * @return {Object} data A data block which is used by an Roo.data.Store object as
1907 * a cache of Roo.data.Records.
1909 readRecords : function(o){
1911 * After any data loads, the raw JSON data is available for further custom processing.
1915 var s = this.meta, Record = this.recordType,
1916 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1918 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1920 if(s.totalProperty) {
1921 this.getTotal = this.getJsonAccessor(s.totalProperty);
1923 if(s.successProperty) {
1924 this.getSuccess = this.getJsonAccessor(s.successProperty);
1926 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1928 var g = this.getJsonAccessor(s.id);
1929 this.getId = function(rec) {
1931 return (r === undefined || r === "") ? null : r;
1934 this.getId = function(){return null;};
1937 for(var jj = 0; jj < fl; jj++){
1939 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1940 this.ef[jj] = this.getJsonAccessor(map);
1944 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1945 if(s.totalProperty){
1946 var vt = parseInt(this.getTotal(o), 10);
1951 if(s.successProperty){
1952 var vs = this.getSuccess(o);
1953 if(vs === false || vs === 'false'){
1958 for(var i = 0; i < c; i++){
1961 var id = this.getId(n);
1962 for(var j = 0; j < fl; j++){
1964 var v = this.ef[j](n);
1966 Roo.log('missing convert for ' + f.name);
1970 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1972 var record = new Record(values, id);
1974 records[i] = record;
1980 totalRecords : totalRecords
1985 * Ext JS Library 1.1.1
1986 * Copyright(c) 2006-2007, Ext JS, LLC.
1988 * Originally Released Under LGPL - original licence link has changed is not relivant.
1991 * <script type="text/javascript">
1995 * @class Roo.data.XmlReader
1996 * @extends Roo.data.DataReader
1997 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1998 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2000 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2001 * header in the HTTP response must be set to "text/xml".</em>
2005 var RecordDef = Roo.data.Record.create([
2006 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2007 {name: 'occupation'} // This field will use "occupation" as the mapping.
2009 var myReader = new Roo.data.XmlReader({
2010 totalRecords: "results", // The element which contains the total dataset size (optional)
2011 record: "row", // The repeated element which contains row information
2012 id: "id" // The element within the row that provides an ID for the record (optional)
2016 * This would consume an XML file like this:
2020 <results>2</results>
2023 <name>Bill</name>
2024 <occupation>Gardener</occupation>
2028 <name>Ben</name>
2029 <occupation>Horticulturalist</occupation>
2033 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2034 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2035 * paged from the remote server.
2036 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2037 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2038 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2039 * a record identifier value.
2041 * Create a new XmlReader
2042 * @param {Object} meta Metadata configuration options
2043 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2044 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2045 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2047 Roo.data.XmlReader = function(meta, recordType){
2049 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2051 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2056 * This method is only used by a DataProxy which has retrieved data from a remote server.
2057 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2058 * to contain a method called 'responseXML' that returns an XML document object.
2059 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2060 * a cache of Roo.data.Records.
2062 read : function(response){
2063 var doc = response.responseXML;
2065 throw {message: "XmlReader.read: XML Document not available"};
2067 return this.readRecords(doc);
2071 * Create a data block containing Roo.data.Records from an XML document.
2072 * @param {Object} doc A parsed XML document.
2073 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2074 * a cache of Roo.data.Records.
2076 readRecords : function(doc){
2078 * After any data loads/reads, the raw XML Document is available for further custom processing.
2082 var root = doc.documentElement || doc;
2083 var q = Roo.DomQuery;
2084 var recordType = this.recordType, fields = recordType.prototype.fields;
2085 var sid = this.meta.id;
2086 var totalRecords = 0, success = true;
2087 if(this.meta.totalRecords){
2088 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2091 if(this.meta.success){
2092 var sv = q.selectValue(this.meta.success, root, true);
2093 success = sv !== false && sv !== 'false';
2096 var ns = q.select(this.meta.record, root);
2097 for(var i = 0, len = ns.length; i < len; i++) {
2100 var id = sid ? q.selectValue(sid, n) : undefined;
2101 for(var j = 0, jlen = fields.length; j < jlen; j++){
2102 var f = fields.items[j];
2103 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2107 var record = new recordType(values, id);
2109 records[records.length] = record;
2115 totalRecords : totalRecords || records.length
2120 * Ext JS Library 1.1.1
2121 * Copyright(c) 2006-2007, Ext JS, LLC.
2123 * Originally Released Under LGPL - original licence link has changed is not relivant.
2126 * <script type="text/javascript">
2130 * @class Roo.data.ArrayReader
2131 * @extends Roo.data.DataReader
2132 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2133 * Each element of that Array represents a row of data fields. The
2134 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2135 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2139 var RecordDef = Roo.data.Record.create([
2140 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2141 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2143 var myReader = new Roo.data.ArrayReader({
2144 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2148 * This would consume an Array like this:
2150 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2154 * Create a new JsonReader
2155 * @param {Object} meta Metadata configuration options.
2156 * @param {Object|Array} recordType Either an Array of field definition objects
2158 * @cfg {Array} fields Array of field definition objects
2159 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2160 * as specified to {@link Roo.data.Record#create},
2161 * or an {@link Roo.data.Record} object
2164 * created using {@link Roo.data.Record#create}.
2166 Roo.data.ArrayReader = function(meta, recordType)
2168 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2171 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2173 readerType : 'Array',
2175 * Create a data block containing Roo.data.Records from an XML document.
2176 * @param {Object} o An Array of row objects which represents the dataset.
2177 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2178 * a cache of Roo.data.Records.
2180 readRecords : function(o)
2182 var sid = this.meta ? this.meta.id : null;
2183 var recordType = this.recordType, fields = recordType.prototype.fields;
2186 for(var i = 0; i < root.length; i++){
2189 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2190 for(var j = 0, jlen = fields.length; j < jlen; j++){
2191 var f = fields.items[j];
2192 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2193 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2197 var record = new recordType(values, id);
2199 records[records.length] = record;
2203 totalRecords : records.length
2208 * Ext JS Library 1.1.1
2209 * Copyright(c) 2006-2007, Ext JS, LLC.
2211 * Originally Released Under LGPL - original licence link has changed is not relivant.
2214 * <script type="text/javascript">
2219 * @class Roo.data.Tree
2220 * @extends Roo.util.Observable
2221 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2222 * in the tree have most standard DOM functionality.
2224 * @param {Node} root (optional) The root node
2226 Roo.data.Tree = function(root){
2229 * The root node for this tree
2234 this.setRootNode(root);
2239 * Fires when a new child node is appended to a node in this tree.
2240 * @param {Tree} tree The owner tree
2241 * @param {Node} parent The parent node
2242 * @param {Node} node The newly appended node
2243 * @param {Number} index The index of the newly appended node
2248 * Fires when a child node is removed from a node in this tree.
2249 * @param {Tree} tree The owner tree
2250 * @param {Node} parent The parent node
2251 * @param {Node} node The child node removed
2256 * Fires when a node is moved to a new location in the tree
2257 * @param {Tree} tree The owner tree
2258 * @param {Node} node The node moved
2259 * @param {Node} oldParent The old parent of this node
2260 * @param {Node} newParent The new parent of this node
2261 * @param {Number} index The index it was moved to
2266 * Fires when a new child node is inserted in a node in this tree.
2267 * @param {Tree} tree The owner tree
2268 * @param {Node} parent The parent node
2269 * @param {Node} node The child node inserted
2270 * @param {Node} refNode The child node the node was inserted before
2274 * @event beforeappend
2275 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2276 * @param {Tree} tree The owner tree
2277 * @param {Node} parent The parent node
2278 * @param {Node} node The child node to be appended
2280 "beforeappend" : true,
2282 * @event beforeremove
2283 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2284 * @param {Tree} tree The owner tree
2285 * @param {Node} parent The parent node
2286 * @param {Node} node The child node to be removed
2288 "beforeremove" : true,
2291 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2292 * @param {Tree} tree The owner tree
2293 * @param {Node} node The node being moved
2294 * @param {Node} oldParent The parent of the node
2295 * @param {Node} newParent The new parent the node is moving to
2296 * @param {Number} index The index it is being moved to
2298 "beforemove" : true,
2300 * @event beforeinsert
2301 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2302 * @param {Tree} tree The owner tree
2303 * @param {Node} parent The parent node
2304 * @param {Node} node The child node to be inserted
2305 * @param {Node} refNode The child node the node is being inserted before
2307 "beforeinsert" : true
2310 Roo.data.Tree.superclass.constructor.call(this);
2313 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2316 proxyNodeEvent : function(){
2317 return this.fireEvent.apply(this, arguments);
2321 * Returns the root node for this tree.
2324 getRootNode : function(){
2329 * Sets the root node for this tree.
2330 * @param {Node} node
2333 setRootNode : function(node){
2335 node.ownerTree = this;
2337 this.registerNode(node);
2342 * Gets a node in this tree by its id.
2343 * @param {String} id
2346 getNodeById : function(id){
2347 return this.nodeHash[id];
2350 registerNode : function(node){
2351 this.nodeHash[node.id] = node;
2354 unregisterNode : function(node){
2355 delete this.nodeHash[node.id];
2358 toString : function(){
2359 return "[Tree"+(this.id?" "+this.id:"")+"]";
2364 * @class Roo.data.Node
2365 * @extends Roo.util.Observable
2366 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2367 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2369 * @param {Object} attributes The attributes/config for the node
2371 Roo.data.Node = function(attributes){
2373 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2376 this.attributes = attributes || {};
2377 this.leaf = this.attributes.leaf;
2379 * The node id. @type String
2381 this.id = this.attributes.id;
2383 this.id = Roo.id(null, "ynode-");
2384 this.attributes.id = this.id;
2389 * All child nodes of this node. @type Array
2391 this.childNodes = [];
2392 if(!this.childNodes.indexOf){ // indexOf is a must
2393 this.childNodes.indexOf = function(o){
2394 for(var i = 0, len = this.length; i < len; i++){
2403 * The parent node for this node. @type Node
2405 this.parentNode = null;
2407 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2409 this.firstChild = null;
2411 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2413 this.lastChild = null;
2415 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2417 this.previousSibling = null;
2419 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2421 this.nextSibling = null;
2426 * Fires when a new child node is appended
2427 * @param {Tree} tree The owner tree
2428 * @param {Node} this This node
2429 * @param {Node} node The newly appended node
2430 * @param {Number} index The index of the newly appended node
2435 * Fires when a child node is removed
2436 * @param {Tree} tree The owner tree
2437 * @param {Node} this This node
2438 * @param {Node} node The removed node
2443 * Fires when this node is moved to a new location in the tree
2444 * @param {Tree} tree The owner tree
2445 * @param {Node} this This node
2446 * @param {Node} oldParent The old parent of this node
2447 * @param {Node} newParent The new parent of this node
2448 * @param {Number} index The index it was moved to
2453 * Fires when a new child node is inserted.
2454 * @param {Tree} tree The owner tree
2455 * @param {Node} this This node
2456 * @param {Node} node The child node inserted
2457 * @param {Node} refNode The child node the node was inserted before
2461 * @event beforeappend
2462 * Fires before a new child is appended, return false to cancel the append.
2463 * @param {Tree} tree The owner tree
2464 * @param {Node} this This node
2465 * @param {Node} node The child node to be appended
2467 "beforeappend" : true,
2469 * @event beforeremove
2470 * Fires before a child is removed, return false to cancel the remove.
2471 * @param {Tree} tree The owner tree
2472 * @param {Node} this This node
2473 * @param {Node} node The child node to be removed
2475 "beforeremove" : true,
2478 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2479 * @param {Tree} tree The owner tree
2480 * @param {Node} this This node
2481 * @param {Node} oldParent The parent of this node
2482 * @param {Node} newParent The new parent this node is moving to
2483 * @param {Number} index The index it is being moved to
2485 "beforemove" : true,
2487 * @event beforeinsert
2488 * Fires before a new child is inserted, return false to cancel the insert.
2489 * @param {Tree} tree The owner tree
2490 * @param {Node} this This node
2491 * @param {Node} node The child node to be inserted
2492 * @param {Node} refNode The child node the node is being inserted before
2494 "beforeinsert" : true
2496 this.listeners = this.attributes.listeners;
2497 Roo.data.Node.superclass.constructor.call(this);
2500 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2501 fireEvent : function(evtName){
2502 // first do standard event for this node
2503 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2506 // then bubble it up to the tree if the event wasn't cancelled
2507 var ot = this.getOwnerTree();
2509 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2517 * Returns true if this node is a leaf
2520 isLeaf : function(){
2521 return this.leaf === true;
2525 setFirstChild : function(node){
2526 this.firstChild = node;
2530 setLastChild : function(node){
2531 this.lastChild = node;
2536 * Returns true if this node is the last child of its parent
2539 isLast : function(){
2540 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2544 * Returns true if this node is the first child of its parent
2547 isFirst : function(){
2548 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2551 hasChildNodes : function(){
2552 return !this.isLeaf() && this.childNodes.length > 0;
2556 * Insert node(s) as the last child node of this node.
2557 * @param {Node/Array} node The node or Array of nodes to append
2558 * @return {Node} The appended node if single append, or null if an array was passed
2560 appendChild : function(node){
2562 if(node instanceof Array){
2564 }else if(arguments.length > 1){
2568 // if passed an array or multiple args do them one by one
2570 for(var i = 0, len = multi.length; i < len; i++) {
2571 this.appendChild(multi[i]);
2574 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2577 var index = this.childNodes.length;
2578 var oldParent = node.parentNode;
2579 // it's a move, make sure we move it cleanly
2581 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2584 oldParent.removeChild(node);
2587 index = this.childNodes.length;
2589 this.setFirstChild(node);
2591 this.childNodes.push(node);
2592 node.parentNode = this;
2593 var ps = this.childNodes[index-1];
2595 node.previousSibling = ps;
2596 ps.nextSibling = node;
2598 node.previousSibling = null;
2600 node.nextSibling = null;
2601 this.setLastChild(node);
2602 node.setOwnerTree(this.getOwnerTree());
2603 this.fireEvent("append", this.ownerTree, this, node, index);
2604 if(this.ownerTree) {
2605 this.ownerTree.fireEvent("appendnode", this, node, index);
2608 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2615 * Removes a child node from this node.
2616 * @param {Node} node The node to remove
2617 * @return {Node} The removed node
2619 removeChild : function(node){
2620 var index = this.childNodes.indexOf(node);
2624 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2628 // remove it from childNodes collection
2629 this.childNodes.splice(index, 1);
2632 if(node.previousSibling){
2633 node.previousSibling.nextSibling = node.nextSibling;
2635 if(node.nextSibling){
2636 node.nextSibling.previousSibling = node.previousSibling;
2639 // update child refs
2640 if(this.firstChild == node){
2641 this.setFirstChild(node.nextSibling);
2643 if(this.lastChild == node){
2644 this.setLastChild(node.previousSibling);
2647 node.setOwnerTree(null);
2648 // clear any references from the node
2649 node.parentNode = null;
2650 node.previousSibling = null;
2651 node.nextSibling = null;
2652 this.fireEvent("remove", this.ownerTree, this, node);
2657 * Inserts the first node before the second node in this nodes childNodes collection.
2658 * @param {Node} node The node to insert
2659 * @param {Node} refNode The node to insert before (if null the node is appended)
2660 * @return {Node} The inserted node
2662 insertBefore : function(node, refNode){
2663 if(!refNode){ // like standard Dom, refNode can be null for append
2664 return this.appendChild(node);
2667 if(node == refNode){
2671 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2674 var index = this.childNodes.indexOf(refNode);
2675 var oldParent = node.parentNode;
2676 var refIndex = index;
2678 // when moving internally, indexes will change after remove
2679 if(oldParent == this && this.childNodes.indexOf(node) < index){
2683 // it's a move, make sure we move it cleanly
2685 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2688 oldParent.removeChild(node);
2691 this.setFirstChild(node);
2693 this.childNodes.splice(refIndex, 0, node);
2694 node.parentNode = this;
2695 var ps = this.childNodes[refIndex-1];
2697 node.previousSibling = ps;
2698 ps.nextSibling = node;
2700 node.previousSibling = null;
2702 node.nextSibling = refNode;
2703 refNode.previousSibling = node;
2704 node.setOwnerTree(this.getOwnerTree());
2705 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2707 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2713 * Returns the child node at the specified index.
2714 * @param {Number} index
2717 item : function(index){
2718 return this.childNodes[index];
2722 * Replaces one child node in this node with another.
2723 * @param {Node} newChild The replacement node
2724 * @param {Node} oldChild The node to replace
2725 * @return {Node} The replaced node
2727 replaceChild : function(newChild, oldChild){
2728 this.insertBefore(newChild, oldChild);
2729 this.removeChild(oldChild);
2734 * Returns the index of a child node
2735 * @param {Node} node
2736 * @return {Number} The index of the node or -1 if it was not found
2738 indexOf : function(child){
2739 return this.childNodes.indexOf(child);
2743 * Returns the tree this node is in.
2746 getOwnerTree : function(){
2747 // if it doesn't have one, look for one
2748 if(!this.ownerTree){
2752 this.ownerTree = p.ownerTree;
2758 return this.ownerTree;
2762 * Returns depth of this node (the root node has a depth of 0)
2765 getDepth : function(){
2768 while(p.parentNode){
2776 setOwnerTree : function(tree){
2777 // if it's move, we need to update everyone
2778 if(tree != this.ownerTree){
2780 this.ownerTree.unregisterNode(this);
2782 this.ownerTree = tree;
2783 var cs = this.childNodes;
2784 for(var i = 0, len = cs.length; i < len; i++) {
2785 cs[i].setOwnerTree(tree);
2788 tree.registerNode(this);
2794 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2795 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2796 * @return {String} The path
2798 getPath : function(attr){
2799 attr = attr || "id";
2800 var p = this.parentNode;
2801 var b = [this.attributes[attr]];
2803 b.unshift(p.attributes[attr]);
2806 var sep = this.getOwnerTree().pathSeparator;
2807 return sep + b.join(sep);
2811 * Bubbles up the tree from 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 bubble is stopped.
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 bubble : function(fn, scope, args){
2822 if(fn.call(scope || p, args || p) === false){
2830 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2831 * function call will be the scope provided or the current node. The arguments to the function
2832 * will be the args provided or the current node. If the function returns false at any point,
2833 * the cascade is stopped on that branch.
2834 * @param {Function} fn The function to call
2835 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2838 cascade : function(fn, scope, args){
2839 if(fn.call(scope || this, args || this) !== false){
2840 var cs = this.childNodes;
2841 for(var i = 0, len = cs.length; i < len; i++) {
2842 cs[i].cascade(fn, scope, args);
2848 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2849 * function call will be the scope provided or the current node. The arguments to the function
2850 * will be the args provided or the current node. If the function returns false at any point,
2851 * the iteration stops.
2852 * @param {Function} fn The function to call
2853 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2854 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2856 eachChild : function(fn, scope, args){
2857 var cs = this.childNodes;
2858 for(var i = 0, len = cs.length; i < len; i++) {
2859 if(fn.call(scope || this, args || cs[i]) === false){
2866 * Finds the first child that has the attribute with the specified value.
2867 * @param {String} attribute The attribute name
2868 * @param {Mixed} value The value to search for
2869 * @return {Node} The found child or null if none was found
2871 findChild : function(attribute, value){
2872 var cs = this.childNodes;
2873 for(var i = 0, len = cs.length; i < len; i++) {
2874 if(cs[i].attributes[attribute] == value){
2882 * Finds the first child by a custom function. The child matches if the function passed
2884 * @param {Function} fn
2885 * @param {Object} scope (optional)
2886 * @return {Node} The found child or null if none was found
2888 findChildBy : function(fn, scope){
2889 var cs = this.childNodes;
2890 for(var i = 0, len = cs.length; i < len; i++) {
2891 if(fn.call(scope||cs[i], cs[i]) === true){
2899 * Sorts this nodes children using the supplied sort function
2900 * @param {Function} fn
2901 * @param {Object} scope (optional)
2903 sort : function(fn, scope){
2904 var cs = this.childNodes;
2905 var len = cs.length;
2907 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2909 for(var i = 0; i < len; i++){
2911 n.previousSibling = cs[i-1];
2912 n.nextSibling = cs[i+1];
2914 this.setFirstChild(n);
2917 this.setLastChild(n);
2924 * Returns true if this node is an ancestor (at any point) of the passed node.
2925 * @param {Node} node
2928 contains : function(node){
2929 return node.isAncestor(this);
2933 * Returns true if the passed node is an ancestor (at any point) of this node.
2934 * @param {Node} node
2937 isAncestor : function(node){
2938 var p = this.parentNode;
2948 toString : function(){
2949 return "[Node"+(this.id?" "+this.id:"")+"]";
2953 * Ext JS Library 1.1.1
2954 * Copyright(c) 2006-2007, Ext JS, LLC.
2956 * Originally Released Under LGPL - original licence link has changed is not relivant.
2959 * <script type="text/javascript">
2964 * @extends Roo.Element
2965 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2966 * automatic maintaining of shadow/shim positions.
2967 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2968 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2969 * you can pass a string with a CSS class name. False turns off the shadow.
2970 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2971 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2972 * @cfg {String} cls CSS class to add to the element
2973 * @cfg {Number} zindex Starting z-index (defaults to 11000)
2974 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2976 * @param {Object} config An object with config options.
2977 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2980 Roo.Layer = function(config, existingEl){
2981 config = config || {};
2982 var dh = Roo.DomHelper;
2983 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2985 this.dom = Roo.getDom(existingEl);
2988 var o = config.dh || {tag: "div", cls: "x-layer"};
2989 this.dom = dh.append(pel, o);
2992 this.addClass(config.cls);
2994 this.constrain = config.constrain !== false;
2995 this.visibilityMode = Roo.Element.VISIBILITY;
2997 this.id = this.dom.id = config.id;
2999 this.id = Roo.id(this.dom);
3001 this.zindex = config.zindex || this.getZIndex();
3002 this.position("absolute", this.zindex);
3004 this.shadowOffset = config.shadowOffset || 4;
3005 this.shadow = new Roo.Shadow({
3006 offset : this.shadowOffset,
3007 mode : config.shadow
3010 this.shadowOffset = 0;
3012 this.useShim = config.shim !== false && Roo.useShims;
3013 this.useDisplay = config.useDisplay;
3017 var supr = Roo.Element.prototype;
3019 // shims are shared among layer to keep from having 100 iframes
3022 Roo.extend(Roo.Layer, Roo.Element, {
3024 getZIndex : function(){
3025 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3028 getShim : function(){
3035 var shim = shims.shift();
3037 shim = this.createShim();
3038 shim.enableDisplayMode('block');
3039 shim.dom.style.display = 'none';
3040 shim.dom.style.visibility = 'visible';
3042 var pn = this.dom.parentNode;
3043 if(shim.dom.parentNode != pn){
3044 pn.insertBefore(shim.dom, this.dom);
3046 shim.setStyle('z-index', this.getZIndex()-2);
3051 hideShim : function(){
3053 this.shim.setDisplayed(false);
3054 shims.push(this.shim);
3059 disableShadow : function(){
3061 this.shadowDisabled = true;
3063 this.lastShadowOffset = this.shadowOffset;
3064 this.shadowOffset = 0;
3068 enableShadow : function(show){
3070 this.shadowDisabled = false;
3071 this.shadowOffset = this.lastShadowOffset;
3072 delete this.lastShadowOffset;
3080 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3081 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3082 sync : function(doShow){
3083 var sw = this.shadow;
3084 if(!this.updating && this.isVisible() && (sw || this.useShim)){
3085 var sh = this.getShim();
3087 var w = this.getWidth(),
3088 h = this.getHeight();
3090 var l = this.getLeft(true),
3091 t = this.getTop(true);
3093 if(sw && !this.shadowDisabled){
3094 if(doShow && !sw.isVisible()){
3097 sw.realign(l, t, w, h);
3103 // fit the shim behind the shadow, so it is shimmed too
3104 var a = sw.adjusts, s = sh.dom.style;
3105 s.left = (Math.min(l, l+a.l))+"px";
3106 s.top = (Math.min(t, t+a.t))+"px";
3107 s.width = (w+a.w)+"px";
3108 s.height = (h+a.h)+"px";
3115 sh.setLeftTop(l, t);
3122 destroy : function(){
3127 this.removeAllListeners();
3128 var pn = this.dom.parentNode;
3130 pn.removeChild(this.dom);
3132 Roo.Element.uncache(this.id);
3135 remove : function(){
3140 beginUpdate : function(){
3141 this.updating = true;
3145 endUpdate : function(){
3146 this.updating = false;
3151 hideUnders : function(negOffset){
3159 constrainXY : function(){
3161 var vw = Roo.lib.Dom.getViewWidth(),
3162 vh = Roo.lib.Dom.getViewHeight();
3163 var s = Roo.get(document).getScroll();
3165 var xy = this.getXY();
3166 var x = xy[0], y = xy[1];
3167 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3168 // only move it if it needs it
3170 // first validate right/bottom
3171 if((x + w) > vw+s.left){
3172 x = vw - w - this.shadowOffset;
3175 if((y + h) > vh+s.top){
3176 y = vh - h - this.shadowOffset;
3179 // then make sure top/left isn't negative
3190 var ay = this.avoidY;
3191 if(y <= ay && (y+h) >= ay){
3197 supr.setXY.call(this, xy);
3203 isVisible : function(){
3204 return this.visible;
3208 showAction : function(){
3209 this.visible = true; // track visibility to prevent getStyle calls
3210 if(this.useDisplay === true){
3211 this.setDisplayed("");
3212 }else if(this.lastXY){
3213 supr.setXY.call(this, this.lastXY);
3214 }else if(this.lastLT){
3215 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3220 hideAction : function(){
3221 this.visible = false;
3222 if(this.useDisplay === true){
3223 this.setDisplayed(false);
3225 this.setLeftTop(-10000,-10000);
3229 // overridden Element method
3230 setVisible : function(v, a, d, c, e){
3235 var cb = function(){
3240 }.createDelegate(this);
3241 supr.setVisible.call(this, true, true, d, cb, e);
3244 this.hideUnders(true);
3253 }.createDelegate(this);
3255 supr.setVisible.call(this, v, a, d, cb, e);
3264 storeXY : function(xy){
3269 storeLeftTop : function(left, top){
3271 this.lastLT = [left, top];
3275 beforeFx : function(){
3276 this.beforeAction();
3277 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3281 afterFx : function(){
3282 Roo.Layer.superclass.afterFx.apply(this, arguments);
3283 this.sync(this.isVisible());
3287 beforeAction : function(){
3288 if(!this.updating && this.shadow){
3293 // overridden Element method
3294 setLeft : function(left){
3295 this.storeLeftTop(left, this.getTop(true));
3296 supr.setLeft.apply(this, arguments);
3300 setTop : function(top){
3301 this.storeLeftTop(this.getLeft(true), top);
3302 supr.setTop.apply(this, arguments);
3306 setLeftTop : function(left, top){
3307 this.storeLeftTop(left, top);
3308 supr.setLeftTop.apply(this, arguments);
3312 setXY : function(xy, a, d, c, e){
3314 this.beforeAction();
3316 var cb = this.createCB(c);
3317 supr.setXY.call(this, xy, a, d, cb, e);
3324 createCB : function(c){
3335 // overridden Element method
3336 setX : function(x, a, d, c, e){
3337 this.setXY([x, this.getY()], a, d, c, e);
3340 // overridden Element method
3341 setY : function(y, a, d, c, e){
3342 this.setXY([this.getX(), y], a, d, c, e);
3345 // overridden Element method
3346 setSize : function(w, h, a, d, c, e){
3347 this.beforeAction();
3348 var cb = this.createCB(c);
3349 supr.setSize.call(this, w, h, a, d, cb, e);
3355 // overridden Element method
3356 setWidth : function(w, a, d, c, e){
3357 this.beforeAction();
3358 var cb = this.createCB(c);
3359 supr.setWidth.call(this, w, a, d, cb, e);
3365 // overridden Element method
3366 setHeight : function(h, a, d, c, e){
3367 this.beforeAction();
3368 var cb = this.createCB(c);
3369 supr.setHeight.call(this, h, a, d, cb, e);
3375 // overridden Element method
3376 setBounds : function(x, y, w, h, a, d, c, e){
3377 this.beforeAction();
3378 var cb = this.createCB(c);
3380 this.storeXY([x, y]);
3381 supr.setXY.call(this, [x, y]);
3382 supr.setSize.call(this, w, h, a, d, cb, e);
3385 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3391 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3392 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3393 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3394 * @param {Number} zindex The new z-index to set
3395 * @return {this} The Layer
3397 setZIndex : function(zindex){
3398 this.zindex = zindex;
3399 this.setStyle("z-index", zindex + 2);
3401 this.shadow.setZIndex(zindex + 1);
3404 this.shim.setStyle("z-index", zindex);
3410 * Ext JS Library 1.1.1
3411 * Copyright(c) 2006-2007, Ext JS, LLC.
3413 * Originally Released Under LGPL - original licence link has changed is not relivant.
3416 * <script type="text/javascript">
3422 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3423 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3424 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3426 * Create a new Shadow
3427 * @param {Object} config The config object
3429 Roo.Shadow = function(config){
3430 Roo.apply(this, config);
3431 if(typeof this.mode != "string"){
3432 this.mode = this.defaultMode;
3434 var o = this.offset, a = {h: 0};
3435 var rad = Math.floor(this.offset/2);
3436 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3442 a.l -= this.offset + rad;
3443 a.t -= this.offset + rad;
3454 a.l -= (this.offset - rad);
3455 a.t -= this.offset + rad;
3457 a.w -= (this.offset - rad)*2;
3468 a.l -= (this.offset - rad);
3469 a.t -= (this.offset - rad);
3471 a.w -= (this.offset + rad + 1);
3472 a.h -= (this.offset + rad);
3481 Roo.Shadow.prototype = {
3483 * @cfg {String} mode
3484 * The shadow display mode. Supports the following options:<br />
3485 * sides: Shadow displays on both sides and bottom only<br />
3486 * frame: Shadow displays equally on all four sides<br />
3487 * drop: Traditional bottom-right drop shadow (default)
3490 * @cfg {String} offset
3491 * The number of pixels to offset the shadow from the element (defaults to 4)
3496 defaultMode: "drop",
3499 * Displays the shadow under the target element
3500 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3502 show : function(target){
3503 target = Roo.get(target);
3505 this.el = Roo.Shadow.Pool.pull();
3506 if(this.el.dom.nextSibling != target.dom){
3507 this.el.insertBefore(target);
3510 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3512 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3515 target.getLeft(true),
3516 target.getTop(true),
3520 this.el.dom.style.display = "block";
3524 * Returns true if the shadow is visible, else false
3526 isVisible : function(){
3527 return this.el ? true : false;
3531 * Direct alignment when values are already available. Show must be called at least once before
3532 * calling this method to ensure it is initialized.
3533 * @param {Number} left The target element left position
3534 * @param {Number} top The target element top position
3535 * @param {Number} width The target element width
3536 * @param {Number} height The target element height
3538 realign : function(l, t, w, h){
3542 var a = this.adjusts, d = this.el.dom, s = d.style;
3544 s.left = (l+a.l)+"px";
3545 s.top = (t+a.t)+"px";
3546 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3548 if(s.width != sws || s.height != shs){
3552 var cn = d.childNodes;
3553 var sww = Math.max(0, (sw-12))+"px";
3554 cn[0].childNodes[1].style.width = sww;
3555 cn[1].childNodes[1].style.width = sww;
3556 cn[2].childNodes[1].style.width = sww;
3557 cn[1].style.height = Math.max(0, (sh-12))+"px";
3567 this.el.dom.style.display = "none";
3568 Roo.Shadow.Pool.push(this.el);
3574 * Adjust the z-index of this shadow
3575 * @param {Number} zindex The new z-index
3577 setZIndex : function(z){
3580 this.el.setStyle("z-index", z);
3585 // Private utility class that manages the internal Shadow cache
3586 Roo.Shadow.Pool = function(){
3588 var markup = Roo.isIE ?
3589 '<div class="x-ie-shadow"></div>' :
3590 '<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>';
3595 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3596 sh.autoBoxAdjust = false;
3601 push : function(sh){
3607 * Ext JS Library 1.1.1
3608 * Copyright(c) 2006-2007, Ext JS, LLC.
3610 * Originally Released Under LGPL - original licence link has changed is not relivant.
3613 * <script type="text/javascript">
3618 * @class Roo.SplitBar
3619 * @extends Roo.util.Observable
3620 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3624 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3625 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3626 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3627 split.minSize = 100;
3628 split.maxSize = 600;
3629 split.animate = true;
3630 split.on('moved', splitterMoved);
3633 * Create a new SplitBar
3634 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3635 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3636 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3637 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3638 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3639 position of the SplitBar).
3641 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3644 this.el = Roo.get(dragElement, true);
3645 this.el.dom.unselectable = "on";
3647 this.resizingEl = Roo.get(resizingElement, true);
3651 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3652 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3655 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3658 * The minimum size of the resizing element. (Defaults to 0)
3664 * The maximum size of the resizing element. (Defaults to 2000)
3667 this.maxSize = 2000;
3670 * Whether to animate the transition to the new size
3673 this.animate = false;
3676 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3679 this.useShim = false;
3686 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3688 this.proxy = Roo.get(existingProxy).dom;
3691 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3694 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3697 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3700 this.dragSpecs = {};
3703 * @private The adapter to use to positon and resize elements
3705 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3706 this.adapter.init(this);
3708 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3710 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3711 this.el.addClass("x-splitbar-h");
3714 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3715 this.el.addClass("x-splitbar-v");
3721 * Fires when the splitter is moved (alias for {@link #event-moved})
3722 * @param {Roo.SplitBar} this
3723 * @param {Number} newSize the new width or height
3728 * Fires when the splitter is moved
3729 * @param {Roo.SplitBar} this
3730 * @param {Number} newSize the new width or height
3734 * @event beforeresize
3735 * Fires before the splitter is dragged
3736 * @param {Roo.SplitBar} this
3738 "beforeresize" : true,
3740 "beforeapply" : true
3743 Roo.util.Observable.call(this);
3746 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3747 onStartProxyDrag : function(x, y){
3748 this.fireEvent("beforeresize", this);
3750 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3752 o.enableDisplayMode("block");
3753 // all splitbars share the same overlay
3754 Roo.SplitBar.prototype.overlay = o;
3756 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3757 this.overlay.show();
3758 Roo.get(this.proxy).setDisplayed("block");
3759 var size = this.adapter.getElementSize(this);
3760 this.activeMinSize = this.getMinimumSize();;
3761 this.activeMaxSize = this.getMaximumSize();;
3762 var c1 = size - this.activeMinSize;
3763 var c2 = Math.max(this.activeMaxSize - size, 0);
3764 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3765 this.dd.resetConstraints();
3766 this.dd.setXConstraint(
3767 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3768 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3770 this.dd.setYConstraint(0, 0);
3772 this.dd.resetConstraints();
3773 this.dd.setXConstraint(0, 0);
3774 this.dd.setYConstraint(
3775 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3776 this.placement == Roo.SplitBar.TOP ? c2 : c1
3779 this.dragSpecs.startSize = size;
3780 this.dragSpecs.startPoint = [x, y];
3781 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3785 * @private Called after the drag operation by the DDProxy
3787 onEndProxyDrag : function(e){
3788 Roo.get(this.proxy).setDisplayed(false);
3789 var endPoint = Roo.lib.Event.getXY(e);
3791 this.overlay.hide();
3794 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3795 newSize = this.dragSpecs.startSize +
3796 (this.placement == Roo.SplitBar.LEFT ?
3797 endPoint[0] - this.dragSpecs.startPoint[0] :
3798 this.dragSpecs.startPoint[0] - endPoint[0]
3801 newSize = this.dragSpecs.startSize +
3802 (this.placement == Roo.SplitBar.TOP ?
3803 endPoint[1] - this.dragSpecs.startPoint[1] :
3804 this.dragSpecs.startPoint[1] - endPoint[1]
3807 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3808 if(newSize != this.dragSpecs.startSize){
3809 if(this.fireEvent('beforeapply', this, newSize) !== false){
3810 this.adapter.setElementSize(this, newSize);
3811 this.fireEvent("moved", this, newSize);
3812 this.fireEvent("resize", this, newSize);
3818 * Get the adapter this SplitBar uses
3819 * @return The adapter object
3821 getAdapter : function(){
3822 return this.adapter;
3826 * Set the adapter this SplitBar uses
3827 * @param {Object} adapter A SplitBar adapter object
3829 setAdapter : function(adapter){
3830 this.adapter = adapter;
3831 this.adapter.init(this);
3835 * Gets the minimum size for the resizing element
3836 * @return {Number} The minimum size
3838 getMinimumSize : function(){
3839 return this.minSize;
3843 * Sets the minimum size for the resizing element
3844 * @param {Number} minSize The minimum size
3846 setMinimumSize : function(minSize){
3847 this.minSize = minSize;
3851 * Gets the maximum size for the resizing element
3852 * @return {Number} The maximum size
3854 getMaximumSize : function(){
3855 return this.maxSize;
3859 * Sets the maximum size for the resizing element
3860 * @param {Number} maxSize The maximum size
3862 setMaximumSize : function(maxSize){
3863 this.maxSize = maxSize;
3867 * Sets the initialize size for the resizing element
3868 * @param {Number} size The initial size
3870 setCurrentSize : function(size){
3871 var oldAnimate = this.animate;
3872 this.animate = false;
3873 this.adapter.setElementSize(this, size);
3874 this.animate = oldAnimate;
3878 * Destroy this splitbar.
3879 * @param {Boolean} removeEl True to remove the element
3881 destroy : function(removeEl){
3886 this.proxy.parentNode.removeChild(this.proxy);
3894 * @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.
3896 Roo.SplitBar.createProxy = function(dir){
3897 var proxy = new Roo.Element(document.createElement("div"));
3898 proxy.unselectable();
3899 var cls = 'x-splitbar-proxy';
3900 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3901 document.body.appendChild(proxy.dom);
3906 * @class Roo.SplitBar.BasicLayoutAdapter
3907 * Default Adapter. It assumes the splitter and resizing element are not positioned
3908 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3910 Roo.SplitBar.BasicLayoutAdapter = function(){
3913 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3914 // do nothing for now
3919 * Called before drag operations to get the current size of the resizing element.
3920 * @param {Roo.SplitBar} s The SplitBar using this adapter
3922 getElementSize : function(s){
3923 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3924 return s.resizingEl.getWidth();
3926 return s.resizingEl.getHeight();
3931 * Called after drag operations to set the size of the resizing element.
3932 * @param {Roo.SplitBar} s The SplitBar using this adapter
3933 * @param {Number} newSize The new size to set
3934 * @param {Function} onComplete A function to be invoked when resizing is complete
3936 setElementSize : function(s, newSize, onComplete){
3937 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3939 s.resizingEl.setWidth(newSize);
3941 onComplete(s, newSize);
3944 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3949 s.resizingEl.setHeight(newSize);
3951 onComplete(s, newSize);
3954 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3961 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3962 * @extends Roo.SplitBar.BasicLayoutAdapter
3963 * Adapter that moves the splitter element to align with the resized sizing element.
3964 * Used with an absolute positioned SplitBar.
3965 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3966 * document.body, make sure you assign an id to the body element.
3968 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3969 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3970 this.container = Roo.get(container);
3973 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3978 getElementSize : function(s){
3979 return this.basic.getElementSize(s);
3982 setElementSize : function(s, newSize, onComplete){
3983 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3986 moveSplitter : function(s){
3987 var yes = Roo.SplitBar;
3988 switch(s.placement){
3990 s.el.setX(s.resizingEl.getRight());
3993 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3996 s.el.setY(s.resizingEl.getBottom());
3999 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4006 * Orientation constant - Create a vertical SplitBar
4010 Roo.SplitBar.VERTICAL = 1;
4013 * Orientation constant - Create a horizontal SplitBar
4017 Roo.SplitBar.HORIZONTAL = 2;
4020 * Placement constant - The resizing element is to the left of the splitter element
4024 Roo.SplitBar.LEFT = 1;
4027 * Placement constant - The resizing element is to the right of the splitter element
4031 Roo.SplitBar.RIGHT = 2;
4034 * Placement constant - The resizing element is positioned above the splitter element
4038 Roo.SplitBar.TOP = 3;
4041 * Placement constant - The resizing element is positioned under splitter element
4045 Roo.SplitBar.BOTTOM = 4;
4048 * Ext JS Library 1.1.1
4049 * Copyright(c) 2006-2007, Ext JS, LLC.
4051 * Originally Released Under LGPL - original licence link has changed is not relivant.
4054 * <script type="text/javascript">
4059 * @extends Roo.util.Observable
4060 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
4061 * This class also supports single and multi selection modes. <br>
4062 * Create a data model bound view:
4064 var store = new Roo.data.Store(...);
4066 var view = new Roo.View({
4068 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
4071 selectedClass: "ydataview-selected",
4075 // listen for node click?
4076 view.on("click", function(vw, index, node, e){
4077 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4081 dataModel.load("foobar.xml");
4083 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4085 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4086 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4088 * Note: old style constructor is still suported (container, template, config)
4092 * @param {Object} config The config object
4095 Roo.View = function(config, depreciated_tpl, depreciated_config){
4097 this.parent = false;
4099 if (typeof(depreciated_tpl) == 'undefined') {
4100 // new way.. - universal constructor.
4101 Roo.apply(this, config);
4102 this.el = Roo.get(this.el);
4105 this.el = Roo.get(config);
4106 this.tpl = depreciated_tpl;
4107 Roo.apply(this, depreciated_config);
4109 this.wrapEl = this.el.wrap().wrap();
4110 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4113 if(typeof(this.tpl) == "string"){
4114 this.tpl = new Roo.Template(this.tpl);
4116 // support xtype ctors..
4117 this.tpl = new Roo.factory(this.tpl, Roo);
4126 * @event beforeclick
4127 * Fires before a click is processed. Returns false to cancel the default action.
4128 * @param {Roo.View} this
4129 * @param {Number} index The index of the target node
4130 * @param {HTMLElement} node The target node
4131 * @param {Roo.EventObject} e The raw event object
4133 "beforeclick" : true,
4136 * Fires when a template node is clicked.
4137 * @param {Roo.View} this
4138 * @param {Number} index The index of the target node
4139 * @param {HTMLElement} node The target node
4140 * @param {Roo.EventObject} e The raw event object
4145 * Fires when a template node is double clicked.
4146 * @param {Roo.View} this
4147 * @param {Number} index The index of the target node
4148 * @param {HTMLElement} node The target node
4149 * @param {Roo.EventObject} e The raw event object
4153 * @event contextmenu
4154 * Fires when a template node is right clicked.
4155 * @param {Roo.View} this
4156 * @param {Number} index The index of the target node
4157 * @param {HTMLElement} node The target node
4158 * @param {Roo.EventObject} e The raw event object
4160 "contextmenu" : true,
4162 * @event selectionchange
4163 * Fires when the selected nodes change.
4164 * @param {Roo.View} this
4165 * @param {Array} selections Array of the selected nodes
4167 "selectionchange" : true,
4170 * @event beforeselect
4171 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4172 * @param {Roo.View} this
4173 * @param {HTMLElement} node The node to be selected
4174 * @param {Array} selections Array of currently selected nodes
4176 "beforeselect" : true,
4178 * @event preparedata
4179 * Fires on every row to render, to allow you to change the data.
4180 * @param {Roo.View} this
4181 * @param {Object} data to be rendered (change this)
4183 "preparedata" : true
4191 "click": this.onClick,
4192 "dblclick": this.onDblClick,
4193 "contextmenu": this.onContextMenu,
4197 this.selections = [];
4199 this.cmp = new Roo.CompositeElementLite([]);
4201 this.store = Roo.factory(this.store, Roo.data);
4202 this.setStore(this.store, true);
4205 if ( this.footer && this.footer.xtype) {
4207 var fctr = this.wrapEl.appendChild(document.createElement("div"));
4209 this.footer.dataSource = this.store;
4210 this.footer.container = fctr;
4211 this.footer = Roo.factory(this.footer, Roo);
4212 fctr.insertFirst(this.el);
4214 // this is a bit insane - as the paging toolbar seems to detach the el..
4215 // dom.parentNode.parentNode.parentNode
4216 // they get detached?
4220 Roo.View.superclass.constructor.call(this);
4225 Roo.extend(Roo.View, Roo.util.Observable, {
4228 * @cfg {Roo.data.Store} store Data store to load data from.
4233 * @cfg {String|Roo.Element} el The container element.
4238 * @cfg {String|Roo.Template} tpl The template used by this View
4242 * @cfg {String} dataName the named area of the template to use as the data area
4243 * Works with domtemplates roo-name="name"
4247 * @cfg {String} selectedClass The css class to add to selected nodes
4249 selectedClass : "x-view-selected",
4251 * @cfg {String} emptyText The empty text to show when nothing is loaded.
4256 * @cfg {String} text to display on mask (default Loading)
4260 * @cfg {Boolean} multiSelect Allow multiple selection
4262 multiSelect : false,
4264 * @cfg {Boolean} singleSelect Allow single selection
4266 singleSelect: false,
4269 * @cfg {Boolean} toggleSelect - selecting
4271 toggleSelect : false,
4274 * @cfg {Boolean} tickable - selecting
4279 * Returns the element this view is bound to.
4280 * @return {Roo.Element}
4289 * Refreshes the view. - called by datachanged on the store. - do not call directly.
4291 refresh : function(){
4292 //Roo.log('refresh');
4295 // if we are using something like 'domtemplate', then
4296 // the what gets used is:
4297 // t.applySubtemplate(NAME, data, wrapping data..)
4298 // the outer template then get' applied with
4299 // the store 'extra data'
4300 // and the body get's added to the
4301 // roo-name="data" node?
4302 // <span class='roo-tpl-{name}'></span> ?????
4306 this.clearSelections();
4309 var records = this.store.getRange();
4310 if(records.length < 1) {
4312 // is this valid?? = should it render a template??
4314 this.el.update(this.emptyText);
4318 if (this.dataName) {
4319 this.el.update(t.apply(this.store.meta)); //????
4320 el = this.el.child('.roo-tpl-' + this.dataName);
4323 for(var i = 0, len = records.length; i < len; i++){
4324 var data = this.prepareData(records[i].data, i, records[i]);
4325 this.fireEvent("preparedata", this, data, i, records[i]);
4327 var d = Roo.apply({}, data);
4330 Roo.apply(d, {'roo-id' : Roo.id()});
4334 Roo.each(this.parent.item, function(item){
4335 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4338 Roo.apply(d, {'roo-data-checked' : 'checked'});
4342 html[html.length] = Roo.util.Format.trim(
4344 t.applySubtemplate(this.dataName, d, this.store.meta) :
4351 el.update(html.join(""));
4352 this.nodes = el.dom.childNodes;
4353 this.updateIndexes(0);
4358 * Function to override to reformat the data that is sent to
4359 * the template for each node.
4360 * DEPRICATED - use the preparedata event handler.
4361 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4362 * a JSON object for an UpdateManager bound view).
4364 prepareData : function(data, index, record)
4366 this.fireEvent("preparedata", this, data, index, record);
4370 onUpdate : function(ds, record){
4371 // Roo.log('on update');
4372 this.clearSelections();
4373 var index = this.store.indexOf(record);
4374 var n = this.nodes[index];
4375 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4376 n.parentNode.removeChild(n);
4377 this.updateIndexes(index, index);
4383 onAdd : function(ds, records, index)
4385 //Roo.log(['on Add', ds, records, index] );
4386 this.clearSelections();
4387 if(this.nodes.length == 0){
4391 var n = this.nodes[index];
4392 for(var i = 0, len = records.length; i < len; i++){
4393 var d = this.prepareData(records[i].data, i, records[i]);
4395 this.tpl.insertBefore(n, d);
4398 this.tpl.append(this.el, d);
4401 this.updateIndexes(index);
4404 onRemove : function(ds, record, index){
4405 // Roo.log('onRemove');
4406 this.clearSelections();
4407 var el = this.dataName ?
4408 this.el.child('.roo-tpl-' + this.dataName) :
4411 el.dom.removeChild(this.nodes[index]);
4412 this.updateIndexes(index);
4416 * Refresh an individual node.
4417 * @param {Number} index
4419 refreshNode : function(index){
4420 this.onUpdate(this.store, this.store.getAt(index));
4423 updateIndexes : function(startIndex, endIndex){
4424 var ns = this.nodes;
4425 startIndex = startIndex || 0;
4426 endIndex = endIndex || ns.length - 1;
4427 for(var i = startIndex; i <= endIndex; i++){
4428 ns[i].nodeIndex = i;
4433 * Changes the data store this view uses and refresh the view.
4434 * @param {Store} store
4436 setStore : function(store, initial){
4437 if(!initial && this.store){
4438 this.store.un("datachanged", this.refresh);
4439 this.store.un("add", this.onAdd);
4440 this.store.un("remove", this.onRemove);
4441 this.store.un("update", this.onUpdate);
4442 this.store.un("clear", this.refresh);
4443 this.store.un("beforeload", this.onBeforeLoad);
4444 this.store.un("load", this.onLoad);
4445 this.store.un("loadexception", this.onLoad);
4449 store.on("datachanged", this.refresh, this);
4450 store.on("add", this.onAdd, this);
4451 store.on("remove", this.onRemove, this);
4452 store.on("update", this.onUpdate, this);
4453 store.on("clear", this.refresh, this);
4454 store.on("beforeload", this.onBeforeLoad, this);
4455 store.on("load", this.onLoad, this);
4456 store.on("loadexception", this.onLoad, this);
4464 * onbeforeLoad - masks the loading area.
4467 onBeforeLoad : function(store,opts)
4469 //Roo.log('onBeforeLoad');
4473 this.el.mask(this.mask ? this.mask : "Loading" );
4475 onLoad : function ()
4482 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4483 * @param {HTMLElement} node
4484 * @return {HTMLElement} The template node
4486 findItemFromChild : function(node){
4487 var el = this.dataName ?
4488 this.el.child('.roo-tpl-' + this.dataName,true) :
4491 if(!node || node.parentNode == el){
4494 var p = node.parentNode;
4495 while(p && p != el){
4496 if(p.parentNode == el){
4505 onClick : function(e){
4506 var item = this.findItemFromChild(e.getTarget());
4508 var index = this.indexOf(item);
4509 if(this.onItemClick(item, index, e) !== false){
4510 this.fireEvent("click", this, index, item, e);
4513 this.clearSelections();
4518 onContextMenu : function(e){
4519 var item = this.findItemFromChild(e.getTarget());
4521 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4526 onDblClick : function(e){
4527 var item = this.findItemFromChild(e.getTarget());
4529 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4533 onItemClick : function(item, index, e)
4535 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4538 if (this.toggleSelect) {
4539 var m = this.isSelected(item) ? 'unselect' : 'select';
4542 _t[m](item, true, false);
4545 if(this.multiSelect || this.singleSelect){
4546 if(this.multiSelect && e.shiftKey && this.lastSelection){
4547 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4549 this.select(item, this.multiSelect && e.ctrlKey);
4550 this.lastSelection = item;
4562 * Get the number of selected nodes.
4565 getSelectionCount : function(){
4566 return this.selections.length;
4570 * Get the currently selected nodes.
4571 * @return {Array} An array of HTMLElements
4573 getSelectedNodes : function(){
4574 return this.selections;
4578 * Get the indexes of the selected nodes.
4581 getSelectedIndexes : function(){
4582 var indexes = [], s = this.selections;
4583 for(var i = 0, len = s.length; i < len; i++){
4584 indexes.push(s[i].nodeIndex);
4590 * Clear all selections
4591 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4593 clearSelections : function(suppressEvent){
4594 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4595 this.cmp.elements = this.selections;
4596 this.cmp.removeClass(this.selectedClass);
4597 this.selections = [];
4599 this.fireEvent("selectionchange", this, this.selections);
4605 * Returns true if the passed node is selected
4606 * @param {HTMLElement/Number} node The node or node index
4609 isSelected : function(node){
4610 var s = this.selections;
4614 node = this.getNode(node);
4615 return s.indexOf(node) !== -1;
4620 * @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
4621 * @param {Boolean} keepExisting (optional) true to keep existing selections
4622 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4624 select : function(nodeInfo, keepExisting, suppressEvent){
4625 if(nodeInfo instanceof Array){
4627 this.clearSelections(true);
4629 for(var i = 0, len = nodeInfo.length; i < len; i++){
4630 this.select(nodeInfo[i], true, true);
4634 var node = this.getNode(nodeInfo);
4635 if(!node || this.isSelected(node)){
4636 return; // already selected.
4639 this.clearSelections(true);
4642 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4643 Roo.fly(node).addClass(this.selectedClass);
4644 this.selections.push(node);
4646 this.fireEvent("selectionchange", this, this.selections);
4654 * @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
4655 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4656 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4658 unselect : function(nodeInfo, keepExisting, suppressEvent)
4660 if(nodeInfo instanceof Array){
4661 Roo.each(this.selections, function(s) {
4662 this.unselect(s, nodeInfo);
4666 var node = this.getNode(nodeInfo);
4667 if(!node || !this.isSelected(node)){
4668 //Roo.log("not selected");
4669 return; // not selected.
4673 Roo.each(this.selections, function(s) {
4675 Roo.fly(node).removeClass(this.selectedClass);
4682 this.selections= ns;
4683 this.fireEvent("selectionchange", this, this.selections);
4687 * Gets a template 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 {HTMLElement} The node or null if it wasn't found
4691 getNode : function(nodeInfo){
4692 if(typeof nodeInfo == "string"){
4693 return document.getElementById(nodeInfo);
4694 }else if(typeof nodeInfo == "number"){
4695 return this.nodes[nodeInfo];
4701 * Gets a range template nodes.
4702 * @param {Number} startIndex
4703 * @param {Number} endIndex
4704 * @return {Array} An array of nodes
4706 getNodes : function(start, end){
4707 var ns = this.nodes;
4709 end = typeof end == "undefined" ? ns.length - 1 : end;
4712 for(var i = start; i <= end; i++){
4716 for(var i = start; i >= end; i--){
4724 * Finds the index of the passed node
4725 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4726 * @return {Number} The index of the node or -1
4728 indexOf : function(node){
4729 node = this.getNode(node);
4730 if(typeof node.nodeIndex == "number"){
4731 return node.nodeIndex;
4733 var ns = this.nodes;
4734 for(var i = 0, len = ns.length; i < len; i++){
4744 * Ext JS Library 1.1.1
4745 * Copyright(c) 2006-2007, Ext JS, LLC.
4747 * Originally Released Under LGPL - original licence link has changed is not relivant.
4750 * <script type="text/javascript">
4754 * @class Roo.JsonView
4756 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4758 var view = new Roo.JsonView({
4759 container: "my-element",
4760 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4765 // listen for node click?
4766 view.on("click", function(vw, index, node, e){
4767 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4770 // direct load of JSON data
4771 view.load("foobar.php");
4773 // Example from my blog list
4774 var tpl = new Roo.Template(
4775 '<div class="entry">' +
4776 '<a class="entry-title" href="{link}">{title}</a>' +
4777 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4778 "</div><hr />"
4781 var moreView = new Roo.JsonView({
4782 container : "entry-list",
4786 moreView.on("beforerender", this.sortEntries, this);
4788 url: "/blog/get-posts.php",
4789 params: "allposts=true",
4790 text: "Loading Blog Entries..."
4794 * Note: old code is supported with arguments : (container, template, config)
4798 * Create a new JsonView
4800 * @param {Object} config The config object
4803 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4806 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4808 var um = this.el.getUpdateManager();
4809 um.setRenderer(this);
4810 um.on("update", this.onLoad, this);
4811 um.on("failure", this.onLoadException, this);
4814 * @event beforerender
4815 * Fires before rendering of the downloaded JSON data.
4816 * @param {Roo.JsonView} this
4817 * @param {Object} data The JSON data loaded
4821 * Fires when data is loaded.
4822 * @param {Roo.JsonView} this
4823 * @param {Object} data The JSON data loaded
4824 * @param {Object} response The raw Connect response object
4827 * @event loadexception
4828 * Fires when loading fails.
4829 * @param {Roo.JsonView} this
4830 * @param {Object} response The raw Connect response object
4833 'beforerender' : true,
4835 'loadexception' : true
4838 Roo.extend(Roo.JsonView, Roo.View, {
4840 * @type {String} The root property in the loaded JSON object that contains the data
4845 * Refreshes the view.
4847 refresh : function(){
4848 this.clearSelections();
4851 var o = this.jsonData;
4852 if(o && o.length > 0){
4853 for(var i = 0, len = o.length; i < len; i++){
4854 var data = this.prepareData(o[i], i, o);
4855 html[html.length] = this.tpl.apply(data);
4858 html.push(this.emptyText);
4860 this.el.update(html.join(""));
4861 this.nodes = this.el.dom.childNodes;
4862 this.updateIndexes(0);
4866 * 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.
4867 * @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:
4870 url: "your-url.php",
4871 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4872 callback: yourFunction,
4873 scope: yourObject, //(optional scope)
4881 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4882 * 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.
4883 * @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}
4884 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4885 * @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.
4888 var um = this.el.getUpdateManager();
4889 um.update.apply(um, arguments);
4892 // note - render is a standard framework call...
4893 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4894 render : function(el, response){
4896 this.clearSelections();
4900 if (response != '') {
4901 o = Roo.util.JSON.decode(response.responseText);
4904 o = o[this.jsonRoot];
4910 * The current JSON data or null
4913 this.beforeRender();
4918 * Get the number of records in the current JSON dataset
4921 getCount : function(){
4922 return this.jsonData ? this.jsonData.length : 0;
4926 * Returns the JSON object for the specified node(s)
4927 * @param {HTMLElement/Array} node The node or an array of nodes
4928 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4929 * you get the JSON object for the node
4931 getNodeData : function(node){
4932 if(node instanceof Array){
4934 for(var i = 0, len = node.length; i < len; i++){
4935 data.push(this.getNodeData(node[i]));
4939 return this.jsonData[this.indexOf(node)] || null;
4942 beforeRender : function(){
4943 this.snapshot = this.jsonData;
4945 this.sort.apply(this, this.sortInfo);
4947 this.fireEvent("beforerender", this, this.jsonData);
4950 onLoad : function(el, o){
4951 this.fireEvent("load", this, this.jsonData, o);
4954 onLoadException : function(el, o){
4955 this.fireEvent("loadexception", this, o);
4959 * Filter the data by a specific property.
4960 * @param {String} property A property on your JSON objects
4961 * @param {String/RegExp} value Either string that the property values
4962 * should start with, or a RegExp to test against the property
4964 filter : function(property, value){
4967 var ss = this.snapshot;
4968 if(typeof value == "string"){
4969 var vlen = value.length;
4974 value = value.toLowerCase();
4975 for(var i = 0, len = ss.length; i < len; i++){
4977 if(o[property].substr(0, vlen).toLowerCase() == value){
4981 } else if(value.exec){ // regex?
4982 for(var i = 0, len = ss.length; i < len; i++){
4984 if(value.test(o[property])){
4991 this.jsonData = data;
4997 * Filter by a function. The passed function will be called with each
4998 * object in the current dataset. If the function returns true the value is kept,
4999 * otherwise it is filtered.
5000 * @param {Function} fn
5001 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5003 filterBy : function(fn, scope){
5006 var ss = this.snapshot;
5007 for(var i = 0, len = ss.length; i < len; i++){
5009 if(fn.call(scope || this, o)){
5013 this.jsonData = data;
5019 * Clears the current filter.
5021 clearFilter : function(){
5022 if(this.snapshot && this.jsonData != this.snapshot){
5023 this.jsonData = this.snapshot;
5030 * Sorts the data for this view and refreshes it.
5031 * @param {String} property A property on your JSON objects to sort on
5032 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5033 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5035 sort : function(property, dir, sortType){
5036 this.sortInfo = Array.prototype.slice.call(arguments, 0);
5039 var dsc = dir && dir.toLowerCase() == "desc";
5040 var f = function(o1, o2){
5041 var v1 = sortType ? sortType(o1[p]) : o1[p];
5042 var v2 = sortType ? sortType(o2[p]) : o2[p];
5045 return dsc ? +1 : -1;
5047 return dsc ? -1 : +1;
5052 this.jsonData.sort(f);
5054 if(this.jsonData != this.snapshot){
5055 this.snapshot.sort(f);
5061 * Ext JS Library 1.1.1
5062 * Copyright(c) 2006-2007, Ext JS, LLC.
5064 * Originally Released Under LGPL - original licence link has changed is not relivant.
5067 * <script type="text/javascript">
5072 * @class Roo.ColorPalette
5073 * @extends Roo.Component
5074 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
5075 * Here's an example of typical usage:
5077 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
5078 cp.render('my-div');
5080 cp.on('select', function(palette, selColor){
5081 // do something with selColor
5085 * Create a new ColorPalette
5086 * @param {Object} config The config object
5088 Roo.ColorPalette = function(config){
5089 Roo.ColorPalette.superclass.constructor.call(this, config);
5093 * Fires when a color is selected
5094 * @param {ColorPalette} this
5095 * @param {String} color The 6-digit color hex code (without the # symbol)
5101 this.on("select", this.handler, this.scope, true);
5104 Roo.extend(Roo.ColorPalette, Roo.Component, {
5106 * @cfg {String} itemCls
5107 * The CSS class to apply to the containing element (defaults to "x-color-palette")
5109 itemCls : "x-color-palette",
5111 * @cfg {String} value
5112 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
5113 * the hex codes are case-sensitive.
5118 ctype: "Roo.ColorPalette",
5121 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5123 allowReselect : false,
5126 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
5127 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
5128 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5129 * of colors with the width setting until the box is symmetrical.</p>
5130 * <p>You can override individual colors if needed:</p>
5132 var cp = new Roo.ColorPalette();
5133 cp.colors[0] = "FF0000"; // change the first box to red
5136 Or you can provide a custom array of your own for complete control:
5138 var cp = new Roo.ColorPalette();
5139 cp.colors = ["000000", "993300", "333300"];
5144 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5145 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5146 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5147 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5148 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5152 onRender : function(container, position){
5153 var t = new Roo.MasterTemplate(
5154 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
5156 var c = this.colors;
5157 for(var i = 0, len = c.length; i < len; i++){
5160 var el = document.createElement("div");
5161 el.className = this.itemCls;
5163 container.dom.insertBefore(el, position);
5164 this.el = Roo.get(el);
5165 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
5166 if(this.clickEvent != 'click'){
5167 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
5172 afterRender : function(){
5173 Roo.ColorPalette.superclass.afterRender.call(this);
5182 handleClick : function(e, t){
5185 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5186 this.select(c.toUpperCase());
5191 * Selects the specified color in the palette (fires the select event)
5192 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5194 select : function(color){
5195 color = color.replace("#", "");
5196 if(color != this.value || this.allowReselect){
5199 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5201 el.child("a.color-"+color).addClass("x-color-palette-sel");
5203 this.fireEvent("select", this, color);
5208 * Ext JS Library 1.1.1
5209 * Copyright(c) 2006-2007, Ext JS, LLC.
5211 * Originally Released Under LGPL - original licence link has changed is not relivant.
5214 * <script type="text/javascript">
5218 * @class Roo.DatePicker
5219 * @extends Roo.Component
5220 * Simple date picker class.
5222 * Create a new DatePicker
5223 * @param {Object} config The config object
5225 Roo.DatePicker = function(config){
5226 Roo.DatePicker.superclass.constructor.call(this, config);
5228 this.value = config && config.value ?
5229 config.value.clearTime() : new Date().clearTime();
5234 * Fires when a date is selected
5235 * @param {DatePicker} this
5236 * @param {Date} date The selected date
5240 * @event monthchange
5241 * Fires when the displayed month changes
5242 * @param {DatePicker} this
5243 * @param {Date} date The selected month
5249 this.on("select", this.handler, this.scope || this);
5251 // build the disabledDatesRE
5252 if(!this.disabledDatesRE && this.disabledDates){
5253 var dd = this.disabledDates;
5255 for(var i = 0; i < dd.length; i++){
5257 if(i != dd.length-1) {
5261 this.disabledDatesRE = new RegExp(re + ")");
5265 Roo.extend(Roo.DatePicker, Roo.Component, {
5267 * @cfg {String} todayText
5268 * The text to display on the button that selects the current date (defaults to "Today")
5270 todayText : "Today",
5272 * @cfg {String} okText
5273 * The text to display on the ok button
5275 okText : " OK ", //   to give the user extra clicking room
5277 * @cfg {String} cancelText
5278 * The text to display on the cancel button
5280 cancelText : "Cancel",
5282 * @cfg {String} todayTip
5283 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5285 todayTip : "{0} (Spacebar)",
5287 * @cfg {Date} minDate
5288 * Minimum allowable date (JavaScript date object, defaults to null)
5292 * @cfg {Date} maxDate
5293 * Maximum allowable date (JavaScript date object, defaults to null)
5297 * @cfg {String} minText
5298 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5300 minText : "This date is before the minimum date",
5302 * @cfg {String} maxText
5303 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5305 maxText : "This date is after the maximum date",
5307 * @cfg {String} format
5308 * The default date format string which can be overriden for localization support. The format must be
5309 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5313 * @cfg {Array} disabledDays
5314 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5316 disabledDays : null,
5318 * @cfg {String} disabledDaysText
5319 * The tooltip to display when the date falls on a disabled day (defaults to "")
5321 disabledDaysText : "",
5323 * @cfg {RegExp} disabledDatesRE
5324 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5326 disabledDatesRE : null,
5328 * @cfg {String} disabledDatesText
5329 * The tooltip text to display when the date falls on a disabled date (defaults to "")
5331 disabledDatesText : "",
5333 * @cfg {Boolean} constrainToViewport
5334 * True to constrain the date picker to the viewport (defaults to true)
5336 constrainToViewport : true,
5338 * @cfg {Array} monthNames
5339 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5341 monthNames : Date.monthNames,
5343 * @cfg {Array} dayNames
5344 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5346 dayNames : Date.dayNames,
5348 * @cfg {String} nextText
5349 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5351 nextText: 'Next Month (Control+Right)',
5353 * @cfg {String} prevText
5354 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5356 prevText: 'Previous Month (Control+Left)',
5358 * @cfg {String} monthYearText
5359 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5361 monthYearText: 'Choose a month (Control+Up/Down to move years)',
5363 * @cfg {Number} startDay
5364 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5368 * @cfg {Bool} showClear
5369 * Show a clear button (usefull for date form elements that can be blank.)
5375 * Sets the value of the date field
5376 * @param {Date} value The date to set
5378 setValue : function(value){
5379 var old = this.value;
5381 if (typeof(value) == 'string') {
5383 value = Date.parseDate(value, this.format);
5389 this.value = value.clearTime(true);
5391 this.update(this.value);
5396 * Gets the current selected value of the date field
5397 * @return {Date} The selected date
5399 getValue : function(){
5406 this.update(this.activeDate);
5411 onRender : function(container, position){
5414 '<table cellspacing="0">',
5415 '<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>',
5416 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5417 var dn = this.dayNames;
5418 for(var i = 0; i < 7; i++){
5419 var d = this.startDay+i;
5423 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5425 m[m.length] = "</tr></thead><tbody><tr>";
5426 for(var i = 0; i < 42; i++) {
5427 if(i % 7 == 0 && i != 0){
5428 m[m.length] = "</tr><tr>";
5430 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5432 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5433 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5435 var el = document.createElement("div");
5436 el.className = "x-date-picker";
5437 el.innerHTML = m.join("");
5439 container.dom.insertBefore(el, position);
5441 this.el = Roo.get(el);
5442 this.eventEl = Roo.get(el.firstChild);
5444 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5445 handler: this.showPrevMonth,
5447 preventDefault:true,
5451 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5452 handler: this.showNextMonth,
5454 preventDefault:true,
5458 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5460 this.monthPicker = this.el.down('div.x-date-mp');
5461 this.monthPicker.enableDisplayMode('block');
5463 var kn = new Roo.KeyNav(this.eventEl, {
5464 "left" : function(e){
5466 this.showPrevMonth() :
5467 this.update(this.activeDate.add("d", -1));
5470 "right" : function(e){
5472 this.showNextMonth() :
5473 this.update(this.activeDate.add("d", 1));
5478 this.showNextYear() :
5479 this.update(this.activeDate.add("d", -7));
5482 "down" : function(e){
5484 this.showPrevYear() :
5485 this.update(this.activeDate.add("d", 7));
5488 "pageUp" : function(e){
5489 this.showNextMonth();
5492 "pageDown" : function(e){
5493 this.showPrevMonth();
5496 "enter" : function(e){
5497 e.stopPropagation();
5504 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5506 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5508 this.el.unselectable();
5510 this.cells = this.el.select("table.x-date-inner tbody td");
5511 this.textNodes = this.el.query("table.x-date-inner tbody span");
5513 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5515 tooltip: this.monthYearText
5518 this.mbtn.on('click', this.showMonthPicker, this);
5519 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5522 var today = (new Date()).dateFormat(this.format);
5524 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5525 if (this.showClear) {
5526 baseTb.add( new Roo.Toolbar.Fill());
5529 text: String.format(this.todayText, today),
5530 tooltip: String.format(this.todayTip, today),
5531 handler: this.selectToday,
5535 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5538 if (this.showClear) {
5540 baseTb.add( new Roo.Toolbar.Fill());
5543 cls: 'x-btn-icon x-btn-clear',
5544 handler: function() {
5546 this.fireEvent("select", this, '');
5556 this.update(this.value);
5559 createMonthPicker : function(){
5560 if(!this.monthPicker.dom.firstChild){
5561 var buf = ['<table border="0" cellspacing="0">'];
5562 for(var i = 0; i < 6; i++){
5564 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5565 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5567 '<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>' :
5568 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5572 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5574 '</button><button type="button" class="x-date-mp-cancel">',
5576 '</button></td></tr>',
5579 this.monthPicker.update(buf.join(''));
5580 this.monthPicker.on('click', this.onMonthClick, this);
5581 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5583 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5584 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5586 this.mpMonths.each(function(m, a, i){
5589 m.dom.xmonth = 5 + Math.round(i * .5);
5591 m.dom.xmonth = Math.round((i-1) * .5);
5597 showMonthPicker : function(){
5598 this.createMonthPicker();
5599 var size = this.el.getSize();
5600 this.monthPicker.setSize(size);
5601 this.monthPicker.child('table').setSize(size);
5603 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5604 this.updateMPMonth(this.mpSelMonth);
5605 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5606 this.updateMPYear(this.mpSelYear);
5608 this.monthPicker.slideIn('t', {duration:.2});
5611 updateMPYear : function(y){
5613 var ys = this.mpYears.elements;
5614 for(var i = 1; i <= 10; i++){
5615 var td = ys[i-1], y2;
5617 y2 = y + Math.round(i * .5);
5618 td.firstChild.innerHTML = y2;
5621 y2 = y - (5-Math.round(i * .5));
5622 td.firstChild.innerHTML = y2;
5625 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5629 updateMPMonth : function(sm){
5630 this.mpMonths.each(function(m, a, i){
5631 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5635 selectMPMonth: function(m){
5639 onMonthClick : function(e, t){
5641 var el = new Roo.Element(t), pn;
5642 if(el.is('button.x-date-mp-cancel')){
5643 this.hideMonthPicker();
5645 else if(el.is('button.x-date-mp-ok')){
5646 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5647 this.hideMonthPicker();
5649 else if(pn = el.up('td.x-date-mp-month', 2)){
5650 this.mpMonths.removeClass('x-date-mp-sel');
5651 pn.addClass('x-date-mp-sel');
5652 this.mpSelMonth = pn.dom.xmonth;
5654 else if(pn = el.up('td.x-date-mp-year', 2)){
5655 this.mpYears.removeClass('x-date-mp-sel');
5656 pn.addClass('x-date-mp-sel');
5657 this.mpSelYear = pn.dom.xyear;
5659 else if(el.is('a.x-date-mp-prev')){
5660 this.updateMPYear(this.mpyear-10);
5662 else if(el.is('a.x-date-mp-next')){
5663 this.updateMPYear(this.mpyear+10);
5667 onMonthDblClick : function(e, t){
5669 var el = new Roo.Element(t), pn;
5670 if(pn = el.up('td.x-date-mp-month', 2)){
5671 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5672 this.hideMonthPicker();
5674 else if(pn = el.up('td.x-date-mp-year', 2)){
5675 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5676 this.hideMonthPicker();
5680 hideMonthPicker : function(disableAnim){
5681 if(this.monthPicker){
5682 if(disableAnim === true){
5683 this.monthPicker.hide();
5685 this.monthPicker.slideOut('t', {duration:.2});
5691 showPrevMonth : function(e){
5692 this.update(this.activeDate.add("mo", -1));
5696 showNextMonth : function(e){
5697 this.update(this.activeDate.add("mo", 1));
5701 showPrevYear : function(){
5702 this.update(this.activeDate.add("y", -1));
5706 showNextYear : function(){
5707 this.update(this.activeDate.add("y", 1));
5711 handleMouseWheel : function(e){
5712 var delta = e.getWheelDelta();
5714 this.showPrevMonth();
5716 } else if(delta < 0){
5717 this.showNextMonth();
5723 handleDateClick : function(e, t){
5725 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5726 this.setValue(new Date(t.dateValue));
5727 this.fireEvent("select", this, this.value);
5732 selectToday : function(){
5733 this.setValue(new Date().clearTime());
5734 this.fireEvent("select", this, this.value);
5738 update : function(date)
5740 var vd = this.activeDate;
5741 this.activeDate = date;
5743 var t = date.getTime();
5744 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5745 this.cells.removeClass("x-date-selected");
5746 this.cells.each(function(c){
5747 if(c.dom.firstChild.dateValue == t){
5748 c.addClass("x-date-selected");
5749 setTimeout(function(){
5750 try{c.dom.firstChild.focus();}catch(e){}
5759 var days = date.getDaysInMonth();
5760 var firstOfMonth = date.getFirstDateOfMonth();
5761 var startingPos = firstOfMonth.getDay()-this.startDay;
5763 if(startingPos <= this.startDay){
5767 var pm = date.add("mo", -1);
5768 var prevStart = pm.getDaysInMonth()-startingPos;
5770 var cells = this.cells.elements;
5771 var textEls = this.textNodes;
5772 days += startingPos;
5774 // convert everything to numbers so it's fast
5776 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5777 var today = new Date().clearTime().getTime();
5778 var sel = date.clearTime().getTime();
5779 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5780 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5781 var ddMatch = this.disabledDatesRE;
5782 var ddText = this.disabledDatesText;
5783 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5784 var ddaysText = this.disabledDaysText;
5785 var format = this.format;
5787 var setCellClass = function(cal, cell){
5789 var t = d.getTime();
5790 cell.firstChild.dateValue = t;
5792 cell.className += " x-date-today";
5793 cell.title = cal.todayText;
5796 cell.className += " x-date-selected";
5797 setTimeout(function(){
5798 try{cell.firstChild.focus();}catch(e){}
5803 cell.className = " x-date-disabled";
5804 cell.title = cal.minText;
5808 cell.className = " x-date-disabled";
5809 cell.title = cal.maxText;
5813 if(ddays.indexOf(d.getDay()) != -1){
5814 cell.title = ddaysText;
5815 cell.className = " x-date-disabled";
5818 if(ddMatch && format){
5819 var fvalue = d.dateFormat(format);
5820 if(ddMatch.test(fvalue)){
5821 cell.title = ddText.replace("%0", fvalue);
5822 cell.className = " x-date-disabled";
5828 for(; i < startingPos; i++) {
5829 textEls[i].innerHTML = (++prevStart);
5830 d.setDate(d.getDate()+1);
5831 cells[i].className = "x-date-prevday";
5832 setCellClass(this, cells[i]);
5834 for(; i < days; i++){
5835 intDay = i - startingPos + 1;
5836 textEls[i].innerHTML = (intDay);
5837 d.setDate(d.getDate()+1);
5838 cells[i].className = "x-date-active";
5839 setCellClass(this, cells[i]);
5842 for(; i < 42; i++) {
5843 textEls[i].innerHTML = (++extraDays);
5844 d.setDate(d.getDate()+1);
5845 cells[i].className = "x-date-nextday";
5846 setCellClass(this, cells[i]);
5849 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5850 this.fireEvent('monthchange', this, date);
5852 if(!this.internalRender){
5853 var main = this.el.dom.firstChild;
5854 var w = main.offsetWidth;
5855 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5856 Roo.fly(main).setWidth(w);
5857 this.internalRender = true;
5858 // opera does not respect the auto grow header center column
5859 // then, after it gets a width opera refuses to recalculate
5860 // without a second pass
5861 if(Roo.isOpera && !this.secondPass){
5862 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5863 this.secondPass = true;
5864 this.update.defer(10, this, [date]);
5872 * Ext JS Library 1.1.1
5873 * Copyright(c) 2006-2007, Ext JS, LLC.
5875 * Originally Released Under LGPL - original licence link has changed is not relivant.
5878 * <script type="text/javascript">
5881 * @class Roo.TabPanel
5882 * @extends Roo.util.Observable
5883 * A lightweight tab container.
5887 // basic tabs 1, built from existing content
5888 var tabs = new Roo.TabPanel("tabs1");
5889 tabs.addTab("script", "View Script");
5890 tabs.addTab("markup", "View Markup");
5891 tabs.activate("script");
5893 // more advanced tabs, built from javascript
5894 var jtabs = new Roo.TabPanel("jtabs");
5895 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5897 // set up the UpdateManager
5898 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5899 var updater = tab2.getUpdateManager();
5900 updater.setDefaultUrl("ajax1.htm");
5901 tab2.on('activate', updater.refresh, updater, true);
5903 // Use setUrl for Ajax loading
5904 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5905 tab3.setUrl("ajax2.htm", null, true);
5908 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5911 jtabs.activate("jtabs-1");
5914 * Create a new TabPanel.
5915 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5916 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5918 Roo.TabPanel = function(container, config){
5920 * The container element for this TabPanel.
5923 this.el = Roo.get(container, true);
5925 if(typeof config == "boolean"){
5926 this.tabPosition = config ? "bottom" : "top";
5928 Roo.apply(this, config);
5931 if(this.tabPosition == "bottom"){
5932 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5933 this.el.addClass("x-tabs-bottom");
5935 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5936 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5937 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5939 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5941 if(this.tabPosition != "bottom"){
5942 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5945 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5946 this.el.addClass("x-tabs-top");
5950 this.bodyEl.setStyle("position", "relative");
5953 this.activateDelegate = this.activate.createDelegate(this);
5958 * Fires when the active tab changes
5959 * @param {Roo.TabPanel} this
5960 * @param {Roo.TabPanelItem} activePanel The new active tab
5964 * @event beforetabchange
5965 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5966 * @param {Roo.TabPanel} this
5967 * @param {Object} e Set cancel to true on this object to cancel the tab change
5968 * @param {Roo.TabPanelItem} tab The tab being changed to
5970 "beforetabchange" : true
5973 Roo.EventManager.onWindowResize(this.onResize, this);
5974 this.cpad = this.el.getPadding("lr");
5975 this.hiddenCount = 0;
5978 // toolbar on the tabbar support...
5980 var tcfg = this.toolbar;
5981 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5982 this.toolbar = new Roo.Toolbar(tcfg);
5984 var tbl = tcfg.container.child('table', true);
5985 tbl.setAttribute('width', '100%');
5992 Roo.TabPanel.superclass.constructor.call(this);
5995 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5997 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5999 tabPosition : "top",
6001 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6003 currentTabWidth : 0,
6005 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6009 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6013 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6015 preferredTabWidth : 175,
6017 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6021 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6023 monitorResize : true,
6025 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
6030 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6031 * @param {String} id The id of the div to use <b>or create</b>
6032 * @param {String} text The text for the tab
6033 * @param {String} content (optional) Content to put in the TabPanelItem body
6034 * @param {Boolean} closable (optional) True to create a close icon on the tab
6035 * @return {Roo.TabPanelItem} The created TabPanelItem
6037 addTab : function(id, text, content, closable){
6038 var item = new Roo.TabPanelItem(this, id, text, closable);
6039 this.addTabItem(item);
6041 item.setContent(content);
6047 * Returns the {@link Roo.TabPanelItem} with the specified id/index
6048 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6049 * @return {Roo.TabPanelItem}
6051 getTab : function(id){
6052 return this.items[id];
6056 * Hides the {@link Roo.TabPanelItem} with the specified id/index
6057 * @param {String/Number} id The id or index of the TabPanelItem to hide.
6059 hideTab : function(id){
6060 var t = this.items[id];
6064 this.autoSizeTabs();
6069 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6070 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6072 unhideTab : function(id){
6073 var t = this.items[id];
6077 this.autoSizeTabs();
6082 * Adds an existing {@link Roo.TabPanelItem}.
6083 * @param {Roo.TabPanelItem} item The TabPanelItem to add
6085 addTabItem : function(item){
6086 this.items[item.id] = item;
6087 this.items.push(item);
6088 if(this.resizeTabs){
6089 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6090 this.autoSizeTabs();
6097 * Removes a {@link Roo.TabPanelItem}.
6098 * @param {String/Number} id The id or index of the TabPanelItem to remove.
6100 removeTab : function(id){
6101 var items = this.items;
6102 var tab = items[id];
6103 if(!tab) { return; }
6104 var index = items.indexOf(tab);
6105 if(this.active == tab && items.length > 1){
6106 var newTab = this.getNextAvailable(index);
6111 this.stripEl.dom.removeChild(tab.pnode.dom);
6112 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6113 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6115 items.splice(index, 1);
6116 delete this.items[tab.id];
6117 tab.fireEvent("close", tab);
6118 tab.purgeListeners();
6119 this.autoSizeTabs();
6122 getNextAvailable : function(start){
6123 var items = this.items;
6125 // look for a next tab that will slide over to
6126 // replace the one being removed
6127 while(index < items.length){
6128 var item = items[++index];
6129 if(item && !item.isHidden()){
6133 // if one isn't found select the previous tab (on the left)
6136 var item = items[--index];
6137 if(item && !item.isHidden()){
6145 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6146 * @param {String/Number} id The id or index of the TabPanelItem to disable.
6148 disableTab : function(id){
6149 var tab = this.items[id];
6150 if(tab && this.active != tab){
6156 * Enables a {@link Roo.TabPanelItem} that is disabled.
6157 * @param {String/Number} id The id or index of the TabPanelItem to enable.
6159 enableTab : function(id){
6160 var tab = this.items[id];
6165 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6166 * @param {String/Number} id The id or index of the TabPanelItem to activate.
6167 * @return {Roo.TabPanelItem} The TabPanelItem.
6169 activate : function(id){
6170 var tab = this.items[id];
6174 if(tab == this.active || tab.disabled){
6178 this.fireEvent("beforetabchange", this, e, tab);
6179 if(e.cancel !== true && !tab.disabled){
6183 this.active = this.items[id];
6185 this.fireEvent("tabchange", this, this.active);
6191 * Gets the active {@link Roo.TabPanelItem}.
6192 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6194 getActiveTab : function(){
6199 * Updates the tab body element to fit the height of the container element
6200 * for overflow scrolling
6201 * @param {Number} targetHeight (optional) Override the starting height from the elements height
6203 syncHeight : function(targetHeight){
6204 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6205 var bm = this.bodyEl.getMargins();
6206 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6207 this.bodyEl.setHeight(newHeight);
6211 onResize : function(){
6212 if(this.monitorResize){
6213 this.autoSizeTabs();
6218 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6220 beginUpdate : function(){
6221 this.updating = true;
6225 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6227 endUpdate : function(){
6228 this.updating = false;
6229 this.autoSizeTabs();
6233 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6235 autoSizeTabs : function(){
6236 var count = this.items.length;
6237 var vcount = count - this.hiddenCount;
6238 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6241 var w = Math.max(this.el.getWidth() - this.cpad, 10);
6242 var availWidth = Math.floor(w / vcount);
6243 var b = this.stripBody;
6244 if(b.getWidth() > w){
6245 var tabs = this.items;
6246 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6247 if(availWidth < this.minTabWidth){
6248 /*if(!this.sleft){ // incomplete scrolling code
6249 this.createScrollButtons();
6252 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6255 if(this.currentTabWidth < this.preferredTabWidth){
6256 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6262 * Returns the number of tabs in this TabPanel.
6265 getCount : function(){
6266 return this.items.length;
6270 * Resizes all the tabs to the passed width
6271 * @param {Number} The new width
6273 setTabWidth : function(width){
6274 this.currentTabWidth = width;
6275 for(var i = 0, len = this.items.length; i < len; i++) {
6276 if(!this.items[i].isHidden()) {
6277 this.items[i].setWidth(width);
6283 * Destroys this TabPanel
6284 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6286 destroy : function(removeEl){
6287 Roo.EventManager.removeResizeListener(this.onResize, this);
6288 for(var i = 0, len = this.items.length; i < len; i++){
6289 this.items[i].purgeListeners();
6291 if(removeEl === true){
6299 * @class Roo.TabPanelItem
6300 * @extends Roo.util.Observable
6301 * Represents an individual item (tab plus body) in a TabPanel.
6302 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6303 * @param {String} id The id of this TabPanelItem
6304 * @param {String} text The text for the tab of this TabPanelItem
6305 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6307 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6309 * The {@link Roo.TabPanel} this TabPanelItem belongs to
6310 * @type Roo.TabPanel
6312 this.tabPanel = tabPanel;
6314 * The id for this TabPanelItem
6319 this.disabled = false;
6323 this.loaded = false;
6324 this.closable = closable;
6327 * The body element for this TabPanelItem.
6330 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6331 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6332 this.bodyEl.setStyle("display", "block");
6333 this.bodyEl.setStyle("zoom", "1");
6336 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6338 this.el = Roo.get(els.el, true);
6339 this.inner = Roo.get(els.inner, true);
6340 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6341 this.pnode = Roo.get(els.el.parentNode, true);
6342 this.el.on("mousedown", this.onTabMouseDown, this);
6343 this.el.on("click", this.onTabClick, this);
6346 var c = Roo.get(els.close, true);
6347 c.dom.title = this.closeText;
6348 c.addClassOnOver("close-over");
6349 c.on("click", this.closeClick, this);
6355 * Fires when this tab becomes the active tab.
6356 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6357 * @param {Roo.TabPanelItem} this
6361 * @event beforeclose
6362 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6363 * @param {Roo.TabPanelItem} this
6364 * @param {Object} e Set cancel to true on this object to cancel the close.
6366 "beforeclose": true,
6369 * Fires when this tab is closed.
6370 * @param {Roo.TabPanelItem} this
6375 * Fires when this tab is no longer the active tab.
6376 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6377 * @param {Roo.TabPanelItem} this
6381 this.hidden = false;
6383 Roo.TabPanelItem.superclass.constructor.call(this);
6386 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6387 purgeListeners : function(){
6388 Roo.util.Observable.prototype.purgeListeners.call(this);
6389 this.el.removeAllListeners();
6392 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6395 this.pnode.addClass("on");
6398 this.tabPanel.stripWrap.repaint();
6400 this.fireEvent("activate", this.tabPanel, this);
6404 * Returns true if this tab is the active tab.
6407 isActive : function(){
6408 return this.tabPanel.getActiveTab() == this;
6412 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6415 this.pnode.removeClass("on");
6417 this.fireEvent("deactivate", this.tabPanel, this);
6420 hideAction : function(){
6422 this.bodyEl.setStyle("position", "absolute");
6423 this.bodyEl.setLeft("-20000px");
6424 this.bodyEl.setTop("-20000px");
6427 showAction : function(){
6428 this.bodyEl.setStyle("position", "relative");
6429 this.bodyEl.setTop("");
6430 this.bodyEl.setLeft("");
6435 * Set the tooltip for the tab.
6436 * @param {String} tooltip The tab's tooltip
6438 setTooltip : function(text){
6439 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6440 this.textEl.dom.qtip = text;
6441 this.textEl.dom.removeAttribute('title');
6443 this.textEl.dom.title = text;
6447 onTabClick : function(e){
6449 this.tabPanel.activate(this.id);
6452 onTabMouseDown : function(e){
6454 this.tabPanel.activate(this.id);
6457 getWidth : function(){
6458 return this.inner.getWidth();
6461 setWidth : function(width){
6462 var iwidth = width - this.pnode.getPadding("lr");
6463 this.inner.setWidth(iwidth);
6464 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6465 this.pnode.setWidth(width);
6469 * Show or hide the tab
6470 * @param {Boolean} hidden True to hide or false to show.
6472 setHidden : function(hidden){
6473 this.hidden = hidden;
6474 this.pnode.setStyle("display", hidden ? "none" : "");
6478 * Returns true if this tab is "hidden"
6481 isHidden : function(){
6486 * Returns the text for this tab
6489 getText : function(){
6493 autoSize : function(){
6494 //this.el.beginMeasure();
6495 this.textEl.setWidth(1);
6497 * #2804 [new] Tabs in Roojs
6498 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6500 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6501 //this.el.endMeasure();
6505 * Sets the text for the tab (Note: this also sets the tooltip text)
6506 * @param {String} text The tab's text and tooltip
6508 setText : function(text){
6510 this.textEl.update(text);
6511 this.setTooltip(text);
6512 if(!this.tabPanel.resizeTabs){
6517 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6519 activate : function(){
6520 this.tabPanel.activate(this.id);
6524 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6526 disable : function(){
6527 if(this.tabPanel.active != this){
6528 this.disabled = true;
6529 this.pnode.addClass("disabled");
6534 * Enables this TabPanelItem if it was previously disabled.
6536 enable : function(){
6537 this.disabled = false;
6538 this.pnode.removeClass("disabled");
6542 * Sets the content for this TabPanelItem.
6543 * @param {String} content The content
6544 * @param {Boolean} loadScripts true to look for and load scripts
6546 setContent : function(content, loadScripts){
6547 this.bodyEl.update(content, loadScripts);
6551 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6552 * @return {Roo.UpdateManager} The UpdateManager
6554 getUpdateManager : function(){
6555 return this.bodyEl.getUpdateManager();
6559 * Set a URL to be used to load the content for this TabPanelItem.
6560 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6561 * @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)
6562 * @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)
6563 * @return {Roo.UpdateManager} The UpdateManager
6565 setUrl : function(url, params, loadOnce){
6566 if(this.refreshDelegate){
6567 this.un('activate', this.refreshDelegate);
6569 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6570 this.on("activate", this.refreshDelegate);
6571 return this.bodyEl.getUpdateManager();
6575 _handleRefresh : function(url, params, loadOnce){
6576 if(!loadOnce || !this.loaded){
6577 var updater = this.bodyEl.getUpdateManager();
6578 updater.update(url, params, this._setLoaded.createDelegate(this));
6583 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6584 * Will fail silently if the setUrl method has not been called.
6585 * This does not activate the panel, just updates its content.
6587 refresh : function(){
6588 if(this.refreshDelegate){
6589 this.loaded = false;
6590 this.refreshDelegate();
6595 _setLoaded : function(){
6600 closeClick : function(e){
6603 this.fireEvent("beforeclose", this, o);
6604 if(o.cancel !== true){
6605 this.tabPanel.removeTab(this.id);
6609 * The text displayed in the tooltip for the close icon.
6612 closeText : "Close this tab"
6616 Roo.TabPanel.prototype.createStrip = function(container){
6617 var strip = document.createElement("div");
6618 strip.className = "x-tabs-wrap";
6619 container.appendChild(strip);
6623 Roo.TabPanel.prototype.createStripList = function(strip){
6624 // div wrapper for retard IE
6625 // returns the "tr" element.
6626 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6627 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6628 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6629 return strip.firstChild.firstChild.firstChild.firstChild;
6632 Roo.TabPanel.prototype.createBody = function(container){
6633 var body = document.createElement("div");
6634 Roo.id(body, "tab-body");
6635 Roo.fly(body).addClass("x-tabs-body");
6636 container.appendChild(body);
6640 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6641 var body = Roo.getDom(id);
6643 body = document.createElement("div");
6646 Roo.fly(body).addClass("x-tabs-item-body");
6647 bodyEl.insertBefore(body, bodyEl.firstChild);
6651 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6652 var td = document.createElement("td");
6653 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6654 //stripEl.appendChild(td);
6656 td.className = "x-tabs-closable";
6658 this.closeTpl = new Roo.Template(
6659 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6660 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6661 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6664 var el = this.closeTpl.overwrite(td, {"text": text});
6665 var close = el.getElementsByTagName("div")[0];
6666 var inner = el.getElementsByTagName("em")[0];
6667 return {"el": el, "close": close, "inner": inner};
6670 this.tabTpl = new Roo.Template(
6671 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6672 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6675 var el = this.tabTpl.overwrite(td, {"text": text});
6676 var inner = el.getElementsByTagName("em")[0];
6677 return {"el": el, "inner": inner};
6681 * Ext JS Library 1.1.1
6682 * Copyright(c) 2006-2007, Ext JS, LLC.
6684 * Originally Released Under LGPL - original licence link has changed is not relivant.
6687 * <script type="text/javascript">
6692 * @extends Roo.util.Observable
6693 * Simple Button class
6694 * @cfg {String} text The button text
6695 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6696 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6697 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6698 * @cfg {Object} scope The scope of the handler
6699 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6700 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6701 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6702 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6703 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6704 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6705 applies if enableToggle = true)
6706 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6707 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6708 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6710 * Create a new button
6711 * @param {Object} config The config object
6713 Roo.Button = function(renderTo, config)
6717 renderTo = config.renderTo || false;
6720 Roo.apply(this, config);
6724 * Fires when this button is clicked
6725 * @param {Button} this
6726 * @param {EventObject} e The click event
6731 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6732 * @param {Button} this
6733 * @param {Boolean} pressed
6738 * Fires when the mouse hovers over the button
6739 * @param {Button} this
6740 * @param {Event} e The event object
6745 * Fires when the mouse exits the button
6746 * @param {Button} this
6747 * @param {Event} e The event object
6752 * Fires when the button is rendered
6753 * @param {Button} this
6758 this.menu = Roo.menu.MenuMgr.get(this.menu);
6760 // register listeners first!! - so render can be captured..
6761 Roo.util.Observable.call(this);
6763 this.render(renderTo);
6769 Roo.extend(Roo.Button, Roo.util.Observable, {
6775 * Read-only. True if this button is hidden
6780 * Read-only. True if this button is disabled
6785 * Read-only. True if this button is pressed (only if enableToggle = true)
6791 * @cfg {Number} tabIndex
6792 * The DOM tabIndex for this button (defaults to undefined)
6794 tabIndex : undefined,
6797 * @cfg {Boolean} enableToggle
6798 * True to enable pressed/not pressed toggling (defaults to false)
6800 enableToggle: false,
6803 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6807 * @cfg {String} menuAlign
6808 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6810 menuAlign : "tl-bl?",
6813 * @cfg {String} iconCls
6814 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6816 iconCls : undefined,
6818 * @cfg {String} type
6819 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6824 menuClassTarget: 'tr',
6827 * @cfg {String} clickEvent
6828 * The type of event to map to the button's event handler (defaults to 'click')
6830 clickEvent : 'click',
6833 * @cfg {Boolean} handleMouseEvents
6834 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6836 handleMouseEvents : true,
6839 * @cfg {String} tooltipType
6840 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6842 tooltipType : 'qtip',
6846 * A CSS class to apply to the button's main element.
6850 * @cfg {Roo.Template} template (Optional)
6851 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6852 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6853 * require code modifications if required elements (e.g. a button) aren't present.
6857 render : function(renderTo){
6859 if(this.hideParent){
6860 this.parentEl = Roo.get(renderTo);
6864 if(!Roo.Button.buttonTemplate){
6865 // hideous table template
6866 Roo.Button.buttonTemplate = new Roo.Template(
6867 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6868 '<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>',
6869 "</tr></tbody></table>");
6871 this.template = Roo.Button.buttonTemplate;
6873 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6874 var btnEl = btn.child("button:first");
6875 btnEl.on('focus', this.onFocus, this);
6876 btnEl.on('blur', this.onBlur, this);
6878 btn.addClass(this.cls);
6881 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6884 btnEl.addClass(this.iconCls);
6886 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6889 if(this.tabIndex !== undefined){
6890 btnEl.dom.tabIndex = this.tabIndex;
6893 if(typeof this.tooltip == 'object'){
6894 Roo.QuickTips.tips(Roo.apply({
6898 btnEl.dom[this.tooltipType] = this.tooltip;
6902 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6906 this.el.dom.id = this.el.id = this.id;
6909 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6910 this.menu.on("show", this.onMenuShow, this);
6911 this.menu.on("hide", this.onMenuHide, this);
6913 btn.addClass("x-btn");
6914 if(Roo.isIE && !Roo.isIE7){
6915 this.autoWidth.defer(1, this);
6919 if(this.handleMouseEvents){
6920 btn.on("mouseover", this.onMouseOver, this);
6921 btn.on("mouseout", this.onMouseOut, this);
6922 btn.on("mousedown", this.onMouseDown, this);
6924 btn.on(this.clickEvent, this.onClick, this);
6925 //btn.on("mouseup", this.onMouseUp, this);
6932 Roo.ButtonToggleMgr.register(this);
6934 this.el.addClass("x-btn-pressed");
6937 var repeater = new Roo.util.ClickRepeater(btn,
6938 typeof this.repeat == "object" ? this.repeat : {}
6940 repeater.on("click", this.onClick, this);
6943 this.fireEvent('render', this);
6947 * Returns the button's underlying element
6948 * @return {Roo.Element} The element
6955 * Destroys this Button and removes any listeners.
6957 destroy : function(){
6958 Roo.ButtonToggleMgr.unregister(this);
6959 this.el.removeAllListeners();
6960 this.purgeListeners();
6965 autoWidth : function(){
6967 this.el.setWidth("auto");
6968 if(Roo.isIE7 && Roo.isStrict){
6969 var ib = this.el.child('button');
6970 if(ib && ib.getWidth() > 20){
6972 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6977 this.el.beginMeasure();
6979 if(this.el.getWidth() < this.minWidth){
6980 this.el.setWidth(this.minWidth);
6983 this.el.endMeasure();
6990 * Assigns this button's click handler
6991 * @param {Function} handler The function to call when the button is clicked
6992 * @param {Object} scope (optional) Scope for the function passed in
6994 setHandler : function(handler, scope){
6995 this.handler = handler;
7000 * Sets this button's text
7001 * @param {String} text The button text
7003 setText : function(text){
7006 this.el.child("td.x-btn-center button.x-btn-text").update(text);
7012 * Gets the text for this button
7013 * @return {String} The button text
7015 getText : function(){
7023 this.hidden = false;
7025 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7035 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7040 * Convenience function for boolean show/hide
7041 * @param {Boolean} visible True to show, false to hide
7043 setVisible: function(visible){
7052 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7053 * @param {Boolean} state (optional) Force a particular state
7055 toggle : function(state){
7056 state = state === undefined ? !this.pressed : state;
7057 if(state != this.pressed){
7059 this.el.addClass("x-btn-pressed");
7060 this.pressed = true;
7061 this.fireEvent("toggle", this, true);
7063 this.el.removeClass("x-btn-pressed");
7064 this.pressed = false;
7065 this.fireEvent("toggle", this, false);
7067 if(this.toggleHandler){
7068 this.toggleHandler.call(this.scope || this, this, state);
7077 this.el.child('button:first').focus();
7081 * Disable this button
7083 disable : function(){
7085 this.el.addClass("x-btn-disabled");
7087 this.disabled = true;
7091 * Enable this button
7093 enable : function(){
7095 this.el.removeClass("x-btn-disabled");
7097 this.disabled = false;
7101 * Convenience function for boolean enable/disable
7102 * @param {Boolean} enabled True to enable, false to disable
7104 setDisabled : function(v){
7105 this[v !== true ? "enable" : "disable"]();
7109 onClick : function(e)
7118 if(this.enableToggle){
7121 if(this.menu && !this.menu.isVisible()){
7122 this.menu.show(this.el, this.menuAlign);
7124 this.fireEvent("click", this, e);
7126 this.el.removeClass("x-btn-over");
7127 this.handler.call(this.scope || this, this, e);
7132 onMouseOver : function(e){
7134 this.el.addClass("x-btn-over");
7135 this.fireEvent('mouseover', this, e);
7139 onMouseOut : function(e){
7140 if(!e.within(this.el, true)){
7141 this.el.removeClass("x-btn-over");
7142 this.fireEvent('mouseout', this, e);
7146 onFocus : function(e){
7148 this.el.addClass("x-btn-focus");
7152 onBlur : function(e){
7153 this.el.removeClass("x-btn-focus");
7156 onMouseDown : function(e){
7157 if(!this.disabled && e.button == 0){
7158 this.el.addClass("x-btn-click");
7159 Roo.get(document).on('mouseup', this.onMouseUp, this);
7163 onMouseUp : function(e){
7165 this.el.removeClass("x-btn-click");
7166 Roo.get(document).un('mouseup', this.onMouseUp, this);
7170 onMenuShow : function(e){
7171 this.el.addClass("x-btn-menu-active");
7174 onMenuHide : function(e){
7175 this.el.removeClass("x-btn-menu-active");
7179 // Private utility class used by Button
7180 Roo.ButtonToggleMgr = function(){
7183 function toggleGroup(btn, state){
7185 var g = groups[btn.toggleGroup];
7186 for(var i = 0, l = g.length; i < l; i++){
7195 register : function(btn){
7196 if(!btn.toggleGroup){
7199 var g = groups[btn.toggleGroup];
7201 g = groups[btn.toggleGroup] = [];
7204 btn.on("toggle", toggleGroup);
7207 unregister : function(btn){
7208 if(!btn.toggleGroup){
7211 var g = groups[btn.toggleGroup];
7214 btn.un("toggle", toggleGroup);
7220 * Ext JS Library 1.1.1
7221 * Copyright(c) 2006-2007, Ext JS, LLC.
7223 * Originally Released Under LGPL - original licence link has changed is not relivant.
7226 * <script type="text/javascript">
7230 * @class Roo.SplitButton
7231 * @extends Roo.Button
7232 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7233 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
7234 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7235 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7236 * @cfg {String} arrowTooltip The title attribute of the arrow
7238 * Create a new menu button
7239 * @param {String/HTMLElement/Element} renderTo The element to append the button to
7240 * @param {Object} config The config object
7242 Roo.SplitButton = function(renderTo, config){
7243 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7246 * Fires when this button's arrow is clicked
7247 * @param {SplitButton} this
7248 * @param {EventObject} e The click event
7250 this.addEvents({"arrowclick":true});
7253 Roo.extend(Roo.SplitButton, Roo.Button, {
7254 render : function(renderTo){
7255 // this is one sweet looking template!
7256 var tpl = new Roo.Template(
7257 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7258 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7259 '<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>',
7260 "</tbody></table></td><td>",
7261 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7262 '<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>',
7263 "</tbody></table></td></tr></table>"
7265 var btn = tpl.append(renderTo, [this.text, this.type], true);
7266 var btnEl = btn.child("button");
7268 btn.addClass(this.cls);
7271 btnEl.setStyle('background-image', 'url(' +this.icon +')');
7274 btnEl.addClass(this.iconCls);
7276 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7280 if(this.handleMouseEvents){
7281 btn.on("mouseover", this.onMouseOver, this);
7282 btn.on("mouseout", this.onMouseOut, this);
7283 btn.on("mousedown", this.onMouseDown, this);
7284 btn.on("mouseup", this.onMouseUp, this);
7286 btn.on(this.clickEvent, this.onClick, this);
7288 if(typeof this.tooltip == 'object'){
7289 Roo.QuickTips.tips(Roo.apply({
7293 btnEl.dom[this.tooltipType] = this.tooltip;
7296 if(this.arrowTooltip){
7297 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7306 this.el.addClass("x-btn-pressed");
7308 if(Roo.isIE && !Roo.isIE7){
7309 this.autoWidth.defer(1, this);
7314 this.menu.on("show", this.onMenuShow, this);
7315 this.menu.on("hide", this.onMenuHide, this);
7317 this.fireEvent('render', this);
7321 autoWidth : function(){
7323 var tbl = this.el.child("table:first");
7324 var tbl2 = this.el.child("table:last");
7325 this.el.setWidth("auto");
7326 tbl.setWidth("auto");
7327 if(Roo.isIE7 && Roo.isStrict){
7328 var ib = this.el.child('button:first');
7329 if(ib && ib.getWidth() > 20){
7331 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7336 this.el.beginMeasure();
7338 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7339 tbl.setWidth(this.minWidth-tbl2.getWidth());
7342 this.el.endMeasure();
7345 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7349 * Sets this button's click handler
7350 * @param {Function} handler The function to call when the button is clicked
7351 * @param {Object} scope (optional) Scope for the function passed above
7353 setHandler : function(handler, scope){
7354 this.handler = handler;
7359 * Sets this button's arrow click handler
7360 * @param {Function} handler The function to call when the arrow is clicked
7361 * @param {Object} scope (optional) Scope for the function passed above
7363 setArrowHandler : function(handler, scope){
7364 this.arrowHandler = handler;
7373 this.el.child("button:first").focus();
7378 onClick : function(e){
7381 if(e.getTarget(".x-btn-menu-arrow-wrap")){
7382 if(this.menu && !this.menu.isVisible()){
7383 this.menu.show(this.el, this.menuAlign);
7385 this.fireEvent("arrowclick", this, e);
7386 if(this.arrowHandler){
7387 this.arrowHandler.call(this.scope || this, this, e);
7390 this.fireEvent("click", this, e);
7392 this.handler.call(this.scope || this, this, e);
7398 onMouseDown : function(e){
7400 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7404 onMouseUp : function(e){
7405 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7411 Roo.MenuButton = Roo.SplitButton;/*
7413 * Ext JS Library 1.1.1
7414 * Copyright(c) 2006-2007, Ext JS, LLC.
7416 * Originally Released Under LGPL - original licence link has changed is not relivant.
7419 * <script type="text/javascript">
7423 * @class Roo.Toolbar
7424 * Basic Toolbar class.
7426 * Creates a new Toolbar
7427 * @param {Object} container The config object
7429 Roo.Toolbar = function(container, buttons, config)
7431 /// old consturctor format still supported..
7432 if(container instanceof Array){ // omit the container for later rendering
7433 buttons = container;
7437 if (typeof(container) == 'object' && container.xtype) {
7439 container = config.container;
7440 buttons = config.buttons || []; // not really - use items!!
7443 if (config && config.items) {
7444 xitems = config.items;
7445 delete config.items;
7447 Roo.apply(this, config);
7448 this.buttons = buttons;
7451 this.render(container);
7453 this.xitems = xitems;
7454 Roo.each(xitems, function(b) {
7460 Roo.Toolbar.prototype = {
7462 * @cfg {Array} items
7463 * array of button configs or elements to add (will be converted to a MixedCollection)
7467 * @cfg {String/HTMLElement/Element} container
7468 * The id or element that will contain the toolbar
7471 render : function(ct){
7472 this.el = Roo.get(ct);
7474 this.el.addClass(this.cls);
7476 // using a table allows for vertical alignment
7477 // 100% width is needed by Safari...
7478 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7479 this.tr = this.el.child("tr", true);
7481 this.items = new Roo.util.MixedCollection(false, function(o){
7482 return o.id || ("item" + (++autoId));
7485 this.add.apply(this, this.buttons);
7486 delete this.buttons;
7491 * Adds element(s) to the toolbar -- this function takes a variable number of
7492 * arguments of mixed type and adds them to the toolbar.
7493 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7495 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7496 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7497 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7498 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7499 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7500 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7501 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7502 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7503 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7505 * @param {Mixed} arg2
7506 * @param {Mixed} etc.
7509 var a = arguments, l = a.length;
7510 for(var i = 0; i < l; i++){
7515 _add : function(el) {
7518 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7521 if (el.applyTo){ // some kind of form field
7522 return this.addField(el);
7524 if (el.render){ // some kind of Toolbar.Item
7525 return this.addItem(el);
7527 if (typeof el == "string"){ // string
7528 if(el == "separator" || el == "-"){
7529 return this.addSeparator();
7532 return this.addSpacer();
7535 return this.addFill();
7537 return this.addText(el);
7540 if(el.tagName){ // element
7541 return this.addElement(el);
7543 if(typeof el == "object"){ // must be button config?
7544 return this.addButton(el);
7552 * Add an Xtype element
7553 * @param {Object} xtype Xtype Object
7554 * @return {Object} created Object
7556 addxtype : function(e){
7561 * Returns the Element for this toolbar.
7562 * @return {Roo.Element}
7570 * @return {Roo.Toolbar.Item} The separator item
7572 addSeparator : function(){
7573 return this.addItem(new Roo.Toolbar.Separator());
7577 * Adds a spacer element
7578 * @return {Roo.Toolbar.Spacer} The spacer item
7580 addSpacer : function(){
7581 return this.addItem(new Roo.Toolbar.Spacer());
7585 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7586 * @return {Roo.Toolbar.Fill} The fill item
7588 addFill : function(){
7589 return this.addItem(new Roo.Toolbar.Fill());
7593 * Adds any standard HTML element to the toolbar
7594 * @param {String/HTMLElement/Element} el The element or id of the element to add
7595 * @return {Roo.Toolbar.Item} The element's item
7597 addElement : function(el){
7598 return this.addItem(new Roo.Toolbar.Item(el));
7601 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7602 * @type Roo.util.MixedCollection
7607 * Adds any Toolbar.Item or subclass
7608 * @param {Roo.Toolbar.Item} item
7609 * @return {Roo.Toolbar.Item} The item
7611 addItem : function(item){
7612 var td = this.nextBlock();
7614 this.items.add(item);
7619 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7620 * @param {Object/Array} config A button config or array of configs
7621 * @return {Roo.Toolbar.Button/Array}
7623 addButton : function(config){
7624 if(config instanceof Array){
7626 for(var i = 0, len = config.length; i < len; i++) {
7627 buttons.push(this.addButton(config[i]));
7632 if(!(config instanceof Roo.Toolbar.Button)){
7634 new Roo.Toolbar.SplitButton(config) :
7635 new Roo.Toolbar.Button(config);
7637 var td = this.nextBlock();
7644 * Adds text to the toolbar
7645 * @param {String} text The text to add
7646 * @return {Roo.Toolbar.Item} The element's item
7648 addText : function(text){
7649 return this.addItem(new Roo.Toolbar.TextItem(text));
7653 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7654 * @param {Number} index The index where the item is to be inserted
7655 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7656 * @return {Roo.Toolbar.Button/Item}
7658 insertButton : function(index, item){
7659 if(item instanceof Array){
7661 for(var i = 0, len = item.length; i < len; i++) {
7662 buttons.push(this.insertButton(index + i, item[i]));
7666 if (!(item instanceof Roo.Toolbar.Button)){
7667 item = new Roo.Toolbar.Button(item);
7669 var td = document.createElement("td");
7670 this.tr.insertBefore(td, this.tr.childNodes[index]);
7672 this.items.insert(index, item);
7677 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7678 * @param {Object} config
7679 * @return {Roo.Toolbar.Item} The element's item
7681 addDom : function(config, returnEl){
7682 var td = this.nextBlock();
7683 Roo.DomHelper.overwrite(td, config);
7684 var ti = new Roo.Toolbar.Item(td.firstChild);
7691 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7692 * @type Roo.util.MixedCollection
7697 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7698 * Note: the field should not have been rendered yet. For a field that has already been
7699 * rendered, use {@link #addElement}.
7700 * @param {Roo.form.Field} field
7701 * @return {Roo.ToolbarItem}
7705 addField : function(field) {
7708 this.fields = new Roo.util.MixedCollection(false, function(o){
7709 return o.id || ("item" + (++autoId));
7714 var td = this.nextBlock();
7716 var ti = new Roo.Toolbar.Item(td.firstChild);
7719 this.fields.add(field);
7730 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7731 this.el.child('div').hide();
7739 this.el.child('div').show();
7743 nextBlock : function(){
7744 var td = document.createElement("td");
7745 this.tr.appendChild(td);
7750 destroy : function(){
7751 if(this.items){ // rendered?
7752 Roo.destroy.apply(Roo, this.items.items);
7754 if(this.fields){ // rendered?
7755 Roo.destroy.apply(Roo, this.fields.items);
7757 Roo.Element.uncache(this.el, this.tr);
7762 * @class Roo.Toolbar.Item
7763 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7765 * Creates a new Item
7766 * @param {HTMLElement} el
7768 Roo.Toolbar.Item = function(el){
7770 if (typeof (el.xtype) != 'undefined') {
7775 this.el = Roo.getDom(el);
7776 this.id = Roo.id(this.el);
7777 this.hidden = false;
7782 * Fires when the button is rendered
7783 * @param {Button} this
7787 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7789 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7790 //Roo.Toolbar.Item.prototype = {
7793 * Get this item's HTML Element
7794 * @return {HTMLElement}
7801 render : function(td){
7804 td.appendChild(this.el);
7806 this.fireEvent('render', this);
7810 * Removes and destroys this item.
7812 destroy : function(){
7813 this.td.parentNode.removeChild(this.td);
7820 this.hidden = false;
7821 this.td.style.display = "";
7829 this.td.style.display = "none";
7833 * Convenience function for boolean show/hide.
7834 * @param {Boolean} visible true to show/false to hide
7836 setVisible: function(visible){
7845 * Try to focus this item.
7848 Roo.fly(this.el).focus();
7852 * Disables this item.
7854 disable : function(){
7855 Roo.fly(this.td).addClass("x-item-disabled");
7856 this.disabled = true;
7857 this.el.disabled = true;
7861 * Enables this item.
7863 enable : function(){
7864 Roo.fly(this.td).removeClass("x-item-disabled");
7865 this.disabled = false;
7866 this.el.disabled = false;
7872 * @class Roo.Toolbar.Separator
7873 * @extends Roo.Toolbar.Item
7874 * A simple toolbar separator class
7876 * Creates a new Separator
7878 Roo.Toolbar.Separator = function(cfg){
7880 var s = document.createElement("span");
7881 s.className = "ytb-sep";
7886 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7888 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7890 disable:Roo.emptyFn,
7895 * @class Roo.Toolbar.Spacer
7896 * @extends Roo.Toolbar.Item
7897 * A simple element that adds extra horizontal space to a toolbar.
7899 * Creates a new Spacer
7901 Roo.Toolbar.Spacer = function(cfg){
7902 var s = document.createElement("div");
7903 s.className = "ytb-spacer";
7907 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7909 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7911 disable:Roo.emptyFn,
7916 * @class Roo.Toolbar.Fill
7917 * @extends Roo.Toolbar.Spacer
7918 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7920 * Creates a new Spacer
7922 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7924 render : function(td){
7925 td.style.width = '100%';
7926 Roo.Toolbar.Fill.superclass.render.call(this, td);
7931 * @class Roo.Toolbar.TextItem
7932 * @extends Roo.Toolbar.Item
7933 * A simple class that renders text directly into a toolbar.
7935 * Creates a new TextItem
7936 * @param {String} text
7938 Roo.Toolbar.TextItem = function(cfg){
7939 var text = cfg || "";
7940 if (typeof(cfg) == 'object') {
7941 text = cfg.text || "";
7945 var s = document.createElement("span");
7946 s.className = "ytb-text";
7952 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7954 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7958 disable:Roo.emptyFn,
7963 * @class Roo.Toolbar.Button
7964 * @extends Roo.Button
7965 * A button that renders into a toolbar.
7967 * Creates a new Button
7968 * @param {Object} config A standard {@link Roo.Button} config object
7970 Roo.Toolbar.Button = function(config){
7971 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7973 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7974 render : function(td){
7976 Roo.Toolbar.Button.superclass.render.call(this, td);
7980 * Removes and destroys this button
7982 destroy : function(){
7983 Roo.Toolbar.Button.superclass.destroy.call(this);
7984 this.td.parentNode.removeChild(this.td);
7991 this.hidden = false;
7992 this.td.style.display = "";
8000 this.td.style.display = "none";
8004 * Disables this item
8006 disable : function(){
8007 Roo.fly(this.td).addClass("x-item-disabled");
8008 this.disabled = true;
8014 enable : function(){
8015 Roo.fly(this.td).removeClass("x-item-disabled");
8016 this.disabled = false;
8020 Roo.ToolbarButton = Roo.Toolbar.Button;
8023 * @class Roo.Toolbar.SplitButton
8024 * @extends Roo.SplitButton
8025 * A menu button that renders into a toolbar.
8027 * Creates a new SplitButton
8028 * @param {Object} config A standard {@link Roo.SplitButton} config object
8030 Roo.Toolbar.SplitButton = function(config){
8031 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8033 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8034 render : function(td){
8036 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8040 * Removes and destroys this button
8042 destroy : function(){
8043 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8044 this.td.parentNode.removeChild(this.td);
8051 this.hidden = false;
8052 this.td.style.display = "";
8060 this.td.style.display = "none";
8065 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8067 * Ext JS Library 1.1.1
8068 * Copyright(c) 2006-2007, Ext JS, LLC.
8070 * Originally Released Under LGPL - original licence link has changed is not relivant.
8073 * <script type="text/javascript">
8077 * @class Roo.PagingToolbar
8078 * @extends Roo.Toolbar
8079 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8081 * Create a new PagingToolbar
8082 * @param {Object} config The config object
8084 Roo.PagingToolbar = function(el, ds, config)
8086 // old args format still supported... - xtype is prefered..
8087 if (typeof(el) == 'object' && el.xtype) {
8088 // created from xtype...
8091 el = config.container;
8095 items = config.items;
8099 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8102 this.renderButtons(this.el);
8105 // supprot items array.
8107 Roo.each(items, function(e) {
8108 this.add(Roo.factory(e));
8113 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8115 * @cfg {Roo.data.Store} dataSource
8116 * The underlying data store providing the paged data
8119 * @cfg {String/HTMLElement/Element} container
8120 * container The id or element that will contain the toolbar
8123 * @cfg {Boolean} displayInfo
8124 * True to display the displayMsg (defaults to false)
8127 * @cfg {Number} pageSize
8128 * The number of records to display per page (defaults to 20)
8132 * @cfg {String} displayMsg
8133 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8135 displayMsg : 'Displaying {0} - {1} of {2}',
8137 * @cfg {String} emptyMsg
8138 * The message to display when no records are found (defaults to "No data to display")
8140 emptyMsg : 'No data to display',
8142 * Customizable piece of the default paging text (defaults to "Page")
8145 beforePageText : "Page",
8147 * Customizable piece of the default paging text (defaults to "of %0")
8150 afterPageText : "of {0}",
8152 * Customizable piece of the default paging text (defaults to "First Page")
8155 firstText : "First Page",
8157 * Customizable piece of the default paging text (defaults to "Previous Page")
8160 prevText : "Previous Page",
8162 * Customizable piece of the default paging text (defaults to "Next Page")
8165 nextText : "Next Page",
8167 * Customizable piece of the default paging text (defaults to "Last Page")
8170 lastText : "Last Page",
8172 * Customizable piece of the default paging text (defaults to "Refresh")
8175 refreshText : "Refresh",
8178 renderButtons : function(el){
8179 Roo.PagingToolbar.superclass.render.call(this, el);
8180 this.first = this.addButton({
8181 tooltip: this.firstText,
8182 cls: "x-btn-icon x-grid-page-first",
8184 handler: this.onClick.createDelegate(this, ["first"])
8186 this.prev = this.addButton({
8187 tooltip: this.prevText,
8188 cls: "x-btn-icon x-grid-page-prev",
8190 handler: this.onClick.createDelegate(this, ["prev"])
8192 //this.addSeparator();
8193 this.add(this.beforePageText);
8194 this.field = Roo.get(this.addDom({
8199 cls: "x-grid-page-number"
8201 this.field.on("keydown", this.onPagingKeydown, this);
8202 this.field.on("focus", function(){this.dom.select();});
8203 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8204 this.field.setHeight(18);
8205 //this.addSeparator();
8206 this.next = this.addButton({
8207 tooltip: this.nextText,
8208 cls: "x-btn-icon x-grid-page-next",
8210 handler: this.onClick.createDelegate(this, ["next"])
8212 this.last = this.addButton({
8213 tooltip: this.lastText,
8214 cls: "x-btn-icon x-grid-page-last",
8216 handler: this.onClick.createDelegate(this, ["last"])
8218 //this.addSeparator();
8219 this.loading = this.addButton({
8220 tooltip: this.refreshText,
8221 cls: "x-btn-icon x-grid-loading",
8222 handler: this.onClick.createDelegate(this, ["refresh"])
8225 if(this.displayInfo){
8226 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8231 updateInfo : function(){
8233 var count = this.ds.getCount();
8234 var msg = count == 0 ?
8238 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
8240 this.displayEl.update(msg);
8245 onLoad : function(ds, r, o){
8246 this.cursor = o.params ? o.params.start : 0;
8247 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8249 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8250 this.field.dom.value = ap;
8251 this.first.setDisabled(ap == 1);
8252 this.prev.setDisabled(ap == 1);
8253 this.next.setDisabled(ap == ps);
8254 this.last.setDisabled(ap == ps);
8255 this.loading.enable();
8260 getPageData : function(){
8261 var total = this.ds.getTotalCount();
8264 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8265 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8270 onLoadError : function(){
8271 this.loading.enable();
8275 onPagingKeydown : function(e){
8277 var d = this.getPageData();
8279 var v = this.field.dom.value, pageNum;
8280 if(!v || isNaN(pageNum = parseInt(v, 10))){
8281 this.field.dom.value = d.activePage;
8284 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8285 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8288 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))
8290 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8291 this.field.dom.value = pageNum;
8292 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8295 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8297 var v = this.field.dom.value, pageNum;
8298 var increment = (e.shiftKey) ? 10 : 1;
8299 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8302 if(!v || isNaN(pageNum = parseInt(v, 10))) {
8303 this.field.dom.value = d.activePage;
8306 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8308 this.field.dom.value = parseInt(v, 10) + increment;
8309 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8310 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8317 beforeLoad : function(){
8319 this.loading.disable();
8324 onClick : function(which){
8328 ds.load({params:{start: 0, limit: this.pageSize}});
8331 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8334 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8337 var total = ds.getTotalCount();
8338 var extra = total % this.pageSize;
8339 var lastStart = extra ? (total - extra) : total-this.pageSize;
8340 ds.load({params:{start: lastStart, limit: this.pageSize}});
8343 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8349 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8350 * @param {Roo.data.Store} store The data store to unbind
8352 unbind : function(ds){
8353 ds.un("beforeload", this.beforeLoad, this);
8354 ds.un("load", this.onLoad, this);
8355 ds.un("loadexception", this.onLoadError, this);
8356 ds.un("remove", this.updateInfo, this);
8357 ds.un("add", this.updateInfo, this);
8358 this.ds = undefined;
8362 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8363 * @param {Roo.data.Store} store The data store to bind
8365 bind : function(ds){
8366 ds.on("beforeload", this.beforeLoad, this);
8367 ds.on("load", this.onLoad, this);
8368 ds.on("loadexception", this.onLoadError, this);
8369 ds.on("remove", this.updateInfo, this);
8370 ds.on("add", this.updateInfo, this);
8375 * Ext JS Library 1.1.1
8376 * Copyright(c) 2006-2007, Ext JS, LLC.
8378 * Originally Released Under LGPL - original licence link has changed is not relivant.
8381 * <script type="text/javascript">
8385 * @class Roo.Resizable
8386 * @extends Roo.util.Observable
8387 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8388 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8389 * 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
8390 * the element will be wrapped for you automatically.</p>
8391 * <p>Here is the list of valid resize handles:</p>
8394 ------ -------------------
8403 'hd' horizontal drag
8406 * <p>Here's an example showing the creation of a typical Resizable:</p>
8408 var resizer = new Roo.Resizable("element-id", {
8416 resizer.on("resize", myHandler);
8418 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8419 * resizer.east.setDisplayed(false);</p>
8420 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8421 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8422 * resize operation's new size (defaults to [0, 0])
8423 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8424 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8425 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8426 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8427 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8428 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8429 * @cfg {Number} width The width of the element in pixels (defaults to null)
8430 * @cfg {Number} height The height of the element in pixels (defaults to null)
8431 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8432 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8433 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8434 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8435 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8436 * in favor of the handles config option (defaults to false)
8437 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8438 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8439 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8440 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8441 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8442 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8443 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8444 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8445 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8446 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8447 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8449 * Create a new resizable component
8450 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8451 * @param {Object} config configuration options
8453 Roo.Resizable = function(el, config)
8455 this.el = Roo.get(el);
8457 if(config && config.wrap){
8458 config.resizeChild = this.el;
8459 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8460 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8461 this.el.setStyle("overflow", "hidden");
8462 this.el.setPositioning(config.resizeChild.getPositioning());
8463 config.resizeChild.clearPositioning();
8464 if(!config.width || !config.height){
8465 var csize = config.resizeChild.getSize();
8466 this.el.setSize(csize.width, csize.height);
8468 if(config.pinned && !config.adjustments){
8469 config.adjustments = "auto";
8473 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8474 this.proxy.unselectable();
8475 this.proxy.enableDisplayMode('block');
8477 Roo.apply(this, config);
8480 this.disableTrackOver = true;
8481 this.el.addClass("x-resizable-pinned");
8483 // if the element isn't positioned, make it relative
8484 var position = this.el.getStyle("position");
8485 if(position != "absolute" && position != "fixed"){
8486 this.el.setStyle("position", "relative");
8488 if(!this.handles){ // no handles passed, must be legacy style
8489 this.handles = 's,e,se';
8490 if(this.multiDirectional){
8491 this.handles += ',n,w';
8494 if(this.handles == "all"){
8495 this.handles = "n s e w ne nw se sw";
8497 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8498 var ps = Roo.Resizable.positions;
8499 for(var i = 0, len = hs.length; i < len; i++){
8500 if(hs[i] && ps[hs[i]]){
8501 var pos = ps[hs[i]];
8502 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8506 this.corner = this.southeast;
8508 // updateBox = the box can move..
8509 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8510 this.updateBox = true;
8513 this.activeHandle = null;
8515 if(this.resizeChild){
8516 if(typeof this.resizeChild == "boolean"){
8517 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8519 this.resizeChild = Roo.get(this.resizeChild, true);
8523 if(this.adjustments == "auto"){
8524 var rc = this.resizeChild;
8525 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8526 if(rc && (hw || hn)){
8527 rc.position("relative");
8528 rc.setLeft(hw ? hw.el.getWidth() : 0);
8529 rc.setTop(hn ? hn.el.getHeight() : 0);
8531 this.adjustments = [
8532 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8533 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8538 this.dd = this.dynamic ?
8539 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8540 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8546 * @event beforeresize
8547 * Fired before resize is allowed. Set enabled to false to cancel resize.
8548 * @param {Roo.Resizable} this
8549 * @param {Roo.EventObject} e The mousedown event
8551 "beforeresize" : true,
8555 * @param {Roo.Resizable} this
8556 * @param {Number} x The new x position
8557 * @param {Number} y The new y position
8558 * @param {Number} w The new w width
8559 * @param {Number} h The new h hight
8560 * @param {Roo.EventObject} e The mouseup event
8565 * Fired after a resize.
8566 * @param {Roo.Resizable} this
8567 * @param {Number} width The new width
8568 * @param {Number} height The new height
8569 * @param {Roo.EventObject} e The mouseup event
8574 if(this.width !== null && this.height !== null){
8575 this.resizeTo(this.width, this.height);
8577 this.updateChildSize();
8580 this.el.dom.style.zoom = 1;
8582 Roo.Resizable.superclass.constructor.call(this);
8585 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8586 resizeChild : false,
8587 adjustments : [0, 0],
8597 multiDirectional : false,
8598 disableTrackOver : false,
8599 easing : 'easeOutStrong',
8601 heightIncrement : 0,
8605 preserveRatio : false,
8612 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8614 constrainTo: undefined,
8616 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8618 resizeRegion: undefined,
8622 * Perform a manual resize
8623 * @param {Number} width
8624 * @param {Number} height
8626 resizeTo : function(width, height){
8627 this.el.setSize(width, height);
8628 this.updateChildSize();
8629 this.fireEvent("resize", this, width, height, null);
8633 startSizing : function(e, handle){
8634 this.fireEvent("beforeresize", this, e);
8635 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8638 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8639 this.overlay.unselectable();
8640 this.overlay.enableDisplayMode("block");
8641 this.overlay.on("mousemove", this.onMouseMove, this);
8642 this.overlay.on("mouseup", this.onMouseUp, this);
8644 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8646 this.resizing = true;
8647 this.startBox = this.el.getBox();
8648 this.startPoint = e.getXY();
8649 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8650 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8652 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8653 this.overlay.show();
8655 if(this.constrainTo) {
8656 var ct = Roo.get(this.constrainTo);
8657 this.resizeRegion = ct.getRegion().adjust(
8658 ct.getFrameWidth('t'),
8659 ct.getFrameWidth('l'),
8660 -ct.getFrameWidth('b'),
8661 -ct.getFrameWidth('r')
8665 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8667 this.proxy.setBox(this.startBox);
8669 this.proxy.setStyle('visibility', 'visible');
8675 onMouseDown : function(handle, e){
8678 this.activeHandle = handle;
8679 this.startSizing(e, handle);
8684 onMouseUp : function(e){
8685 var size = this.resizeElement();
8686 this.resizing = false;
8688 this.overlay.hide();
8690 this.fireEvent("resize", this, size.width, size.height, e);
8694 updateChildSize : function(){
8696 if(this.resizeChild){
8698 var child = this.resizeChild;
8699 var adj = this.adjustments;
8700 if(el.dom.offsetWidth){
8701 var b = el.getSize(true);
8702 child.setSize(b.width+adj[0], b.height+adj[1]);
8704 // Second call here for IE
8705 // The first call enables instant resizing and
8706 // the second call corrects scroll bars if they
8709 setTimeout(function(){
8710 if(el.dom.offsetWidth){
8711 var b = el.getSize(true);
8712 child.setSize(b.width+adj[0], b.height+adj[1]);
8720 snap : function(value, inc, min){
8721 if(!inc || !value) {
8724 var newValue = value;
8725 var m = value % inc;
8728 newValue = value + (inc-m);
8730 newValue = value - m;
8733 return Math.max(min, newValue);
8737 resizeElement : function(){
8738 var box = this.proxy.getBox();
8740 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8742 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8744 this.updateChildSize();
8752 constrain : function(v, diff, m, mx){
8755 }else if(v - diff > mx){
8762 onMouseMove : function(e){
8765 try{// try catch so if something goes wrong the user doesn't get hung
8767 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8771 //var curXY = this.startPoint;
8772 var curSize = this.curSize || this.startBox;
8773 var x = this.startBox.x, y = this.startBox.y;
8775 var w = curSize.width, h = curSize.height;
8777 var mw = this.minWidth, mh = this.minHeight;
8778 var mxw = this.maxWidth, mxh = this.maxHeight;
8779 var wi = this.widthIncrement;
8780 var hi = this.heightIncrement;
8782 var eventXY = e.getXY();
8783 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8784 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8786 var pos = this.activeHandle.position;
8791 w = Math.min(Math.max(mw, w), mxw);
8796 h = Math.min(Math.max(mh, h), mxh);
8801 w = Math.min(Math.max(mw, w), mxw);
8802 h = Math.min(Math.max(mh, h), mxh);
8805 diffY = this.constrain(h, diffY, mh, mxh);
8812 var adiffX = Math.abs(diffX);
8813 var sub = (adiffX % wi); // how much
8814 if (sub > (wi/2)) { // far enough to snap
8815 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8817 // remove difference..
8818 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8822 x = Math.max(this.minX, x);
8825 diffX = this.constrain(w, diffX, mw, mxw);
8831 w = Math.min(Math.max(mw, w), mxw);
8832 diffY = this.constrain(h, diffY, mh, mxh);
8837 diffX = this.constrain(w, diffX, mw, mxw);
8838 diffY = this.constrain(h, diffY, mh, mxh);
8845 diffX = this.constrain(w, diffX, mw, mxw);
8847 h = Math.min(Math.max(mh, h), mxh);
8853 var sw = this.snap(w, wi, mw);
8854 var sh = this.snap(h, hi, mh);
8855 if(sw != w || sh != h){
8878 if(this.preserveRatio){
8883 h = Math.min(Math.max(mh, h), mxh);
8888 w = Math.min(Math.max(mw, w), mxw);
8893 w = Math.min(Math.max(mw, w), mxw);
8899 w = Math.min(Math.max(mw, w), mxw);
8905 h = Math.min(Math.max(mh, h), mxh);
8913 h = Math.min(Math.max(mh, h), mxh);
8923 h = Math.min(Math.max(mh, h), mxh);
8931 if (pos == 'hdrag') {
8934 this.proxy.setBounds(x, y, w, h);
8936 this.resizeElement();
8940 this.fireEvent("resizing", this, x, y, w, h, e);
8944 handleOver : function(){
8946 this.el.addClass("x-resizable-over");
8951 handleOut : function(){
8953 this.el.removeClass("x-resizable-over");
8958 * Returns the element this component is bound to.
8959 * @return {Roo.Element}
8966 * Returns the resizeChild element (or null).
8967 * @return {Roo.Element}
8969 getResizeChild : function(){
8970 return this.resizeChild;
8972 groupHandler : function()
8977 * Destroys this resizable. If the element was wrapped and
8978 * removeEl is not true then the element remains.
8979 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8981 destroy : function(removeEl){
8982 this.proxy.remove();
8984 this.overlay.removeAllListeners();
8985 this.overlay.remove();
8987 var ps = Roo.Resizable.positions;
8989 if(typeof ps[k] != "function" && this[ps[k]]){
8990 var h = this[ps[k]];
8991 h.el.removeAllListeners();
9003 // hash to map config positions to true positions
9004 Roo.Resizable.positions = {
9005 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
9010 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9012 // only initialize the template if resizable is used
9013 var tpl = Roo.DomHelper.createTemplate(
9014 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9017 Roo.Resizable.Handle.prototype.tpl = tpl;
9019 this.position = pos;
9021 // show north drag fro topdra
9022 var handlepos = pos == 'hdrag' ? 'north' : pos;
9024 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9025 if (pos == 'hdrag') {
9026 this.el.setStyle('cursor', 'pointer');
9028 this.el.unselectable();
9030 this.el.setOpacity(0);
9032 this.el.on("mousedown", this.onMouseDown, this);
9033 if(!disableTrackOver){
9034 this.el.on("mouseover", this.onMouseOver, this);
9035 this.el.on("mouseout", this.onMouseOut, this);
9040 Roo.Resizable.Handle.prototype = {
9041 afterResize : function(rz){
9046 onMouseDown : function(e){
9047 this.rz.onMouseDown(this, e);
9050 onMouseOver : function(e){
9051 this.rz.handleOver(this, e);
9054 onMouseOut : function(e){
9055 this.rz.handleOut(this, e);
9059 * Ext JS Library 1.1.1
9060 * Copyright(c) 2006-2007, Ext JS, LLC.
9062 * Originally Released Under LGPL - original licence link has changed is not relivant.
9065 * <script type="text/javascript">
9070 * @extends Roo.Component
9071 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9073 * Create a new Editor
9074 * @param {Roo.form.Field} field The Field object (or descendant)
9075 * @param {Object} config The config object
9077 Roo.Editor = function(field, config){
9078 Roo.Editor.superclass.constructor.call(this, config);
9082 * @event beforestartedit
9083 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
9084 * false from the handler of this event.
9085 * @param {Editor} this
9086 * @param {Roo.Element} boundEl The underlying element bound to this editor
9087 * @param {Mixed} value The field value being set
9089 "beforestartedit" : true,
9092 * Fires when this editor is displayed
9093 * @param {Roo.Element} boundEl The underlying element bound to this editor
9094 * @param {Mixed} value The starting field value
9098 * @event beforecomplete
9099 * Fires after a change has been made to the field, but before the change is reflected in the underlying
9100 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
9101 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9102 * event will not fire since no edit actually occurred.
9103 * @param {Editor} this
9104 * @param {Mixed} value The current field value
9105 * @param {Mixed} startValue The original field value
9107 "beforecomplete" : true,
9110 * Fires after editing is complete and any changed value has been written to the underlying field.
9111 * @param {Editor} this
9112 * @param {Mixed} value The current field value
9113 * @param {Mixed} startValue The original field value
9118 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9119 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9120 * @param {Roo.form.Field} this
9121 * @param {Roo.EventObject} e The event object
9127 Roo.extend(Roo.Editor, Roo.Component, {
9129 * @cfg {Boolean/String} autosize
9130 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9131 * or "height" to adopt the height only (defaults to false)
9134 * @cfg {Boolean} revertInvalid
9135 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9136 * validation fails (defaults to true)
9139 * @cfg {Boolean} ignoreNoChange
9140 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9141 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
9142 * will never be ignored.
9145 * @cfg {Boolean} hideEl
9146 * False to keep the bound element visible while the editor is displayed (defaults to true)
9149 * @cfg {Mixed} value
9150 * The data value of the underlying field (defaults to "")
9154 * @cfg {String} alignment
9155 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9159 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9160 * for bottom-right shadow (defaults to "frame")
9164 * @cfg {Boolean} constrain True to constrain the editor to the viewport
9168 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9170 completeOnEnter : false,
9172 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9174 cancelOnEsc : false,
9176 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9181 onRender : function(ct, position){
9182 this.el = new Roo.Layer({
9183 shadow: this.shadow,
9189 constrain: this.constrain
9191 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9192 if(this.field.msgTarget != 'title'){
9193 this.field.msgTarget = 'qtip';
9195 this.field.render(this.el);
9197 this.field.el.dom.setAttribute('autocomplete', 'off');
9199 this.field.on("specialkey", this.onSpecialKey, this);
9200 if(this.swallowKeys){
9201 this.field.el.swallowEvent(['keydown','keypress']);
9204 this.field.on("blur", this.onBlur, this);
9205 if(this.field.grow){
9206 this.field.on("autosize", this.el.sync, this.el, {delay:1});
9210 onSpecialKey : function(field, e)
9212 //Roo.log('editor onSpecialKey');
9213 if(this.completeOnEnter && e.getKey() == e.ENTER){
9215 this.completeEdit();
9218 // do not fire special key otherwise it might hide close the editor...
9219 if(e.getKey() == e.ENTER){
9222 if(this.cancelOnEsc && e.getKey() == e.ESC){
9226 this.fireEvent('specialkey', field, e);
9231 * Starts the editing process and shows the editor.
9232 * @param {String/HTMLElement/Element} el The element to edit
9233 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9234 * to the innerHTML of el.
9236 startEdit : function(el, value){
9238 this.completeEdit();
9240 this.boundEl = Roo.get(el);
9241 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9243 this.render(this.parentEl || document.body);
9245 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9248 this.startValue = v;
9249 this.field.setValue(v);
9251 var sz = this.boundEl.getSize();
9252 switch(this.autoSize){
9254 this.setSize(sz.width, "");
9257 this.setSize("", sz.height);
9260 this.setSize(sz.width, sz.height);
9263 this.el.alignTo(this.boundEl, this.alignment);
9264 this.editing = true;
9266 Roo.QuickTips.disable();
9272 * Sets the height and width of this editor.
9273 * @param {Number} width The new width
9274 * @param {Number} height The new height
9276 setSize : function(w, h){
9277 this.field.setSize(w, h);
9284 * Realigns the editor to the bound field based on the current alignment config value.
9286 realign : function(){
9287 this.el.alignTo(this.boundEl, this.alignment);
9291 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9292 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9294 completeEdit : function(remainVisible){
9298 var v = this.getValue();
9299 if(this.revertInvalid !== false && !this.field.isValid()){
9300 v = this.startValue;
9301 this.cancelEdit(true);
9303 if(String(v) === String(this.startValue) && this.ignoreNoChange){
9304 this.editing = false;
9308 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9309 this.editing = false;
9310 if(this.updateEl && this.boundEl){
9311 this.boundEl.update(v);
9313 if(remainVisible !== true){
9316 this.fireEvent("complete", this, v, this.startValue);
9321 onShow : function(){
9323 if(this.hideEl !== false){
9324 this.boundEl.hide();
9327 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9328 this.fixIEFocus = true;
9329 this.deferredFocus.defer(50, this);
9333 this.fireEvent("startedit", this.boundEl, this.startValue);
9336 deferredFocus : function(){
9343 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
9344 * reverted to the original starting value.
9345 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9346 * cancel (defaults to false)
9348 cancelEdit : function(remainVisible){
9350 this.setValue(this.startValue);
9351 if(remainVisible !== true){
9358 onBlur : function(){
9359 if(this.allowBlur !== true && this.editing){
9360 this.completeEdit();
9365 onHide : function(){
9367 this.completeEdit();
9371 if(this.field.collapse){
9372 this.field.collapse();
9375 if(this.hideEl !== false){
9376 this.boundEl.show();
9379 Roo.QuickTips.enable();
9384 * Sets the data value of the editor
9385 * @param {Mixed} value Any valid value supported by the underlying field
9387 setValue : function(v){
9388 this.field.setValue(v);
9392 * Gets the data value of the editor
9393 * @return {Mixed} The data value
9395 getValue : function(){
9396 return this.field.getValue();
9400 * Ext JS Library 1.1.1
9401 * Copyright(c) 2006-2007, Ext JS, LLC.
9403 * Originally Released Under LGPL - original licence link has changed is not relivant.
9406 * <script type="text/javascript">
9410 * @class Roo.BasicDialog
9411 * @extends Roo.util.Observable
9412 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9414 var dlg = new Roo.BasicDialog("my-dlg", {
9423 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9424 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9425 dlg.addButton('Cancel', dlg.hide, dlg);
9428 <b>A Dialog should always be a direct child of the body element.</b>
9429 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9430 * @cfg {String} title Default text to display in the title bar (defaults to null)
9431 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9432 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9433 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9434 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9435 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9436 * (defaults to null with no animation)
9437 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9438 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9439 * property for valid values (defaults to 'all')
9440 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9441 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9442 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9443 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9444 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9445 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9446 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9447 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9448 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9449 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9450 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9451 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9452 * draggable = true (defaults to false)
9453 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9454 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9455 * shadow (defaults to false)
9456 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9457 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9458 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9459 * @cfg {Array} buttons Array of buttons
9460 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9462 * Create a new BasicDialog.
9463 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9464 * @param {Object} config Configuration options
9466 Roo.BasicDialog = function(el, config){
9467 this.el = Roo.get(el);
9468 var dh = Roo.DomHelper;
9469 if(!this.el && config && config.autoCreate){
9470 if(typeof config.autoCreate == "object"){
9471 if(!config.autoCreate.id){
9472 config.autoCreate.id = el;
9474 this.el = dh.append(document.body,
9475 config.autoCreate, true);
9477 this.el = dh.append(document.body,
9478 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9482 el.setDisplayed(true);
9483 el.hide = this.hideAction;
9485 el.addClass("x-dlg");
9487 Roo.apply(this, config);
9489 this.proxy = el.createProxy("x-dlg-proxy");
9490 this.proxy.hide = this.hideAction;
9491 this.proxy.setOpacity(.5);
9495 el.setWidth(config.width);
9498 el.setHeight(config.height);
9500 this.size = el.getSize();
9501 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9502 this.xy = [config.x,config.y];
9504 this.xy = el.getCenterXY(true);
9506 /** The header element @type Roo.Element */
9507 this.header = el.child("> .x-dlg-hd");
9508 /** The body element @type Roo.Element */
9509 this.body = el.child("> .x-dlg-bd");
9510 /** The footer element @type Roo.Element */
9511 this.footer = el.child("> .x-dlg-ft");
9514 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9517 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9520 this.header.unselectable();
9522 this.header.update(this.title);
9524 // this element allows the dialog to be focused for keyboard event
9525 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9526 this.focusEl.swallowEvent("click", true);
9528 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9530 // wrap the body and footer for special rendering
9531 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9533 this.bwrap.dom.appendChild(this.footer.dom);
9536 this.bg = this.el.createChild({
9537 tag: "div", cls:"x-dlg-bg",
9538 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9540 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9543 if(this.autoScroll !== false && !this.autoTabs){
9544 this.body.setStyle("overflow", "auto");
9547 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9549 if(this.closable !== false){
9550 this.el.addClass("x-dlg-closable");
9551 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9552 this.close.on("click", this.closeClick, this);
9553 this.close.addClassOnOver("x-dlg-close-over");
9555 if(this.collapsible !== false){
9556 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9557 this.collapseBtn.on("click", this.collapseClick, this);
9558 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9559 this.header.on("dblclick", this.collapseClick, this);
9561 if(this.resizable !== false){
9562 this.el.addClass("x-dlg-resizable");
9563 this.resizer = new Roo.Resizable(el, {
9564 minWidth: this.minWidth || 80,
9565 minHeight:this.minHeight || 80,
9566 handles: this.resizeHandles || "all",
9569 this.resizer.on("beforeresize", this.beforeResize, this);
9570 this.resizer.on("resize", this.onResize, this);
9572 if(this.draggable !== false){
9573 el.addClass("x-dlg-draggable");
9574 if (!this.proxyDrag) {
9575 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9578 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9580 dd.setHandleElId(this.header.id);
9581 dd.endDrag = this.endMove.createDelegate(this);
9582 dd.startDrag = this.startMove.createDelegate(this);
9583 dd.onDrag = this.onDrag.createDelegate(this);
9588 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9589 this.mask.enableDisplayMode("block");
9591 this.el.addClass("x-dlg-modal");
9594 this.shadow = new Roo.Shadow({
9595 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9596 offset : this.shadowOffset
9599 this.shadowOffset = 0;
9601 if(Roo.useShims && this.shim !== false){
9602 this.shim = this.el.createShim();
9603 this.shim.hide = this.hideAction;
9612 var bts= this.buttons;
9614 Roo.each(bts, function(b) {
9623 * Fires when a key is pressed
9624 * @param {Roo.BasicDialog} this
9625 * @param {Roo.EventObject} e
9630 * Fires when this dialog is moved by the user.
9631 * @param {Roo.BasicDialog} this
9632 * @param {Number} x The new page X
9633 * @param {Number} y The new page Y
9638 * Fires when this dialog is resized by the user.
9639 * @param {Roo.BasicDialog} this
9640 * @param {Number} width The new width
9641 * @param {Number} height The new height
9646 * Fires before this dialog is hidden.
9647 * @param {Roo.BasicDialog} this
9649 "beforehide" : true,
9652 * Fires when this dialog is hidden.
9653 * @param {Roo.BasicDialog} this
9658 * Fires before this dialog is shown.
9659 * @param {Roo.BasicDialog} this
9661 "beforeshow" : true,
9664 * Fires when this dialog is shown.
9665 * @param {Roo.BasicDialog} this
9669 el.on("keydown", this.onKeyDown, this);
9670 el.on("mousedown", this.toFront, this);
9671 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9673 Roo.DialogManager.register(this);
9674 Roo.BasicDialog.superclass.constructor.call(this);
9677 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9678 shadowOffset: Roo.isIE ? 6 : 5,
9682 defaultButton: null,
9683 buttonAlign: "right",
9688 * Sets the dialog title text
9689 * @param {String} text The title text to display
9690 * @return {Roo.BasicDialog} this
9692 setTitle : function(text){
9693 this.header.update(text);
9698 closeClick : function(){
9703 collapseClick : function(){
9704 this[this.collapsed ? "expand" : "collapse"]();
9708 * Collapses the dialog to its minimized state (only the title bar is visible).
9709 * Equivalent to the user clicking the collapse dialog button.
9711 collapse : function(){
9712 if(!this.collapsed){
9713 this.collapsed = true;
9714 this.el.addClass("x-dlg-collapsed");
9715 this.restoreHeight = this.el.getHeight();
9716 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9721 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9722 * clicking the expand dialog button.
9724 expand : function(){
9726 this.collapsed = false;
9727 this.el.removeClass("x-dlg-collapsed");
9728 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9733 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9734 * @return {Roo.TabPanel} The tabs component
9736 initTabs : function(){
9737 var tabs = this.getTabs();
9738 while(tabs.getTab(0)){
9741 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9743 tabs.addTab(Roo.id(dom), dom.title);
9751 beforeResize : function(){
9752 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9756 onResize : function(){
9758 this.syncBodyHeight();
9759 this.adjustAssets();
9761 this.fireEvent("resize", this, this.size.width, this.size.height);
9765 onKeyDown : function(e){
9766 if(this.isVisible()){
9767 this.fireEvent("keydown", this, e);
9772 * Resizes the dialog.
9773 * @param {Number} width
9774 * @param {Number} height
9775 * @return {Roo.BasicDialog} this
9777 resizeTo : function(width, height){
9778 this.el.setSize(width, height);
9779 this.size = {width: width, height: height};
9780 this.syncBodyHeight();
9781 if(this.fixedcenter){
9784 if(this.isVisible()){
9786 this.adjustAssets();
9788 this.fireEvent("resize", this, width, height);
9794 * Resizes the dialog to fit the specified content size.
9795 * @param {Number} width
9796 * @param {Number} height
9797 * @return {Roo.BasicDialog} this
9799 setContentSize : function(w, h){
9800 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9801 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9802 //if(!this.el.isBorderBox()){
9803 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9804 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9807 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9808 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9810 this.resizeTo(w, h);
9815 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9816 * executed in response to a particular key being pressed while the dialog is active.
9817 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9818 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9819 * @param {Function} fn The function to call
9820 * @param {Object} scope (optional) The scope of the function
9821 * @return {Roo.BasicDialog} this
9823 addKeyListener : function(key, fn, scope){
9824 var keyCode, shift, ctrl, alt;
9825 if(typeof key == "object" && !(key instanceof Array)){
9826 keyCode = key["key"];
9827 shift = key["shift"];
9833 var handler = function(dlg, e){
9834 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9836 if(keyCode instanceof Array){
9837 for(var i = 0, len = keyCode.length; i < len; i++){
9838 if(keyCode[i] == k){
9839 fn.call(scope || window, dlg, k, e);
9845 fn.call(scope || window, dlg, k, e);
9850 this.on("keydown", handler);
9855 * Returns the TabPanel component (creates it if it doesn't exist).
9856 * Note: If you wish to simply check for the existence of tabs without creating them,
9857 * check for a null 'tabs' property.
9858 * @return {Roo.TabPanel} The tabs component
9860 getTabs : function(){
9862 this.el.addClass("x-dlg-auto-tabs");
9863 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9864 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9870 * Adds a button to the footer section of the dialog.
9871 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9872 * object or a valid Roo.DomHelper element config
9873 * @param {Function} handler The function called when the button is clicked
9874 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9875 * @return {Roo.Button} The new button
9877 addButton : function(config, handler, scope){
9878 var dh = Roo.DomHelper;
9880 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9882 if(!this.btnContainer){
9883 var tb = this.footer.createChild({
9885 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9886 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9888 this.btnContainer = tb.firstChild.firstChild.firstChild;
9893 minWidth: this.minButtonWidth,
9896 if(typeof config == "string"){
9897 bconfig.text = config;
9900 bconfig.dhconfig = config;
9902 Roo.apply(bconfig, config);
9906 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9907 bconfig.position = Math.max(0, bconfig.position);
9908 fc = this.btnContainer.childNodes[bconfig.position];
9911 var btn = new Roo.Button(
9913 this.btnContainer.insertBefore(document.createElement("td"),fc)
9914 : this.btnContainer.appendChild(document.createElement("td")),
9915 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9918 this.syncBodyHeight();
9921 * Array of all the buttons that have been added to this dialog via addButton
9926 this.buttons.push(btn);
9931 * Sets the default button to be focused when the dialog is displayed.
9932 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9933 * @return {Roo.BasicDialog} this
9935 setDefaultButton : function(btn){
9936 this.defaultButton = btn;
9941 getHeaderFooterHeight : function(safe){
9944 height += this.header.getHeight();
9947 var fm = this.footer.getMargins();
9948 height += (this.footer.getHeight()+fm.top+fm.bottom);
9950 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9951 height += this.centerBg.getPadding("tb");
9956 syncBodyHeight : function()
9958 var bd = this.body, // the text
9959 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9961 var height = this.size.height - this.getHeaderFooterHeight(false);
9962 bd.setHeight(height-bd.getMargins("tb"));
9963 var hh = this.header.getHeight();
9964 var h = this.size.height-hh;
9967 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9968 bw.setHeight(h-cb.getPadding("tb"));
9970 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9971 bd.setWidth(bw.getWidth(true));
9973 this.tabs.syncHeight();
9975 this.tabs.el.repaint();
9981 * Restores the previous state of the dialog if Roo.state is configured.
9982 * @return {Roo.BasicDialog} this
9984 restoreState : function(){
9985 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9986 if(box && box.width){
9987 this.xy = [box.x, box.y];
9988 this.resizeTo(box.width, box.height);
9994 beforeShow : function(){
9996 if(this.fixedcenter){
9997 this.xy = this.el.getCenterXY(true);
10000 Roo.get(document.body).addClass("x-body-masked");
10001 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10004 this.constrainXY();
10008 animShow : function(){
10009 var b = Roo.get(this.animateTarget).getBox();
10010 this.proxy.setSize(b.width, b.height);
10011 this.proxy.setLocation(b.x, b.y);
10013 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10014 true, .35, this.showEl.createDelegate(this));
10018 * Shows the dialog.
10019 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10020 * @return {Roo.BasicDialog} this
10022 show : function(animateTarget){
10023 if (this.fireEvent("beforeshow", this) === false){
10026 if(this.syncHeightBeforeShow){
10027 this.syncBodyHeight();
10028 }else if(this.firstShow){
10029 this.firstShow = false;
10030 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10032 this.animateTarget = animateTarget || this.animateTarget;
10033 if(!this.el.isVisible()){
10035 if(this.animateTarget && Roo.get(this.animateTarget)){
10045 showEl : function(){
10047 this.el.setXY(this.xy);
10049 this.adjustAssets(true);
10052 // IE peekaboo bug - fix found by Dave Fenwick
10056 this.fireEvent("show", this);
10060 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
10061 * dialog itself will receive focus.
10063 focus : function(){
10064 if(this.defaultButton){
10065 this.defaultButton.focus();
10067 this.focusEl.focus();
10072 constrainXY : function(){
10073 if(this.constraintoviewport !== false){
10074 if(!this.viewSize){
10075 if(this.container){
10076 var s = this.container.getSize();
10077 this.viewSize = [s.width, s.height];
10079 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10082 var s = Roo.get(this.container||document).getScroll();
10084 var x = this.xy[0], y = this.xy[1];
10085 var w = this.size.width, h = this.size.height;
10086 var vw = this.viewSize[0], vh = this.viewSize[1];
10087 // only move it if it needs it
10089 // first validate right/bottom
10090 if(x + w > vw+s.left){
10094 if(y + h > vh+s.top){
10098 // then make sure top/left isn't negative
10110 if(this.isVisible()){
10111 this.el.setLocation(x, y);
10112 this.adjustAssets();
10119 onDrag : function(){
10120 if(!this.proxyDrag){
10121 this.xy = this.el.getXY();
10122 this.adjustAssets();
10127 adjustAssets : function(doShow){
10128 var x = this.xy[0], y = this.xy[1];
10129 var w = this.size.width, h = this.size.height;
10130 if(doShow === true){
10132 this.shadow.show(this.el);
10138 if(this.shadow && this.shadow.isVisible()){
10139 this.shadow.show(this.el);
10141 if(this.shim && this.shim.isVisible()){
10142 this.shim.setBounds(x, y, w, h);
10147 adjustViewport : function(w, h){
10149 w = Roo.lib.Dom.getViewWidth();
10150 h = Roo.lib.Dom.getViewHeight();
10153 this.viewSize = [w, h];
10154 if(this.modal && this.mask.isVisible()){
10155 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10156 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10158 if(this.isVisible()){
10159 this.constrainXY();
10164 * Destroys this dialog and all its supporting elements (including any tabs, shim,
10165 * shadow, proxy, mask, etc.) Also removes all event listeners.
10166 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10168 destroy : function(removeEl){
10169 if(this.isVisible()){
10170 this.animateTarget = null;
10173 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10175 this.tabs.destroy(removeEl);
10188 for(var i = 0, len = this.buttons.length; i < len; i++){
10189 this.buttons[i].destroy();
10192 this.el.removeAllListeners();
10193 if(removeEl === true){
10194 this.el.update("");
10197 Roo.DialogManager.unregister(this);
10201 startMove : function(){
10202 if(this.proxyDrag){
10205 if(this.constraintoviewport !== false){
10206 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10211 endMove : function(){
10212 if(!this.proxyDrag){
10213 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10215 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10218 this.refreshSize();
10219 this.adjustAssets();
10221 this.fireEvent("move", this, this.xy[0], this.xy[1]);
10225 * Brings this dialog to the front of any other visible dialogs
10226 * @return {Roo.BasicDialog} this
10228 toFront : function(){
10229 Roo.DialogManager.bringToFront(this);
10234 * Sends this dialog to the back (under) of any other visible dialogs
10235 * @return {Roo.BasicDialog} this
10237 toBack : function(){
10238 Roo.DialogManager.sendToBack(this);
10243 * Centers this dialog in the viewport
10244 * @return {Roo.BasicDialog} this
10246 center : function(){
10247 var xy = this.el.getCenterXY(true);
10248 this.moveTo(xy[0], xy[1]);
10253 * Moves the dialog's top-left corner to the specified point
10254 * @param {Number} x
10255 * @param {Number} y
10256 * @return {Roo.BasicDialog} this
10258 moveTo : function(x, y){
10260 if(this.isVisible()){
10261 this.el.setXY(this.xy);
10262 this.adjustAssets();
10268 * Aligns the dialog to the specified element
10269 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10270 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10271 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10272 * @return {Roo.BasicDialog} this
10274 alignTo : function(element, position, offsets){
10275 this.xy = this.el.getAlignToXY(element, position, offsets);
10276 if(this.isVisible()){
10277 this.el.setXY(this.xy);
10278 this.adjustAssets();
10284 * Anchors an element to another element and realigns it when the window is resized.
10285 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10286 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10287 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10288 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10289 * is a number, it is used as the buffer delay (defaults to 50ms).
10290 * @return {Roo.BasicDialog} this
10292 anchorTo : function(el, alignment, offsets, monitorScroll){
10293 var action = function(){
10294 this.alignTo(el, alignment, offsets);
10296 Roo.EventManager.onWindowResize(action, this);
10297 var tm = typeof monitorScroll;
10298 if(tm != 'undefined'){
10299 Roo.EventManager.on(window, 'scroll', action, this,
10300 {buffer: tm == 'number' ? monitorScroll : 50});
10307 * Returns true if the dialog is visible
10308 * @return {Boolean}
10310 isVisible : function(){
10311 return this.el.isVisible();
10315 animHide : function(callback){
10316 var b = Roo.get(this.animateTarget).getBox();
10318 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10320 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10321 this.hideEl.createDelegate(this, [callback]));
10325 * Hides the dialog.
10326 * @param {Function} callback (optional) Function to call when the dialog is hidden
10327 * @return {Roo.BasicDialog} this
10329 hide : function(callback){
10330 if (this.fireEvent("beforehide", this) === false){
10334 this.shadow.hide();
10339 // sometimes animateTarget seems to get set.. causing problems...
10340 // this just double checks..
10341 if(this.animateTarget && Roo.get(this.animateTarget)) {
10342 this.animHide(callback);
10345 this.hideEl(callback);
10351 hideEl : function(callback){
10355 Roo.get(document.body).removeClass("x-body-masked");
10357 this.fireEvent("hide", this);
10358 if(typeof callback == "function"){
10364 hideAction : function(){
10365 this.setLeft("-10000px");
10366 this.setTop("-10000px");
10367 this.setStyle("visibility", "hidden");
10371 refreshSize : function(){
10372 this.size = this.el.getSize();
10373 this.xy = this.el.getXY();
10374 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10378 // z-index is managed by the DialogManager and may be overwritten at any time
10379 setZIndex : function(index){
10381 this.mask.setStyle("z-index", index);
10384 this.shim.setStyle("z-index", ++index);
10387 this.shadow.setZIndex(++index);
10389 this.el.setStyle("z-index", ++index);
10391 this.proxy.setStyle("z-index", ++index);
10394 this.resizer.proxy.setStyle("z-index", ++index);
10397 this.lastZIndex = index;
10401 * Returns the element for this dialog
10402 * @return {Roo.Element} The underlying dialog Element
10404 getEl : function(){
10410 * @class Roo.DialogManager
10411 * Provides global access to BasicDialogs that have been created and
10412 * support for z-indexing (layering) multiple open dialogs.
10414 Roo.DialogManager = function(){
10416 var accessList = [];
10420 var sortDialogs = function(d1, d2){
10421 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10425 var orderDialogs = function(){
10426 accessList.sort(sortDialogs);
10427 var seed = Roo.DialogManager.zseed;
10428 for(var i = 0, len = accessList.length; i < len; i++){
10429 var dlg = accessList[i];
10431 dlg.setZIndex(seed + (i*10));
10438 * The starting z-index for BasicDialogs (defaults to 9000)
10439 * @type Number The z-index value
10444 register : function(dlg){
10445 list[dlg.id] = dlg;
10446 accessList.push(dlg);
10450 unregister : function(dlg){
10451 delete list[dlg.id];
10454 if(!accessList.indexOf){
10455 for( i = 0, len = accessList.length; i < len; i++){
10456 if(accessList[i] == dlg){
10457 accessList.splice(i, 1);
10462 i = accessList.indexOf(dlg);
10464 accessList.splice(i, 1);
10470 * Gets a registered dialog by id
10471 * @param {String/Object} id The id of the dialog or a dialog
10472 * @return {Roo.BasicDialog} this
10474 get : function(id){
10475 return typeof id == "object" ? id : list[id];
10479 * Brings the specified dialog to the front
10480 * @param {String/Object} dlg The id of the dialog or a dialog
10481 * @return {Roo.BasicDialog} this
10483 bringToFront : function(dlg){
10484 dlg = this.get(dlg);
10487 dlg._lastAccess = new Date().getTime();
10494 * Sends the specified dialog to the back
10495 * @param {String/Object} dlg The id of the dialog or a dialog
10496 * @return {Roo.BasicDialog} this
10498 sendToBack : function(dlg){
10499 dlg = this.get(dlg);
10500 dlg._lastAccess = -(new Date().getTime());
10506 * Hides all dialogs
10508 hideAll : function(){
10509 for(var id in list){
10510 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10519 * @class Roo.LayoutDialog
10520 * @extends Roo.BasicDialog
10521 * Dialog which provides adjustments for working with a layout in a Dialog.
10522 * Add your necessary layout config options to the dialog's config.<br>
10523 * Example usage (including a nested layout):
10526 dialog = new Roo.LayoutDialog("download-dlg", {
10535 // layout config merges with the dialog config
10537 tabPosition: "top",
10538 alwaysShowTabs: true
10541 dialog.addKeyListener(27, dialog.hide, dialog);
10542 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10543 dialog.addButton("Build It!", this.getDownload, this);
10545 // we can even add nested layouts
10546 var innerLayout = new Roo.BorderLayout("dl-inner", {
10556 innerLayout.beginUpdate();
10557 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10558 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10559 innerLayout.endUpdate(true);
10561 var layout = dialog.getLayout();
10562 layout.beginUpdate();
10563 layout.add("center", new Roo.ContentPanel("standard-panel",
10564 {title: "Download the Source", fitToFrame:true}));
10565 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10566 {title: "Build your own roo.js"}));
10567 layout.getRegion("center").showPanel(sp);
10568 layout.endUpdate();
10572 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10573 * @param {Object} config configuration options
10575 Roo.LayoutDialog = function(el, cfg){
10578 if (typeof(cfg) == 'undefined') {
10579 config = Roo.apply({}, el);
10580 // not sure why we use documentElement here.. - it should always be body.
10581 // IE7 borks horribly if we use documentElement.
10582 // webkit also does not like documentElement - it creates a body element...
10583 el = Roo.get( document.body || document.documentElement ).createChild();
10584 //config.autoCreate = true;
10588 config.autoTabs = false;
10589 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10590 this.body.setStyle({overflow:"hidden", position:"relative"});
10591 this.layout = new Roo.BorderLayout(this.body.dom, config);
10592 this.layout.monitorWindowResize = false;
10593 this.el.addClass("x-dlg-auto-layout");
10594 // fix case when center region overwrites center function
10595 this.center = Roo.BasicDialog.prototype.center;
10596 this.on("show", this.layout.layout, this.layout, true);
10597 if (config.items) {
10598 var xitems = config.items;
10599 delete config.items;
10600 Roo.each(xitems, this.addxtype, this);
10605 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10607 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10610 endUpdate : function(){
10611 this.layout.endUpdate();
10615 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10618 beginUpdate : function(){
10619 this.layout.beginUpdate();
10623 * Get the BorderLayout for this dialog
10624 * @return {Roo.BorderLayout}
10626 getLayout : function(){
10627 return this.layout;
10630 showEl : function(){
10631 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10633 this.layout.layout();
10638 // Use the syncHeightBeforeShow config option to control this automatically
10639 syncBodyHeight : function(){
10640 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10641 if(this.layout){this.layout.layout();}
10645 * Add an xtype element (actually adds to the layout.)
10646 * @return {Object} xdata xtype object data.
10649 addxtype : function(c) {
10650 return this.layout.addxtype(c);
10654 * Ext JS Library 1.1.1
10655 * Copyright(c) 2006-2007, Ext JS, LLC.
10657 * Originally Released Under LGPL - original licence link has changed is not relivant.
10660 * <script type="text/javascript">
10664 * @class Roo.MessageBox
10665 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10669 Roo.Msg.alert('Status', 'Changes saved successfully.');
10671 // Prompt for user data:
10672 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10674 // process text value...
10678 // Show a dialog using config options:
10680 title:'Save Changes?',
10681 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10682 buttons: Roo.Msg.YESNOCANCEL,
10689 Roo.MessageBox = function(){
10690 var dlg, opt, mask, waitTimer;
10691 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10692 var buttons, activeTextEl, bwidth;
10695 var handleButton = function(button){
10697 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10701 var handleHide = function(){
10702 if(opt && opt.cls){
10703 dlg.el.removeClass(opt.cls);
10706 Roo.TaskMgr.stop(waitTimer);
10712 var updateButtons = function(b){
10715 buttons["ok"].hide();
10716 buttons["cancel"].hide();
10717 buttons["yes"].hide();
10718 buttons["no"].hide();
10719 dlg.footer.dom.style.display = 'none';
10722 dlg.footer.dom.style.display = '';
10723 for(var k in buttons){
10724 if(typeof buttons[k] != "function"){
10727 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10728 width += buttons[k].el.getWidth()+15;
10738 var handleEsc = function(d, k, e){
10739 if(opt && opt.closable !== false){
10749 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10750 * @return {Roo.BasicDialog} The BasicDialog element
10752 getDialog : function(){
10754 dlg = new Roo.BasicDialog("x-msg-box", {
10759 constraintoviewport:false,
10761 collapsible : false,
10764 width:400, height:100,
10765 buttonAlign:"center",
10766 closeClick : function(){
10767 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10768 handleButton("no");
10770 handleButton("cancel");
10774 dlg.on("hide", handleHide);
10776 dlg.addKeyListener(27, handleEsc);
10778 var bt = this.buttonText;
10779 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10780 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10781 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10782 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10783 bodyEl = dlg.body.createChild({
10785 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>'
10787 msgEl = bodyEl.dom.firstChild;
10788 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10789 textboxEl.enableDisplayMode();
10790 textboxEl.addKeyListener([10,13], function(){
10791 if(dlg.isVisible() && opt && opt.buttons){
10792 if(opt.buttons.ok){
10793 handleButton("ok");
10794 }else if(opt.buttons.yes){
10795 handleButton("yes");
10799 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10800 textareaEl.enableDisplayMode();
10801 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10802 progressEl.enableDisplayMode();
10803 var pf = progressEl.dom.firstChild;
10805 pp = Roo.get(pf.firstChild);
10806 pp.setHeight(pf.offsetHeight);
10814 * Updates the message box body text
10815 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10816 * the XHTML-compliant non-breaking space character '&#160;')
10817 * @return {Roo.MessageBox} This message box
10819 updateText : function(text){
10820 if(!dlg.isVisible() && !opt.width){
10821 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10823 msgEl.innerHTML = text || ' ';
10825 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10826 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10828 Math.min(opt.width || cw , this.maxWidth),
10829 Math.max(opt.minWidth || this.minWidth, bwidth)
10832 activeTextEl.setWidth(w);
10834 if(dlg.isVisible()){
10835 dlg.fixedcenter = false;
10837 // to big, make it scroll. = But as usual stupid IE does not support
10840 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10841 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10842 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10844 bodyEl.dom.style.height = '';
10845 bodyEl.dom.style.overflowY = '';
10848 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10850 bodyEl.dom.style.overflowX = '';
10853 dlg.setContentSize(w, bodyEl.getHeight());
10854 if(dlg.isVisible()){
10855 dlg.fixedcenter = true;
10861 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10862 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10863 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10864 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10865 * @return {Roo.MessageBox} This message box
10867 updateProgress : function(value, text){
10869 this.updateText(text);
10871 if (pp) { // weird bug on my firefox - for some reason this is not defined
10872 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10878 * Returns true if the message box is currently displayed
10879 * @return {Boolean} True if the message box is visible, else false
10881 isVisible : function(){
10882 return dlg && dlg.isVisible();
10886 * Hides the message box if it is displayed
10889 if(this.isVisible()){
10895 * Displays a new message box, or reinitializes an existing message box, based on the config options
10896 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10897 * The following config object properties are supported:
10899 Property Type Description
10900 ---------- --------------- ------------------------------------------------------------------------------------
10901 animEl String/Element An id or Element from which the message box should animate as it opens and
10902 closes (defaults to undefined)
10903 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10904 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10905 closable Boolean False to hide the top-right close button (defaults to true). Note that
10906 progress and wait dialogs will ignore this property and always hide the
10907 close button as they can only be closed programmatically.
10908 cls String A custom CSS class to apply to the message box element
10909 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10910 displayed (defaults to 75)
10911 fn Function A callback function to execute after closing the dialog. The arguments to the
10912 function will be btn (the name of the button that was clicked, if applicable,
10913 e.g. "ok"), and text (the value of the active text field, if applicable).
10914 Progress and wait dialogs will ignore this option since they do not respond to
10915 user actions and can only be closed programmatically, so any required function
10916 should be called by the same code after it closes the dialog.
10917 icon String A CSS class that provides a background image to be used as an icon for
10918 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10919 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10920 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10921 modal Boolean False to allow user interaction with the page while the message box is
10922 displayed (defaults to true)
10923 msg String A string that will replace the existing message box body text (defaults
10924 to the XHTML-compliant non-breaking space character ' ')
10925 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10926 progress Boolean True to display a progress bar (defaults to false)
10927 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10928 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10929 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10930 title String The title text
10931 value String The string value to set into the active textbox element if displayed
10932 wait Boolean True to display a progress bar (defaults to false)
10933 width Number The width of the dialog in pixels
10940 msg: 'Please enter your address:',
10942 buttons: Roo.MessageBox.OKCANCEL,
10945 animEl: 'addAddressBtn'
10948 * @param {Object} config Configuration options
10949 * @return {Roo.MessageBox} This message box
10951 show : function(options)
10954 // this causes nightmares if you show one dialog after another
10955 // especially on callbacks..
10957 if(this.isVisible()){
10960 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10961 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10962 Roo.log("New Dialog Message:" + options.msg )
10963 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10964 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10967 var d = this.getDialog();
10969 d.setTitle(opt.title || " ");
10970 d.close.setDisplayed(opt.closable !== false);
10971 activeTextEl = textboxEl;
10972 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10977 textareaEl.setHeight(typeof opt.multiline == "number" ?
10978 opt.multiline : this.defaultTextHeight);
10979 activeTextEl = textareaEl;
10988 progressEl.setDisplayed(opt.progress === true);
10989 this.updateProgress(0);
10990 activeTextEl.dom.value = opt.value || "";
10992 dlg.setDefaultButton(activeTextEl);
10994 var bs = opt.buttons;
10997 db = buttons["ok"];
10998 }else if(bs && bs.yes){
10999 db = buttons["yes"];
11001 dlg.setDefaultButton(db);
11003 bwidth = updateButtons(opt.buttons);
11004 this.updateText(opt.msg);
11006 d.el.addClass(opt.cls);
11008 d.proxyDrag = opt.proxyDrag === true;
11009 d.modal = opt.modal !== false;
11010 d.mask = opt.modal !== false ? mask : false;
11011 if(!d.isVisible()){
11012 // force it to the end of the z-index stack so it gets a cursor in FF
11013 document.body.appendChild(dlg.el.dom);
11014 d.animateTarget = null;
11015 d.show(options.animEl);
11021 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
11022 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11023 * and closing the message box when the process is complete.
11024 * @param {String} title The title bar text
11025 * @param {String} msg The message box body text
11026 * @return {Roo.MessageBox} This message box
11028 progress : function(title, msg){
11035 minWidth: this.minProgressWidth,
11042 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11043 * If a callback function is passed it will be called after the user clicks the button, and the
11044 * id of the button that was clicked will be passed as the only parameter to the callback
11045 * (could also be the top-right close button).
11046 * @param {String} title The title bar text
11047 * @param {String} msg The message box body text
11048 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11049 * @param {Object} scope (optional) The scope of the callback function
11050 * @return {Roo.MessageBox} This message box
11052 alert : function(title, msg, fn, scope){
11065 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
11066 * interaction while waiting for a long-running process to complete that does not have defined intervals.
11067 * You are responsible for closing the message box when the process is complete.
11068 * @param {String} msg The message box body text
11069 * @param {String} title (optional) The title bar text
11070 * @return {Roo.MessageBox} This message box
11072 wait : function(msg, title){
11083 waitTimer = Roo.TaskMgr.start({
11085 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11093 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11094 * If a callback function is passed it will be called after the user clicks either button, and the id of the
11095 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11096 * @param {String} title The title bar text
11097 * @param {String} msg The message box body text
11098 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11099 * @param {Object} scope (optional) The scope of the callback function
11100 * @return {Roo.MessageBox} This message box
11102 confirm : function(title, msg, fn, scope){
11106 buttons: this.YESNO,
11115 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11116 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
11117 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11118 * (could also be the top-right close button) and the text that was entered will be passed as the two
11119 * parameters to the callback.
11120 * @param {String} title The title bar text
11121 * @param {String} msg The message box body text
11122 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11123 * @param {Object} scope (optional) The scope of the callback function
11124 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11125 * property, or the height in pixels to create the textbox (defaults to false / single-line)
11126 * @return {Roo.MessageBox} This message box
11128 prompt : function(title, msg, fn, scope, multiline){
11132 buttons: this.OKCANCEL,
11137 multiline: multiline,
11144 * Button config that displays a single OK button
11149 * Button config that displays Yes and No buttons
11152 YESNO : {yes:true, no:true},
11154 * Button config that displays OK and Cancel buttons
11157 OKCANCEL : {ok:true, cancel:true},
11159 * Button config that displays Yes, No and Cancel buttons
11162 YESNOCANCEL : {yes:true, no:true, cancel:true},
11165 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11168 defaultTextHeight : 75,
11170 * The maximum width in pixels of the message box (defaults to 600)
11175 * The minimum width in pixels of the message box (defaults to 100)
11180 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
11181 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11184 minProgressWidth : 250,
11186 * An object containing the default button text strings that can be overriden for localized language support.
11187 * Supported properties are: ok, cancel, yes and no.
11188 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11201 * Shorthand for {@link Roo.MessageBox}
11203 Roo.Msg = Roo.MessageBox;/*
11205 * Ext JS Library 1.1.1
11206 * Copyright(c) 2006-2007, Ext JS, LLC.
11208 * Originally Released Under LGPL - original licence link has changed is not relivant.
11211 * <script type="text/javascript">
11214 * @class Roo.QuickTips
11215 * Provides attractive and customizable tooltips for any element.
11218 Roo.QuickTips = function(){
11219 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11220 var ce, bd, xy, dd;
11221 var visible = false, disabled = true, inited = false;
11222 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11224 var onOver = function(e){
11228 var t = e.getTarget();
11229 if(!t || t.nodeType !== 1 || t == document || t == document.body){
11232 if(ce && t == ce.el){
11233 clearTimeout(hideProc);
11236 if(t && tagEls[t.id]){
11237 tagEls[t.id].el = t;
11238 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11241 var ttp, et = Roo.fly(t);
11242 var ns = cfg.namespace;
11243 if(tm.interceptTitles && t.title){
11246 t.removeAttribute("title");
11247 e.preventDefault();
11249 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11252 showProc = show.defer(tm.showDelay, tm, [{
11254 text: ttp.replace(/\\n/g,'<br/>'),
11255 width: et.getAttributeNS(ns, cfg.width),
11256 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11257 title: et.getAttributeNS(ns, cfg.title),
11258 cls: et.getAttributeNS(ns, cfg.cls)
11263 var onOut = function(e){
11264 clearTimeout(showProc);
11265 var t = e.getTarget();
11266 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11267 hideProc = setTimeout(hide, tm.hideDelay);
11271 var onMove = function(e){
11277 if(tm.trackMouse && ce){
11282 var onDown = function(e){
11283 clearTimeout(showProc);
11284 clearTimeout(hideProc);
11286 if(tm.hideOnClick){
11289 tm.enable.defer(100, tm);
11294 var getPad = function(){
11295 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11298 var show = function(o){
11302 clearTimeout(dismissProc);
11304 if(removeCls){ // in case manually hidden
11305 el.removeClass(removeCls);
11309 el.addClass(ce.cls);
11310 removeCls = ce.cls;
11313 tipTitle.update(ce.title);
11316 tipTitle.update('');
11319 el.dom.style.width = tm.maxWidth+'px';
11320 //tipBody.dom.style.width = '';
11321 tipBodyText.update(o.text);
11322 var p = getPad(), w = ce.width;
11324 var td = tipBodyText.dom;
11325 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11326 if(aw > tm.maxWidth){
11328 }else if(aw < tm.minWidth){
11334 //tipBody.setWidth(w);
11335 el.setWidth(parseInt(w, 10) + p);
11336 if(ce.autoHide === false){
11337 close.setDisplayed(true);
11342 close.setDisplayed(false);
11348 el.avoidY = xy[1]-18;
11353 el.setStyle("visibility", "visible");
11354 el.fadeIn({callback: afterShow});
11360 var afterShow = function(){
11364 if(tm.autoDismiss && ce.autoHide !== false){
11365 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11370 var hide = function(noanim){
11371 clearTimeout(dismissProc);
11372 clearTimeout(hideProc);
11374 if(el.isVisible()){
11376 if(noanim !== true && tm.animate){
11377 el.fadeOut({callback: afterHide});
11384 var afterHide = function(){
11387 el.removeClass(removeCls);
11394 * @cfg {Number} minWidth
11395 * The minimum width of the quick tip (defaults to 40)
11399 * @cfg {Number} maxWidth
11400 * The maximum width of the quick tip (defaults to 300)
11404 * @cfg {Boolean} interceptTitles
11405 * True to automatically use the element's DOM title value if available (defaults to false)
11407 interceptTitles : false,
11409 * @cfg {Boolean} trackMouse
11410 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11412 trackMouse : false,
11414 * @cfg {Boolean} hideOnClick
11415 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11417 hideOnClick : true,
11419 * @cfg {Number} showDelay
11420 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11424 * @cfg {Number} hideDelay
11425 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11429 * @cfg {Boolean} autoHide
11430 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11431 * Used in conjunction with hideDelay.
11436 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11437 * (defaults to true). Used in conjunction with autoDismissDelay.
11439 autoDismiss : true,
11442 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11444 autoDismissDelay : 5000,
11446 * @cfg {Boolean} animate
11447 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11452 * @cfg {String} title
11453 * Title text to display (defaults to ''). This can be any valid HTML markup.
11457 * @cfg {String} text
11458 * Body text to display (defaults to ''). This can be any valid HTML markup.
11462 * @cfg {String} cls
11463 * A CSS class to apply to the base quick tip element (defaults to '').
11467 * @cfg {Number} width
11468 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11469 * minWidth or maxWidth.
11474 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11475 * or display QuickTips in a page.
11478 tm = Roo.QuickTips;
11479 cfg = tm.tagConfig;
11481 if(!Roo.isReady){ // allow calling of init() before onReady
11482 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11485 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11486 el.fxDefaults = {stopFx: true};
11487 // maximum custom styling
11488 //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>');
11489 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>');
11490 tipTitle = el.child('h3');
11491 tipTitle.enableDisplayMode("block");
11492 tipBody = el.child('div.x-tip-bd');
11493 tipBodyText = el.child('div.x-tip-bd-inner');
11494 //bdLeft = el.child('div.x-tip-bd-left');
11495 //bdRight = el.child('div.x-tip-bd-right');
11496 close = el.child('div.x-tip-close');
11497 close.enableDisplayMode("block");
11498 close.on("click", hide);
11499 var d = Roo.get(document);
11500 d.on("mousedown", onDown);
11501 d.on("mouseover", onOver);
11502 d.on("mouseout", onOut);
11503 d.on("mousemove", onMove);
11504 esc = d.addKeyListener(27, hide);
11507 dd = el.initDD("default", null, {
11508 onDrag : function(){
11512 dd.setHandleElId(tipTitle.id);
11521 * Configures a new quick tip instance and assigns it to a target element. The following config options
11524 Property Type Description
11525 ---------- --------------------- ------------------------------------------------------------------------
11526 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11528 * @param {Object} config The config object
11530 register : function(config){
11531 var cs = config instanceof Array ? config : arguments;
11532 for(var i = 0, len = cs.length; i < len; i++) {
11534 var target = c.target;
11536 if(target instanceof Array){
11537 for(var j = 0, jlen = target.length; j < jlen; j++){
11538 tagEls[target[j]] = c;
11541 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11548 * Removes this quick tip from its element and destroys it.
11549 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11551 unregister : function(el){
11552 delete tagEls[Roo.id(el)];
11556 * Enable this quick tip.
11558 enable : function(){
11559 if(inited && disabled){
11561 if(locks.length < 1){
11568 * Disable this quick tip.
11570 disable : function(){
11572 clearTimeout(showProc);
11573 clearTimeout(hideProc);
11574 clearTimeout(dismissProc);
11582 * Returns true if the quick tip is enabled, else false.
11584 isEnabled : function(){
11590 namespace : "roo", // was ext?? this may break..
11591 alt_namespace : "ext",
11592 attribute : "qtip",
11602 // backwards compat
11603 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11605 * Ext JS Library 1.1.1
11606 * Copyright(c) 2006-2007, Ext JS, LLC.
11608 * Originally Released Under LGPL - original licence link has changed is not relivant.
11611 * <script type="text/javascript">
11616 * @class Roo.tree.TreePanel
11617 * @extends Roo.data.Tree
11619 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11620 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11621 * @cfg {Boolean} enableDD true to enable drag and drop
11622 * @cfg {Boolean} enableDrag true to enable just drag
11623 * @cfg {Boolean} enableDrop true to enable just drop
11624 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11625 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11626 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11627 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11628 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11629 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11630 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11631 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11632 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11633 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11634 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11635 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11636 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11637 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11638 * @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>
11639 * @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>
11642 * @param {String/HTMLElement/Element} el The container element
11643 * @param {Object} config
11645 Roo.tree.TreePanel = function(el, config){
11647 var loader = false;
11649 root = config.root;
11650 delete config.root;
11652 if (config.loader) {
11653 loader = config.loader;
11654 delete config.loader;
11657 Roo.apply(this, config);
11658 Roo.tree.TreePanel.superclass.constructor.call(this);
11659 this.el = Roo.get(el);
11660 this.el.addClass('x-tree');
11661 //console.log(root);
11663 this.setRootNode( Roo.factory(root, Roo.tree));
11666 this.loader = Roo.factory(loader, Roo.tree);
11669 * Read-only. The id of the container element becomes this TreePanel's id.
11671 this.id = this.el.id;
11674 * @event beforeload
11675 * Fires before a node is loaded, return false to cancel
11676 * @param {Node} node The node being loaded
11678 "beforeload" : true,
11681 * Fires when a node is loaded
11682 * @param {Node} node The node that was loaded
11686 * @event textchange
11687 * Fires when the text for a node is changed
11688 * @param {Node} node The node
11689 * @param {String} text The new text
11690 * @param {String} oldText The old text
11692 "textchange" : true,
11694 * @event beforeexpand
11695 * Fires before a node is expanded, return false to cancel.
11696 * @param {Node} node The node
11697 * @param {Boolean} deep
11698 * @param {Boolean} anim
11700 "beforeexpand" : true,
11702 * @event beforecollapse
11703 * Fires before a node is collapsed, return false to cancel.
11704 * @param {Node} node The node
11705 * @param {Boolean} deep
11706 * @param {Boolean} anim
11708 "beforecollapse" : true,
11711 * Fires when a node is expanded
11712 * @param {Node} node The node
11716 * @event disabledchange
11717 * Fires when the disabled status of a node changes
11718 * @param {Node} node The node
11719 * @param {Boolean} disabled
11721 "disabledchange" : true,
11724 * Fires when a node is collapsed
11725 * @param {Node} node The node
11729 * @event beforeclick
11730 * Fires before click processing on a node. Return false to cancel the default action.
11731 * @param {Node} node The node
11732 * @param {Roo.EventObject} e The event object
11734 "beforeclick":true,
11736 * @event checkchange
11737 * Fires when a node with a checkbox's checked property changes
11738 * @param {Node} this This node
11739 * @param {Boolean} checked
11741 "checkchange":true,
11744 * Fires when a node is clicked
11745 * @param {Node} node The node
11746 * @param {Roo.EventObject} e The event object
11751 * Fires when a node is double clicked
11752 * @param {Node} node The node
11753 * @param {Roo.EventObject} e The event object
11757 * @event contextmenu
11758 * Fires when a node is right clicked
11759 * @param {Node} node The node
11760 * @param {Roo.EventObject} e The event object
11762 "contextmenu":true,
11764 * @event beforechildrenrendered
11765 * Fires right before the child nodes for a node are rendered
11766 * @param {Node} node The node
11768 "beforechildrenrendered":true,
11771 * Fires when a node starts being dragged
11772 * @param {Roo.tree.TreePanel} this
11773 * @param {Roo.tree.TreeNode} node
11774 * @param {event} e The raw browser event
11776 "startdrag" : true,
11779 * Fires when a drag operation is complete
11780 * @param {Roo.tree.TreePanel} this
11781 * @param {Roo.tree.TreeNode} node
11782 * @param {event} e The raw browser event
11787 * Fires when a dragged node is dropped on a valid DD target
11788 * @param {Roo.tree.TreePanel} this
11789 * @param {Roo.tree.TreeNode} node
11790 * @param {DD} dd The dd it was dropped on
11791 * @param {event} e The raw browser event
11795 * @event beforenodedrop
11796 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11797 * passed to handlers has the following properties:<br />
11798 * <ul style="padding:5px;padding-left:16px;">
11799 * <li>tree - The TreePanel</li>
11800 * <li>target - The node being targeted for the drop</li>
11801 * <li>data - The drag data from the drag source</li>
11802 * <li>point - The point of the drop - append, above or below</li>
11803 * <li>source - The drag source</li>
11804 * <li>rawEvent - Raw mouse event</li>
11805 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11806 * to be inserted by setting them on this object.</li>
11807 * <li>cancel - Set this to true to cancel the drop.</li>
11809 * @param {Object} dropEvent
11811 "beforenodedrop" : true,
11814 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11815 * passed to handlers has the following properties:<br />
11816 * <ul style="padding:5px;padding-left:16px;">
11817 * <li>tree - The TreePanel</li>
11818 * <li>target - The node being targeted for the drop</li>
11819 * <li>data - The drag data from the drag source</li>
11820 * <li>point - The point of the drop - append, above or below</li>
11821 * <li>source - The drag source</li>
11822 * <li>rawEvent - Raw mouse event</li>
11823 * <li>dropNode - Dropped node(s).</li>
11825 * @param {Object} dropEvent
11829 * @event nodedragover
11830 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11831 * passed to handlers has the following properties:<br />
11832 * <ul style="padding:5px;padding-left:16px;">
11833 * <li>tree - The TreePanel</li>
11834 * <li>target - The node being targeted for the drop</li>
11835 * <li>data - The drag data from the drag source</li>
11836 * <li>point - The point of the drop - append, above or below</li>
11837 * <li>source - The drag source</li>
11838 * <li>rawEvent - Raw mouse event</li>
11839 * <li>dropNode - Drop node(s) provided by the source.</li>
11840 * <li>cancel - Set this to true to signal drop not allowed.</li>
11842 * @param {Object} dragOverEvent
11844 "nodedragover" : true,
11846 * @event appendnode
11847 * Fires when append node to the tree
11848 * @param {Roo.tree.TreePanel} this
11849 * @param {Roo.tree.TreeNode} node
11850 * @param {Number} index The index of the newly appended node
11852 "appendnode" : true
11855 if(this.singleExpand){
11856 this.on("beforeexpand", this.restrictExpand, this);
11859 this.editor.tree = this;
11860 this.editor = Roo.factory(this.editor, Roo.tree);
11863 if (this.selModel) {
11864 this.selModel = Roo.factory(this.selModel, Roo.tree);
11868 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11869 rootVisible : true,
11870 animate: Roo.enableFx,
11873 hlDrop : Roo.enableFx,
11877 rendererTip: false,
11879 restrictExpand : function(node){
11880 var p = node.parentNode;
11882 if(p.expandedChild && p.expandedChild.parentNode == p){
11883 p.expandedChild.collapse();
11885 p.expandedChild = node;
11889 // private override
11890 setRootNode : function(node){
11891 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11892 if(!this.rootVisible){
11893 node.ui = new Roo.tree.RootTreeNodeUI(node);
11899 * Returns the container element for this TreePanel
11901 getEl : function(){
11906 * Returns the default TreeLoader for this TreePanel
11908 getLoader : function(){
11909 return this.loader;
11915 expandAll : function(){
11916 this.root.expand(true);
11920 * Collapse all nodes
11922 collapseAll : function(){
11923 this.root.collapse(true);
11927 * Returns the selection model used by this TreePanel
11929 getSelectionModel : function(){
11930 if(!this.selModel){
11931 this.selModel = new Roo.tree.DefaultSelectionModel();
11933 return this.selModel;
11937 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11938 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11939 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11942 getChecked : function(a, startNode){
11943 startNode = startNode || this.root;
11945 var f = function(){
11946 if(this.attributes.checked){
11947 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11950 startNode.cascade(f);
11955 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11956 * @param {String} path
11957 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11958 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11959 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11961 expandPath : function(path, attr, callback){
11962 attr = attr || "id";
11963 var keys = path.split(this.pathSeparator);
11964 var curNode = this.root;
11965 if(curNode.attributes[attr] != keys[1]){ // invalid root
11967 callback(false, null);
11972 var f = function(){
11973 if(++index == keys.length){
11975 callback(true, curNode);
11979 var c = curNode.findChild(attr, keys[index]);
11982 callback(false, curNode);
11987 c.expand(false, false, f);
11989 curNode.expand(false, false, f);
11993 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11994 * @param {String} path
11995 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11996 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11997 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11999 selectPath : function(path, attr, callback){
12000 attr = attr || "id";
12001 var keys = path.split(this.pathSeparator);
12002 var v = keys.pop();
12003 if(keys.length > 0){
12004 var f = function(success, node){
12005 if(success && node){
12006 var n = node.findChild(attr, v);
12012 }else if(callback){
12013 callback(false, n);
12017 callback(false, n);
12021 this.expandPath(keys.join(this.pathSeparator), attr, f);
12023 this.root.select();
12025 callback(true, this.root);
12030 getTreeEl : function(){
12035 * Trigger rendering of this TreePanel
12037 render : function(){
12038 if (this.innerCt) {
12039 return this; // stop it rendering more than once!!
12042 this.innerCt = this.el.createChild({tag:"ul",
12043 cls:"x-tree-root-ct " +
12044 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12046 if(this.containerScroll){
12047 Roo.dd.ScrollManager.register(this.el);
12049 if((this.enableDD || this.enableDrop) && !this.dropZone){
12051 * The dropZone used by this tree if drop is enabled
12052 * @type Roo.tree.TreeDropZone
12054 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12055 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12058 if((this.enableDD || this.enableDrag) && !this.dragZone){
12060 * The dragZone used by this tree if drag is enabled
12061 * @type Roo.tree.TreeDragZone
12063 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12064 ddGroup: this.ddGroup || "TreeDD",
12065 scroll: this.ddScroll
12068 this.getSelectionModel().init(this);
12070 Roo.log("ROOT not set in tree");
12073 this.root.render();
12074 if(!this.rootVisible){
12075 this.root.renderChildren();
12081 * Ext JS Library 1.1.1
12082 * Copyright(c) 2006-2007, Ext JS, LLC.
12084 * Originally Released Under LGPL - original licence link has changed is not relivant.
12087 * <script type="text/javascript">
12092 * @class Roo.tree.DefaultSelectionModel
12093 * @extends Roo.util.Observable
12094 * The default single selection for a TreePanel.
12095 * @param {Object} cfg Configuration
12097 Roo.tree.DefaultSelectionModel = function(cfg){
12098 this.selNode = null;
12104 * @event selectionchange
12105 * Fires when the selected node changes
12106 * @param {DefaultSelectionModel} this
12107 * @param {TreeNode} node the new selection
12109 "selectionchange" : true,
12112 * @event beforeselect
12113 * Fires before the selected node changes, return false to cancel the change
12114 * @param {DefaultSelectionModel} this
12115 * @param {TreeNode} node the new selection
12116 * @param {TreeNode} node the old selection
12118 "beforeselect" : true
12121 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12124 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12125 init : function(tree){
12127 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12128 tree.on("click", this.onNodeClick, this);
12131 onNodeClick : function(node, e){
12132 if (e.ctrlKey && this.selNode == node) {
12133 this.unselect(node);
12141 * @param {TreeNode} node The node to select
12142 * @return {TreeNode} The selected node
12144 select : function(node){
12145 var last = this.selNode;
12146 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12148 last.ui.onSelectedChange(false);
12150 this.selNode = node;
12151 node.ui.onSelectedChange(true);
12152 this.fireEvent("selectionchange", this, node, last);
12159 * @param {TreeNode} node The node to unselect
12161 unselect : function(node){
12162 if(this.selNode == node){
12163 this.clearSelections();
12168 * Clear all selections
12170 clearSelections : function(){
12171 var n = this.selNode;
12173 n.ui.onSelectedChange(false);
12174 this.selNode = null;
12175 this.fireEvent("selectionchange", this, null);
12181 * Get the selected node
12182 * @return {TreeNode} The selected node
12184 getSelectedNode : function(){
12185 return this.selNode;
12189 * Returns true if the node is selected
12190 * @param {TreeNode} node The node to check
12191 * @return {Boolean}
12193 isSelected : function(node){
12194 return this.selNode == node;
12198 * Selects the node above the selected node in the tree, intelligently walking the nodes
12199 * @return TreeNode The new selection
12201 selectPrevious : function(){
12202 var s = this.selNode || this.lastSelNode;
12206 var ps = s.previousSibling;
12208 if(!ps.isExpanded() || ps.childNodes.length < 1){
12209 return this.select(ps);
12211 var lc = ps.lastChild;
12212 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12215 return this.select(lc);
12217 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12218 return this.select(s.parentNode);
12224 * Selects the node above the selected node in the tree, intelligently walking the nodes
12225 * @return TreeNode The new selection
12227 selectNext : function(){
12228 var s = this.selNode || this.lastSelNode;
12232 if(s.firstChild && s.isExpanded()){
12233 return this.select(s.firstChild);
12234 }else if(s.nextSibling){
12235 return this.select(s.nextSibling);
12236 }else if(s.parentNode){
12238 s.parentNode.bubble(function(){
12239 if(this.nextSibling){
12240 newS = this.getOwnerTree().selModel.select(this.nextSibling);
12249 onKeyDown : function(e){
12250 var s = this.selNode || this.lastSelNode;
12251 // undesirable, but required
12256 var k = e.getKey();
12264 this.selectPrevious();
12267 e.preventDefault();
12268 if(s.hasChildNodes()){
12269 if(!s.isExpanded()){
12271 }else if(s.firstChild){
12272 this.select(s.firstChild, e);
12277 e.preventDefault();
12278 if(s.hasChildNodes() && s.isExpanded()){
12280 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12281 this.select(s.parentNode, e);
12289 * @class Roo.tree.MultiSelectionModel
12290 * @extends Roo.util.Observable
12291 * Multi selection for a TreePanel.
12292 * @param {Object} cfg Configuration
12294 Roo.tree.MultiSelectionModel = function(){
12295 this.selNodes = [];
12299 * @event selectionchange
12300 * Fires when the selected nodes change
12301 * @param {MultiSelectionModel} this
12302 * @param {Array} nodes Array of the selected nodes
12304 "selectionchange" : true
12306 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12310 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12311 init : function(tree){
12313 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12314 tree.on("click", this.onNodeClick, this);
12317 onNodeClick : function(node, e){
12318 this.select(node, e, e.ctrlKey);
12323 * @param {TreeNode} node The node to select
12324 * @param {EventObject} e (optional) An event associated with the selection
12325 * @param {Boolean} keepExisting True to retain existing selections
12326 * @return {TreeNode} The selected node
12328 select : function(node, e, keepExisting){
12329 if(keepExisting !== true){
12330 this.clearSelections(true);
12332 if(this.isSelected(node)){
12333 this.lastSelNode = node;
12336 this.selNodes.push(node);
12337 this.selMap[node.id] = node;
12338 this.lastSelNode = node;
12339 node.ui.onSelectedChange(true);
12340 this.fireEvent("selectionchange", this, this.selNodes);
12346 * @param {TreeNode} node The node to unselect
12348 unselect : function(node){
12349 if(this.selMap[node.id]){
12350 node.ui.onSelectedChange(false);
12351 var sn = this.selNodes;
12354 index = sn.indexOf(node);
12356 for(var i = 0, len = sn.length; i < len; i++){
12364 this.selNodes.splice(index, 1);
12366 delete this.selMap[node.id];
12367 this.fireEvent("selectionchange", this, this.selNodes);
12372 * Clear all selections
12374 clearSelections : function(suppressEvent){
12375 var sn = this.selNodes;
12377 for(var i = 0, len = sn.length; i < len; i++){
12378 sn[i].ui.onSelectedChange(false);
12380 this.selNodes = [];
12382 if(suppressEvent !== true){
12383 this.fireEvent("selectionchange", this, this.selNodes);
12389 * Returns true if the node is selected
12390 * @param {TreeNode} node The node to check
12391 * @return {Boolean}
12393 isSelected : function(node){
12394 return this.selMap[node.id] ? true : false;
12398 * Returns an array of the selected nodes
12401 getSelectedNodes : function(){
12402 return this.selNodes;
12405 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12407 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12409 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12412 * Ext JS Library 1.1.1
12413 * Copyright(c) 2006-2007, Ext JS, LLC.
12415 * Originally Released Under LGPL - original licence link has changed is not relivant.
12418 * <script type="text/javascript">
12422 * @class Roo.tree.TreeNode
12423 * @extends Roo.data.Node
12424 * @cfg {String} text The text for this node
12425 * @cfg {Boolean} expanded true to start the node expanded
12426 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12427 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12428 * @cfg {Boolean} disabled true to start the node disabled
12429 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12430 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12431 * @cfg {String} cls A css class to be added to the node
12432 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12433 * @cfg {String} href URL of the link used for the node (defaults to #)
12434 * @cfg {String} hrefTarget target frame for the link
12435 * @cfg {String} qtip An Ext QuickTip for the node
12436 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12437 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12438 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12439 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12440 * (defaults to undefined with no checkbox rendered)
12442 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12444 Roo.tree.TreeNode = function(attributes){
12445 attributes = attributes || {};
12446 if(typeof attributes == "string"){
12447 attributes = {text: attributes};
12449 this.childrenRendered = false;
12450 this.rendered = false;
12451 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12452 this.expanded = attributes.expanded === true;
12453 this.isTarget = attributes.isTarget !== false;
12454 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12455 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12458 * Read-only. The text for this node. To change it use setText().
12461 this.text = attributes.text;
12463 * True if this node is disabled.
12466 this.disabled = attributes.disabled === true;
12470 * @event textchange
12471 * Fires when the text for this node is changed
12472 * @param {Node} this This node
12473 * @param {String} text The new text
12474 * @param {String} oldText The old text
12476 "textchange" : true,
12478 * @event beforeexpand
12479 * Fires before this node is expanded, return false to cancel.
12480 * @param {Node} this This node
12481 * @param {Boolean} deep
12482 * @param {Boolean} anim
12484 "beforeexpand" : true,
12486 * @event beforecollapse
12487 * Fires before this node is collapsed, return false to cancel.
12488 * @param {Node} this This node
12489 * @param {Boolean} deep
12490 * @param {Boolean} anim
12492 "beforecollapse" : true,
12495 * Fires when this node is expanded
12496 * @param {Node} this This node
12500 * @event disabledchange
12501 * Fires when the disabled status of this node changes
12502 * @param {Node} this This node
12503 * @param {Boolean} disabled
12505 "disabledchange" : true,
12508 * Fires when this node is collapsed
12509 * @param {Node} this This node
12513 * @event beforeclick
12514 * Fires before click processing. Return false to cancel the default action.
12515 * @param {Node} this This node
12516 * @param {Roo.EventObject} e The event object
12518 "beforeclick":true,
12520 * @event checkchange
12521 * Fires when a node with a checkbox's checked property changes
12522 * @param {Node} this This node
12523 * @param {Boolean} checked
12525 "checkchange":true,
12528 * Fires when this node is clicked
12529 * @param {Node} this This node
12530 * @param {Roo.EventObject} e The event object
12535 * Fires when this node is double clicked
12536 * @param {Node} this This node
12537 * @param {Roo.EventObject} e The event object
12541 * @event contextmenu
12542 * Fires when this node is right clicked
12543 * @param {Node} this This node
12544 * @param {Roo.EventObject} e The event object
12546 "contextmenu":true,
12548 * @event beforechildrenrendered
12549 * Fires right before the child nodes for this node are rendered
12550 * @param {Node} this This node
12552 "beforechildrenrendered":true
12555 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12558 * Read-only. The UI for this node
12561 this.ui = new uiClass(this);
12563 // finally support items[]
12564 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12569 Roo.each(this.attributes.items, function(c) {
12570 this.appendChild(Roo.factory(c,Roo.Tree));
12572 delete this.attributes.items;
12577 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12578 preventHScroll: true,
12580 * Returns true if this node is expanded
12581 * @return {Boolean}
12583 isExpanded : function(){
12584 return this.expanded;
12588 * Returns the UI object for this node
12589 * @return {TreeNodeUI}
12591 getUI : function(){
12595 // private override
12596 setFirstChild : function(node){
12597 var of = this.firstChild;
12598 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12599 if(this.childrenRendered && of && node != of){
12600 of.renderIndent(true, true);
12603 this.renderIndent(true, true);
12607 // private override
12608 setLastChild : function(node){
12609 var ol = this.lastChild;
12610 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12611 if(this.childrenRendered && ol && node != ol){
12612 ol.renderIndent(true, true);
12615 this.renderIndent(true, true);
12619 // these methods are overridden to provide lazy rendering support
12620 // private override
12621 appendChild : function()
12623 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12624 if(node && this.childrenRendered){
12627 this.ui.updateExpandIcon();
12631 // private override
12632 removeChild : function(node){
12633 this.ownerTree.getSelectionModel().unselect(node);
12634 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12635 // if it's been rendered remove dom node
12636 if(this.childrenRendered){
12639 if(this.childNodes.length < 1){
12640 this.collapse(false, false);
12642 this.ui.updateExpandIcon();
12644 if(!this.firstChild) {
12645 this.childrenRendered = false;
12650 // private override
12651 insertBefore : function(node, refNode){
12652 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12653 if(newNode && refNode && this.childrenRendered){
12656 this.ui.updateExpandIcon();
12661 * Sets the text for this node
12662 * @param {String} text
12664 setText : function(text){
12665 var oldText = this.text;
12667 this.attributes.text = text;
12668 if(this.rendered){ // event without subscribing
12669 this.ui.onTextChange(this, text, oldText);
12671 this.fireEvent("textchange", this, text, oldText);
12675 * Triggers selection of this node
12677 select : function(){
12678 this.getOwnerTree().getSelectionModel().select(this);
12682 * Triggers deselection of this node
12684 unselect : function(){
12685 this.getOwnerTree().getSelectionModel().unselect(this);
12689 * Returns true if this node is selected
12690 * @return {Boolean}
12692 isSelected : function(){
12693 return this.getOwnerTree().getSelectionModel().isSelected(this);
12697 * Expand this node.
12698 * @param {Boolean} deep (optional) True to expand all children as well
12699 * @param {Boolean} anim (optional) false to cancel the default animation
12700 * @param {Function} callback (optional) A callback to be called when
12701 * expanding this node completes (does not wait for deep expand to complete).
12702 * Called with 1 parameter, this node.
12704 expand : function(deep, anim, callback){
12705 if(!this.expanded){
12706 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12709 if(!this.childrenRendered){
12710 this.renderChildren();
12712 this.expanded = true;
12714 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12715 this.ui.animExpand(function(){
12716 this.fireEvent("expand", this);
12717 if(typeof callback == "function"){
12721 this.expandChildNodes(true);
12723 }.createDelegate(this));
12727 this.fireEvent("expand", this);
12728 if(typeof callback == "function"){
12733 if(typeof callback == "function"){
12738 this.expandChildNodes(true);
12742 isHiddenRoot : function(){
12743 return this.isRoot && !this.getOwnerTree().rootVisible;
12747 * Collapse this node.
12748 * @param {Boolean} deep (optional) True to collapse all children as well
12749 * @param {Boolean} anim (optional) false to cancel the default animation
12751 collapse : function(deep, anim){
12752 if(this.expanded && !this.isHiddenRoot()){
12753 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12756 this.expanded = false;
12757 if((this.getOwnerTree().animate && anim !== false) || anim){
12758 this.ui.animCollapse(function(){
12759 this.fireEvent("collapse", this);
12761 this.collapseChildNodes(true);
12763 }.createDelegate(this));
12766 this.ui.collapse();
12767 this.fireEvent("collapse", this);
12771 var cs = this.childNodes;
12772 for(var i = 0, len = cs.length; i < len; i++) {
12773 cs[i].collapse(true, false);
12779 delayedExpand : function(delay){
12780 if(!this.expandProcId){
12781 this.expandProcId = this.expand.defer(delay, this);
12786 cancelExpand : function(){
12787 if(this.expandProcId){
12788 clearTimeout(this.expandProcId);
12790 this.expandProcId = false;
12794 * Toggles expanded/collapsed state of the node
12796 toggle : function(){
12805 * Ensures all parent nodes are expanded
12807 ensureVisible : function(callback){
12808 var tree = this.getOwnerTree();
12809 tree.expandPath(this.parentNode.getPath(), false, function(){
12810 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12811 Roo.callback(callback);
12812 }.createDelegate(this));
12816 * Expand all child nodes
12817 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12819 expandChildNodes : function(deep){
12820 var cs = this.childNodes;
12821 for(var i = 0, len = cs.length; i < len; i++) {
12822 cs[i].expand(deep);
12827 * Collapse all child nodes
12828 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12830 collapseChildNodes : function(deep){
12831 var cs = this.childNodes;
12832 for(var i = 0, len = cs.length; i < len; i++) {
12833 cs[i].collapse(deep);
12838 * Disables this node
12840 disable : function(){
12841 this.disabled = true;
12843 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12844 this.ui.onDisableChange(this, true);
12846 this.fireEvent("disabledchange", this, true);
12850 * Enables this node
12852 enable : function(){
12853 this.disabled = false;
12854 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12855 this.ui.onDisableChange(this, false);
12857 this.fireEvent("disabledchange", this, false);
12861 renderChildren : function(suppressEvent){
12862 if(suppressEvent !== false){
12863 this.fireEvent("beforechildrenrendered", this);
12865 var cs = this.childNodes;
12866 for(var i = 0, len = cs.length; i < len; i++){
12867 cs[i].render(true);
12869 this.childrenRendered = true;
12873 sort : function(fn, scope){
12874 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12875 if(this.childrenRendered){
12876 var cs = this.childNodes;
12877 for(var i = 0, len = cs.length; i < len; i++){
12878 cs[i].render(true);
12884 render : function(bulkRender){
12885 this.ui.render(bulkRender);
12886 if(!this.rendered){
12887 this.rendered = true;
12889 this.expanded = false;
12890 this.expand(false, false);
12896 renderIndent : function(deep, refresh){
12898 this.ui.childIndent = null;
12900 this.ui.renderIndent();
12901 if(deep === true && this.childrenRendered){
12902 var cs = this.childNodes;
12903 for(var i = 0, len = cs.length; i < len; i++){
12904 cs[i].renderIndent(true, refresh);
12910 * Ext JS Library 1.1.1
12911 * Copyright(c) 2006-2007, Ext JS, LLC.
12913 * Originally Released Under LGPL - original licence link has changed is not relivant.
12916 * <script type="text/javascript">
12920 * @class Roo.tree.AsyncTreeNode
12921 * @extends Roo.tree.TreeNode
12922 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12924 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12926 Roo.tree.AsyncTreeNode = function(config){
12927 this.loaded = false;
12928 this.loading = false;
12929 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12931 * @event beforeload
12932 * Fires before this node is loaded, return false to cancel
12933 * @param {Node} this This node
12935 this.addEvents({'beforeload':true, 'load': true});
12938 * Fires when this node is loaded
12939 * @param {Node} this This node
12942 * The loader used by this node (defaults to using the tree's defined loader)
12947 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12948 expand : function(deep, anim, callback){
12949 if(this.loading){ // if an async load is already running, waiting til it's done
12951 var f = function(){
12952 if(!this.loading){ // done loading
12953 clearInterval(timer);
12954 this.expand(deep, anim, callback);
12956 }.createDelegate(this);
12957 timer = setInterval(f, 200);
12961 if(this.fireEvent("beforeload", this) === false){
12964 this.loading = true;
12965 this.ui.beforeLoad(this);
12966 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12968 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12972 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12976 * Returns true if this node is currently loading
12977 * @return {Boolean}
12979 isLoading : function(){
12980 return this.loading;
12983 loadComplete : function(deep, anim, callback){
12984 this.loading = false;
12985 this.loaded = true;
12986 this.ui.afterLoad(this);
12987 this.fireEvent("load", this);
12988 this.expand(deep, anim, callback);
12992 * Returns true if this node has been loaded
12993 * @return {Boolean}
12995 isLoaded : function(){
12996 return this.loaded;
12999 hasChildNodes : function(){
13000 if(!this.isLeaf() && !this.loaded){
13003 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13008 * Trigger a reload for this node
13009 * @param {Function} callback
13011 reload : function(callback){
13012 this.collapse(false, false);
13013 while(this.firstChild){
13014 this.removeChild(this.firstChild);
13016 this.childrenRendered = false;
13017 this.loaded = false;
13018 if(this.isHiddenRoot()){
13019 this.expanded = false;
13021 this.expand(false, false, callback);
13025 * Ext JS Library 1.1.1
13026 * Copyright(c) 2006-2007, Ext JS, LLC.
13028 * Originally Released Under LGPL - original licence link has changed is not relivant.
13031 * <script type="text/javascript">
13035 * @class Roo.tree.TreeNodeUI
13037 * @param {Object} node The node to render
13038 * The TreeNode UI implementation is separate from the
13039 * tree implementation. Unless you are customizing the tree UI,
13040 * you should never have to use this directly.
13042 Roo.tree.TreeNodeUI = function(node){
13044 this.rendered = false;
13045 this.animating = false;
13046 this.emptyIcon = Roo.BLANK_IMAGE_URL;
13049 Roo.tree.TreeNodeUI.prototype = {
13050 removeChild : function(node){
13052 this.ctNode.removeChild(node.ui.getEl());
13056 beforeLoad : function(){
13057 this.addClass("x-tree-node-loading");
13060 afterLoad : function(){
13061 this.removeClass("x-tree-node-loading");
13064 onTextChange : function(node, text, oldText){
13066 this.textNode.innerHTML = text;
13070 onDisableChange : function(node, state){
13071 this.disabled = state;
13073 this.addClass("x-tree-node-disabled");
13075 this.removeClass("x-tree-node-disabled");
13079 onSelectedChange : function(state){
13082 this.addClass("x-tree-selected");
13085 this.removeClass("x-tree-selected");
13089 onMove : function(tree, node, oldParent, newParent, index, refNode){
13090 this.childIndent = null;
13092 var targetNode = newParent.ui.getContainer();
13093 if(!targetNode){//target not rendered
13094 this.holder = document.createElement("div");
13095 this.holder.appendChild(this.wrap);
13098 var insertBefore = refNode ? refNode.ui.getEl() : null;
13100 targetNode.insertBefore(this.wrap, insertBefore);
13102 targetNode.appendChild(this.wrap);
13104 this.node.renderIndent(true);
13108 addClass : function(cls){
13110 Roo.fly(this.elNode).addClass(cls);
13114 removeClass : function(cls){
13116 Roo.fly(this.elNode).removeClass(cls);
13120 remove : function(){
13122 this.holder = document.createElement("div");
13123 this.holder.appendChild(this.wrap);
13127 fireEvent : function(){
13128 return this.node.fireEvent.apply(this.node, arguments);
13131 initEvents : function(){
13132 this.node.on("move", this.onMove, this);
13133 var E = Roo.EventManager;
13134 var a = this.anchor;
13136 var el = Roo.fly(a, '_treeui');
13138 if(Roo.isOpera){ // opera render bug ignores the CSS
13139 el.setStyle("text-decoration", "none");
13142 el.on("click", this.onClick, this);
13143 el.on("dblclick", this.onDblClick, this);
13146 Roo.EventManager.on(this.checkbox,
13147 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13150 el.on("contextmenu", this.onContextMenu, this);
13152 var icon = Roo.fly(this.iconNode);
13153 icon.on("click", this.onClick, this);
13154 icon.on("dblclick", this.onDblClick, this);
13155 icon.on("contextmenu", this.onContextMenu, this);
13156 E.on(this.ecNode, "click", this.ecClick, this, true);
13158 if(this.node.disabled){
13159 this.addClass("x-tree-node-disabled");
13161 if(this.node.hidden){
13162 this.addClass("x-tree-node-disabled");
13164 var ot = this.node.getOwnerTree();
13165 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13166 if(dd && (!this.node.isRoot || ot.rootVisible)){
13167 Roo.dd.Registry.register(this.elNode, {
13169 handles: this.getDDHandles(),
13175 getDDHandles : function(){
13176 return [this.iconNode, this.textNode];
13181 this.wrap.style.display = "none";
13187 this.wrap.style.display = "";
13191 onContextMenu : function(e){
13192 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13193 e.preventDefault();
13195 this.fireEvent("contextmenu", this.node, e);
13199 onClick : function(e){
13204 if(this.fireEvent("beforeclick", this.node, e) !== false){
13205 if(!this.disabled && this.node.attributes.href){
13206 this.fireEvent("click", this.node, e);
13209 e.preventDefault();
13214 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13215 this.node.toggle();
13218 this.fireEvent("click", this.node, e);
13224 onDblClick : function(e){
13225 e.preventDefault();
13230 this.toggleCheck();
13232 if(!this.animating && this.node.hasChildNodes()){
13233 this.node.toggle();
13235 this.fireEvent("dblclick", this.node, e);
13238 onCheckChange : function(){
13239 var checked = this.checkbox.checked;
13240 this.node.attributes.checked = checked;
13241 this.fireEvent('checkchange', this.node, checked);
13244 ecClick : function(e){
13245 if(!this.animating && this.node.hasChildNodes()){
13246 this.node.toggle();
13250 startDrop : function(){
13251 this.dropping = true;
13254 // delayed drop so the click event doesn't get fired on a drop
13255 endDrop : function(){
13256 setTimeout(function(){
13257 this.dropping = false;
13258 }.createDelegate(this), 50);
13261 expand : function(){
13262 this.updateExpandIcon();
13263 this.ctNode.style.display = "";
13266 focus : function(){
13267 if(!this.node.preventHScroll){
13268 try{this.anchor.focus();
13270 }else if(!Roo.isIE){
13272 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13273 var l = noscroll.scrollLeft;
13274 this.anchor.focus();
13275 noscroll.scrollLeft = l;
13280 toggleCheck : function(value){
13281 var cb = this.checkbox;
13283 cb.checked = (value === undefined ? !cb.checked : value);
13289 this.anchor.blur();
13293 animExpand : function(callback){
13294 var ct = Roo.get(this.ctNode);
13296 if(!this.node.hasChildNodes()){
13297 this.updateExpandIcon();
13298 this.ctNode.style.display = "";
13299 Roo.callback(callback);
13302 this.animating = true;
13303 this.updateExpandIcon();
13306 callback : function(){
13307 this.animating = false;
13308 Roo.callback(callback);
13311 duration: this.node.ownerTree.duration || .25
13315 highlight : function(){
13316 var tree = this.node.getOwnerTree();
13317 Roo.fly(this.wrap).highlight(
13318 tree.hlColor || "C3DAF9",
13319 {endColor: tree.hlBaseColor}
13323 collapse : function(){
13324 this.updateExpandIcon();
13325 this.ctNode.style.display = "none";
13328 animCollapse : function(callback){
13329 var ct = Roo.get(this.ctNode);
13330 ct.enableDisplayMode('block');
13333 this.animating = true;
13334 this.updateExpandIcon();
13337 callback : function(){
13338 this.animating = false;
13339 Roo.callback(callback);
13342 duration: this.node.ownerTree.duration || .25
13346 getContainer : function(){
13347 return this.ctNode;
13350 getEl : function(){
13354 appendDDGhost : function(ghostNode){
13355 ghostNode.appendChild(this.elNode.cloneNode(true));
13358 getDDRepairXY : function(){
13359 return Roo.lib.Dom.getXY(this.iconNode);
13362 onRender : function(){
13366 render : function(bulkRender){
13367 var n = this.node, a = n.attributes;
13368 var targetNode = n.parentNode ?
13369 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13371 if(!this.rendered){
13372 this.rendered = true;
13374 this.renderElements(n, a, targetNode, bulkRender);
13377 if(this.textNode.setAttributeNS){
13378 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13380 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13383 this.textNode.setAttribute("ext:qtip", a.qtip);
13385 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13388 }else if(a.qtipCfg){
13389 a.qtipCfg.target = Roo.id(this.textNode);
13390 Roo.QuickTips.register(a.qtipCfg);
13393 if(!this.node.expanded){
13394 this.updateExpandIcon();
13397 if(bulkRender === true) {
13398 targetNode.appendChild(this.wrap);
13403 renderElements : function(n, a, targetNode, bulkRender)
13405 // add some indent caching, this helps performance when rendering a large tree
13406 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13407 var t = n.getOwnerTree();
13408 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13409 if (typeof(n.attributes.html) != 'undefined') {
13410 txt = n.attributes.html;
13412 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13413 var cb = typeof a.checked == 'boolean';
13414 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13415 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13416 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13417 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13418 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13419 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13420 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13421 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13422 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13423 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13426 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13427 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13428 n.nextSibling.ui.getEl(), buf.join(""));
13430 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13433 this.elNode = this.wrap.childNodes[0];
13434 this.ctNode = this.wrap.childNodes[1];
13435 var cs = this.elNode.childNodes;
13436 this.indentNode = cs[0];
13437 this.ecNode = cs[1];
13438 this.iconNode = cs[2];
13441 this.checkbox = cs[3];
13444 this.anchor = cs[index];
13445 this.textNode = cs[index].firstChild;
13448 getAnchor : function(){
13449 return this.anchor;
13452 getTextEl : function(){
13453 return this.textNode;
13456 getIconEl : function(){
13457 return this.iconNode;
13460 isChecked : function(){
13461 return this.checkbox ? this.checkbox.checked : false;
13464 updateExpandIcon : function(){
13466 var n = this.node, c1, c2;
13467 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13468 var hasChild = n.hasChildNodes();
13472 c1 = "x-tree-node-collapsed";
13473 c2 = "x-tree-node-expanded";
13476 c1 = "x-tree-node-expanded";
13477 c2 = "x-tree-node-collapsed";
13480 this.removeClass("x-tree-node-leaf");
13481 this.wasLeaf = false;
13483 if(this.c1 != c1 || this.c2 != c2){
13484 Roo.fly(this.elNode).replaceClass(c1, c2);
13485 this.c1 = c1; this.c2 = c2;
13488 // this changes non-leafs into leafs if they have no children.
13489 // it's not very rational behaviour..
13491 if(!this.wasLeaf && this.node.leaf){
13492 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13495 this.wasLeaf = true;
13498 var ecc = "x-tree-ec-icon "+cls;
13499 if(this.ecc != ecc){
13500 this.ecNode.className = ecc;
13506 getChildIndent : function(){
13507 if(!this.childIndent){
13511 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13513 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13515 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13520 this.childIndent = buf.join("");
13522 return this.childIndent;
13525 renderIndent : function(){
13528 var p = this.node.parentNode;
13530 indent = p.ui.getChildIndent();
13532 if(this.indentMarkup != indent){ // don't rerender if not required
13533 this.indentNode.innerHTML = indent;
13534 this.indentMarkup = indent;
13536 this.updateExpandIcon();
13541 Roo.tree.RootTreeNodeUI = function(){
13542 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13544 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13545 render : function(){
13546 if(!this.rendered){
13547 var targetNode = this.node.ownerTree.innerCt.dom;
13548 this.node.expanded = true;
13549 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13550 this.wrap = this.ctNode = targetNode.firstChild;
13553 collapse : function(){
13555 expand : function(){
13559 * Ext JS Library 1.1.1
13560 * Copyright(c) 2006-2007, Ext JS, LLC.
13562 * Originally Released Under LGPL - original licence link has changed is not relivant.
13565 * <script type="text/javascript">
13568 * @class Roo.tree.TreeLoader
13569 * @extends Roo.util.Observable
13570 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13571 * nodes from a specified URL. The response must be a javascript Array definition
13572 * who's elements are node definition objects. eg:
13577 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13578 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13585 * The old style respose with just an array is still supported, but not recommended.
13588 * A server request is sent, and child nodes are loaded only when a node is expanded.
13589 * The loading node's id is passed to the server under the parameter name "node" to
13590 * enable the server to produce the correct child nodes.
13592 * To pass extra parameters, an event handler may be attached to the "beforeload"
13593 * event, and the parameters specified in the TreeLoader's baseParams property:
13595 myTreeLoader.on("beforeload", function(treeLoader, node) {
13596 this.baseParams.category = node.attributes.category;
13601 * This would pass an HTTP parameter called "category" to the server containing
13602 * the value of the Node's "category" attribute.
13604 * Creates a new Treeloader.
13605 * @param {Object} config A config object containing config properties.
13607 Roo.tree.TreeLoader = function(config){
13608 this.baseParams = {};
13609 this.requestMethod = "POST";
13610 Roo.apply(this, config);
13615 * @event beforeload
13616 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13617 * @param {Object} This TreeLoader object.
13618 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13619 * @param {Object} callback The callback function specified in the {@link #load} call.
13624 * Fires when the node has been successfuly loaded.
13625 * @param {Object} This TreeLoader object.
13626 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13627 * @param {Object} response The response object containing the data from the server.
13631 * @event loadexception
13632 * Fires if the network request failed.
13633 * @param {Object} This TreeLoader object.
13634 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13635 * @param {Object} response The response object containing the data from the server.
13637 loadexception : true,
13640 * Fires before a node is created, enabling you to return custom Node types
13641 * @param {Object} This TreeLoader object.
13642 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13647 Roo.tree.TreeLoader.superclass.constructor.call(this);
13650 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13652 * @cfg {String} dataUrl The URL from which to request a Json string which
13653 * specifies an array of node definition object representing the child nodes
13657 * @cfg {String} requestMethod either GET or POST
13658 * defaults to POST (due to BC)
13662 * @cfg {Object} baseParams (optional) An object containing properties which
13663 * specify HTTP parameters to be passed to each request for child nodes.
13666 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13667 * created by this loader. If the attributes sent by the server have an attribute in this object,
13668 * they take priority.
13671 * @cfg {Object} uiProviders (optional) An object containing properties which
13673 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13674 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13675 * <i>uiProvider</i> attribute of a returned child node is a string rather
13676 * than a reference to a TreeNodeUI implementation, this that string value
13677 * is used as a property name in the uiProviders object. You can define the provider named
13678 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13683 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13684 * child nodes before loading.
13686 clearOnLoad : true,
13689 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13690 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13691 * Grid query { data : [ .....] }
13696 * @cfg {String} queryParam (optional)
13697 * Name of the query as it will be passed on the querystring (defaults to 'node')
13698 * eg. the request will be ?node=[id]
13705 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13706 * This is called automatically when a node is expanded, but may be used to reload
13707 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13708 * @param {Roo.tree.TreeNode} node
13709 * @param {Function} callback
13711 load : function(node, callback){
13712 if(this.clearOnLoad){
13713 while(node.firstChild){
13714 node.removeChild(node.firstChild);
13717 if(node.attributes.children){ // preloaded json children
13718 var cs = node.attributes.children;
13719 for(var i = 0, len = cs.length; i < len; i++){
13720 node.appendChild(this.createNode(cs[i]));
13722 if(typeof callback == "function"){
13725 }else if(this.dataUrl){
13726 this.requestData(node, callback);
13730 getParams: function(node){
13731 var buf = [], bp = this.baseParams;
13732 for(var key in bp){
13733 if(typeof bp[key] != "function"){
13734 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13737 var n = this.queryParam === false ? 'node' : this.queryParam;
13738 buf.push(n + "=", encodeURIComponent(node.id));
13739 return buf.join("");
13742 requestData : function(node, callback){
13743 if(this.fireEvent("beforeload", this, node, callback) !== false){
13744 this.transId = Roo.Ajax.request({
13745 method:this.requestMethod,
13746 url: this.dataUrl||this.url,
13747 success: this.handleResponse,
13748 failure: this.handleFailure,
13750 argument: {callback: callback, node: node},
13751 params: this.getParams(node)
13754 // if the load is cancelled, make sure we notify
13755 // the node that we are done
13756 if(typeof callback == "function"){
13762 isLoading : function(){
13763 return this.transId ? true : false;
13766 abort : function(){
13767 if(this.isLoading()){
13768 Roo.Ajax.abort(this.transId);
13773 createNode : function(attr)
13775 // apply baseAttrs, nice idea Corey!
13776 if(this.baseAttrs){
13777 Roo.applyIf(attr, this.baseAttrs);
13779 if(this.applyLoader !== false){
13780 attr.loader = this;
13782 // uiProvider = depreciated..
13784 if(typeof(attr.uiProvider) == 'string'){
13785 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13786 /** eval:var:attr */ eval(attr.uiProvider);
13788 if(typeof(this.uiProviders['default']) != 'undefined') {
13789 attr.uiProvider = this.uiProviders['default'];
13792 this.fireEvent('create', this, attr);
13794 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13796 new Roo.tree.TreeNode(attr) :
13797 new Roo.tree.AsyncTreeNode(attr));
13800 processResponse : function(response, node, callback)
13802 var json = response.responseText;
13805 var o = Roo.decode(json);
13807 if (this.root === false && typeof(o.success) != undefined) {
13808 this.root = 'data'; // the default behaviour for list like data..
13811 if (this.root !== false && !o.success) {
13812 // it's a failure condition.
13813 var a = response.argument;
13814 this.fireEvent("loadexception", this, a.node, response);
13815 Roo.log("Load failed - should have a handler really");
13821 if (this.root !== false) {
13825 for(var i = 0, len = o.length; i < len; i++){
13826 var n = this.createNode(o[i]);
13828 node.appendChild(n);
13831 if(typeof callback == "function"){
13832 callback(this, node);
13835 this.handleFailure(response);
13839 handleResponse : function(response){
13840 this.transId = false;
13841 var a = response.argument;
13842 this.processResponse(response, a.node, a.callback);
13843 this.fireEvent("load", this, a.node, response);
13846 handleFailure : function(response)
13848 // should handle failure better..
13849 this.transId = false;
13850 var a = response.argument;
13851 this.fireEvent("loadexception", this, a.node, response);
13852 if(typeof a.callback == "function"){
13853 a.callback(this, a.node);
13858 * Ext JS Library 1.1.1
13859 * Copyright(c) 2006-2007, Ext JS, LLC.
13861 * Originally Released Under LGPL - original licence link has changed is not relivant.
13864 * <script type="text/javascript">
13868 * @class Roo.tree.TreeFilter
13869 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13870 * @param {TreePanel} tree
13871 * @param {Object} config (optional)
13873 Roo.tree.TreeFilter = function(tree, config){
13875 this.filtered = {};
13876 Roo.apply(this, config);
13879 Roo.tree.TreeFilter.prototype = {
13886 * Filter the data by a specific attribute.
13887 * @param {String/RegExp} value Either string that the attribute value
13888 * should start with or a RegExp to test against the attribute
13889 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13890 * @param {TreeNode} startNode (optional) The node to start the filter at.
13892 filter : function(value, attr, startNode){
13893 attr = attr || "text";
13895 if(typeof value == "string"){
13896 var vlen = value.length;
13897 // auto clear empty filter
13898 if(vlen == 0 && this.clearBlank){
13902 value = value.toLowerCase();
13904 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13906 }else if(value.exec){ // regex?
13908 return value.test(n.attributes[attr]);
13911 throw 'Illegal filter type, must be string or regex';
13913 this.filterBy(f, null, startNode);
13917 * Filter by a function. The passed function will be called with each
13918 * node in the tree (or from the startNode). If the function returns true, the node is kept
13919 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13920 * @param {Function} fn The filter function
13921 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13923 filterBy : function(fn, scope, startNode){
13924 startNode = startNode || this.tree.root;
13925 if(this.autoClear){
13928 var af = this.filtered, rv = this.reverse;
13929 var f = function(n){
13930 if(n == startNode){
13936 var m = fn.call(scope || n, n);
13944 startNode.cascade(f);
13947 if(typeof id != "function"){
13949 if(n && n.parentNode){
13950 n.parentNode.removeChild(n);
13958 * Clears the current filter. Note: with the "remove" option
13959 * set a filter cannot be cleared.
13961 clear : function(){
13963 var af = this.filtered;
13965 if(typeof id != "function"){
13972 this.filtered = {};
13977 * Ext JS Library 1.1.1
13978 * Copyright(c) 2006-2007, Ext JS, LLC.
13980 * Originally Released Under LGPL - original licence link has changed is not relivant.
13983 * <script type="text/javascript">
13988 * @class Roo.tree.TreeSorter
13989 * Provides sorting of nodes in a TreePanel
13991 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13992 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13993 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13994 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13995 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13996 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13998 * @param {TreePanel} tree
13999 * @param {Object} config
14001 Roo.tree.TreeSorter = function(tree, config){
14002 Roo.apply(this, config);
14003 tree.on("beforechildrenrendered", this.doSort, this);
14004 tree.on("append", this.updateSort, this);
14005 tree.on("insert", this.updateSort, this);
14007 var dsc = this.dir && this.dir.toLowerCase() == "desc";
14008 var p = this.property || "text";
14009 var sortType = this.sortType;
14010 var fs = this.folderSort;
14011 var cs = this.caseSensitive === true;
14012 var leafAttr = this.leafAttr || 'leaf';
14014 this.sortFn = function(n1, n2){
14016 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14019 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14023 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14024 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14026 return dsc ? +1 : -1;
14028 return dsc ? -1 : +1;
14035 Roo.tree.TreeSorter.prototype = {
14036 doSort : function(node){
14037 node.sort(this.sortFn);
14040 compareNodes : function(n1, n2){
14041 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14044 updateSort : function(tree, node){
14045 if(node.childrenRendered){
14046 this.doSort.defer(1, this, [node]);
14051 * Ext JS Library 1.1.1
14052 * Copyright(c) 2006-2007, Ext JS, LLC.
14054 * Originally Released Under LGPL - original licence link has changed is not relivant.
14057 * <script type="text/javascript">
14060 if(Roo.dd.DropZone){
14062 Roo.tree.TreeDropZone = function(tree, config){
14063 this.allowParentInsert = false;
14064 this.allowContainerDrop = false;
14065 this.appendOnly = false;
14066 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14068 this.lastInsertClass = "x-tree-no-status";
14069 this.dragOverData = {};
14072 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14073 ddGroup : "TreeDD",
14076 expandDelay : 1000,
14078 expandNode : function(node){
14079 if(node.hasChildNodes() && !node.isExpanded()){
14080 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14084 queueExpand : function(node){
14085 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14088 cancelExpand : function(){
14089 if(this.expandProcId){
14090 clearTimeout(this.expandProcId);
14091 this.expandProcId = false;
14095 isValidDropPoint : function(n, pt, dd, e, data){
14096 if(!n || !data){ return false; }
14097 var targetNode = n.node;
14098 var dropNode = data.node;
14099 // default drop rules
14100 if(!(targetNode && targetNode.isTarget && pt)){
14103 if(pt == "append" && targetNode.allowChildren === false){
14106 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14109 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14112 // reuse the object
14113 var overEvent = this.dragOverData;
14114 overEvent.tree = this.tree;
14115 overEvent.target = targetNode;
14116 overEvent.data = data;
14117 overEvent.point = pt;
14118 overEvent.source = dd;
14119 overEvent.rawEvent = e;
14120 overEvent.dropNode = dropNode;
14121 overEvent.cancel = false;
14122 var result = this.tree.fireEvent("nodedragover", overEvent);
14123 return overEvent.cancel === false && result !== false;
14126 getDropPoint : function(e, n, dd)
14130 return tn.allowChildren !== false ? "append" : false; // always append for root
14132 var dragEl = n.ddel;
14133 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14134 var y = Roo.lib.Event.getPageY(e);
14135 //var noAppend = tn.allowChildren === false || tn.isLeaf();
14137 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14138 var noAppend = tn.allowChildren === false;
14139 if(this.appendOnly || tn.parentNode.allowChildren === false){
14140 return noAppend ? false : "append";
14142 var noBelow = false;
14143 if(!this.allowParentInsert){
14144 noBelow = tn.hasChildNodes() && tn.isExpanded();
14146 var q = (b - t) / (noAppend ? 2 : 3);
14147 if(y >= t && y < (t + q)){
14149 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14156 onNodeEnter : function(n, dd, e, data)
14158 this.cancelExpand();
14161 onNodeOver : function(n, dd, e, data)
14164 var pt = this.getDropPoint(e, n, dd);
14167 // auto node expand check
14168 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14169 this.queueExpand(node);
14170 }else if(pt != "append"){
14171 this.cancelExpand();
14174 // set the insert point style on the target node
14175 var returnCls = this.dropNotAllowed;
14176 if(this.isValidDropPoint(n, pt, dd, e, data)){
14181 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14182 cls = "x-tree-drag-insert-above";
14183 }else if(pt == "below"){
14184 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14185 cls = "x-tree-drag-insert-below";
14187 returnCls = "x-tree-drop-ok-append";
14188 cls = "x-tree-drag-append";
14190 if(this.lastInsertClass != cls){
14191 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14192 this.lastInsertClass = cls;
14199 onNodeOut : function(n, dd, e, data){
14201 this.cancelExpand();
14202 this.removeDropIndicators(n);
14205 onNodeDrop : function(n, dd, e, data){
14206 var point = this.getDropPoint(e, n, dd);
14207 var targetNode = n.node;
14208 targetNode.ui.startDrop();
14209 if(!this.isValidDropPoint(n, point, dd, e, data)){
14210 targetNode.ui.endDrop();
14213 // first try to find the drop node
14214 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14217 target: targetNode,
14222 dropNode: dropNode,
14225 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14226 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14227 targetNode.ui.endDrop();
14230 // allow target changing
14231 targetNode = dropEvent.target;
14232 if(point == "append" && !targetNode.isExpanded()){
14233 targetNode.expand(false, null, function(){
14234 this.completeDrop(dropEvent);
14235 }.createDelegate(this));
14237 this.completeDrop(dropEvent);
14242 completeDrop : function(de){
14243 var ns = de.dropNode, p = de.point, t = de.target;
14244 if(!(ns instanceof Array)){
14248 for(var i = 0, len = ns.length; i < len; i++){
14251 t.parentNode.insertBefore(n, t);
14252 }else if(p == "below"){
14253 t.parentNode.insertBefore(n, t.nextSibling);
14259 if(this.tree.hlDrop){
14263 this.tree.fireEvent("nodedrop", de);
14266 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14267 if(this.tree.hlDrop){
14268 dropNode.ui.focus();
14269 dropNode.ui.highlight();
14271 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14274 getTree : function(){
14278 removeDropIndicators : function(n){
14281 Roo.fly(el).removeClass([
14282 "x-tree-drag-insert-above",
14283 "x-tree-drag-insert-below",
14284 "x-tree-drag-append"]);
14285 this.lastInsertClass = "_noclass";
14289 beforeDragDrop : function(target, e, id){
14290 this.cancelExpand();
14294 afterRepair : function(data){
14295 if(data && Roo.enableFx){
14296 data.node.ui.highlight();
14306 * Ext JS Library 1.1.1
14307 * Copyright(c) 2006-2007, Ext JS, LLC.
14309 * Originally Released Under LGPL - original licence link has changed is not relivant.
14312 * <script type="text/javascript">
14316 if(Roo.dd.DragZone){
14317 Roo.tree.TreeDragZone = function(tree, config){
14318 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14322 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14323 ddGroup : "TreeDD",
14325 onBeforeDrag : function(data, e){
14327 return n && n.draggable && !n.disabled;
14331 onInitDrag : function(e){
14332 var data = this.dragData;
14333 this.tree.getSelectionModel().select(data.node);
14334 this.proxy.update("");
14335 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14336 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14339 getRepairXY : function(e, data){
14340 return data.node.ui.getDDRepairXY();
14343 onEndDrag : function(data, e){
14344 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14349 onValidDrop : function(dd, e, id){
14350 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14354 beforeInvalidDrop : function(e, id){
14355 // this scrolls the original position back into view
14356 var sm = this.tree.getSelectionModel();
14357 sm.clearSelections();
14358 sm.select(this.dragData.node);
14363 * Ext JS Library 1.1.1
14364 * Copyright(c) 2006-2007, Ext JS, LLC.
14366 * Originally Released Under LGPL - original licence link has changed is not relivant.
14369 * <script type="text/javascript">
14372 * @class Roo.tree.TreeEditor
14373 * @extends Roo.Editor
14374 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14375 * as the editor field.
14377 * @param {Object} config (used to be the tree panel.)
14378 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14380 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14381 * @cfg {Roo.form.TextField|Object} field The field configuration
14385 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14388 if (oldconfig) { // old style..
14389 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14392 tree = config.tree;
14393 config.field = config.field || {};
14394 config.field.xtype = 'TextField';
14395 field = Roo.factory(config.field, Roo.form);
14397 config = config || {};
14402 * @event beforenodeedit
14403 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14404 * false from the handler of this event.
14405 * @param {Editor} this
14406 * @param {Roo.tree.Node} node
14408 "beforenodeedit" : true
14412 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14416 tree.on('beforeclick', this.beforeNodeClick, this);
14417 tree.getTreeEl().on('mousedown', this.hide, this);
14418 this.on('complete', this.updateNode, this);
14419 this.on('beforestartedit', this.fitToTree, this);
14420 this.on('startedit', this.bindScroll, this, {delay:10});
14421 this.on('specialkey', this.onSpecialKey, this);
14424 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14426 * @cfg {String} alignment
14427 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14433 * @cfg {Boolean} hideEl
14434 * True to hide the bound element while the editor is displayed (defaults to false)
14438 * @cfg {String} cls
14439 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14441 cls: "x-small-editor x-tree-editor",
14443 * @cfg {Boolean} shim
14444 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14450 * @cfg {Number} maxWidth
14451 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14452 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14453 * scroll and client offsets into account prior to each edit.
14460 fitToTree : function(ed, el){
14461 var td = this.tree.getTreeEl().dom, nd = el.dom;
14462 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14463 td.scrollLeft = nd.offsetLeft;
14467 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14468 this.setSize(w, '');
14470 return this.fireEvent('beforenodeedit', this, this.editNode);
14475 triggerEdit : function(node){
14476 this.completeEdit();
14477 this.editNode = node;
14478 this.startEdit(node.ui.textNode, node.text);
14482 bindScroll : function(){
14483 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14487 beforeNodeClick : function(node, e){
14488 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14489 this.lastClick = new Date();
14490 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14492 this.triggerEdit(node);
14499 updateNode : function(ed, value){
14500 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14501 this.editNode.setText(value);
14505 onHide : function(){
14506 Roo.tree.TreeEditor.superclass.onHide.call(this);
14508 this.editNode.ui.focus();
14513 onSpecialKey : function(field, e){
14514 var k = e.getKey();
14518 }else if(k == e.ENTER && !e.hasModifier()){
14520 this.completeEdit();
14523 });//<Script type="text/javascript">
14526 * Ext JS Library 1.1.1
14527 * Copyright(c) 2006-2007, Ext JS, LLC.
14529 * Originally Released Under LGPL - original licence link has changed is not relivant.
14532 * <script type="text/javascript">
14536 * Not documented??? - probably should be...
14539 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14540 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14542 renderElements : function(n, a, targetNode, bulkRender){
14543 //consel.log("renderElements?");
14544 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14546 var t = n.getOwnerTree();
14547 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14549 var cols = t.columns;
14550 var bw = t.borderWidth;
14552 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14553 var cb = typeof a.checked == "boolean";
14554 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14555 var colcls = 'x-t-' + tid + '-c0';
14557 '<li class="x-tree-node">',
14560 '<div class="x-tree-node-el ', a.cls,'">',
14562 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14565 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14566 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14567 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14568 (a.icon ? ' x-tree-node-inline-icon' : ''),
14569 (a.iconCls ? ' '+a.iconCls : ''),
14570 '" unselectable="on" />',
14571 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14572 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14574 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14575 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14576 '<span unselectable="on" qtip="' + tx + '">',
14580 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14581 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14583 for(var i = 1, len = cols.length; i < len; i++){
14585 colcls = 'x-t-' + tid + '-c' +i;
14586 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14587 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14588 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14594 '<div class="x-clear"></div></div>',
14595 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14598 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14599 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14600 n.nextSibling.ui.getEl(), buf.join(""));
14602 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14604 var el = this.wrap.firstChild;
14606 this.elNode = el.firstChild;
14607 this.ranchor = el.childNodes[1];
14608 this.ctNode = this.wrap.childNodes[1];
14609 var cs = el.firstChild.childNodes;
14610 this.indentNode = cs[0];
14611 this.ecNode = cs[1];
14612 this.iconNode = cs[2];
14615 this.checkbox = cs[3];
14618 this.anchor = cs[index];
14620 this.textNode = cs[index].firstChild;
14622 //el.on("click", this.onClick, this);
14623 //el.on("dblclick", this.onDblClick, this);
14626 // console.log(this);
14628 initEvents : function(){
14629 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14632 var a = this.ranchor;
14634 var el = Roo.get(a);
14636 if(Roo.isOpera){ // opera render bug ignores the CSS
14637 el.setStyle("text-decoration", "none");
14640 el.on("click", this.onClick, this);
14641 el.on("dblclick", this.onDblClick, this);
14642 el.on("contextmenu", this.onContextMenu, this);
14646 /*onSelectedChange : function(state){
14649 this.addClass("x-tree-selected");
14652 this.removeClass("x-tree-selected");
14655 addClass : function(cls){
14657 Roo.fly(this.elRow).addClass(cls);
14663 removeClass : function(cls){
14665 Roo.fly(this.elRow).removeClass(cls);
14671 });//<Script type="text/javascript">
14675 * Ext JS Library 1.1.1
14676 * Copyright(c) 2006-2007, Ext JS, LLC.
14678 * Originally Released Under LGPL - original licence link has changed is not relivant.
14681 * <script type="text/javascript">
14686 * @class Roo.tree.ColumnTree
14687 * @extends Roo.data.TreePanel
14688 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14689 * @cfg {int} borderWidth compined right/left border allowance
14691 * @param {String/HTMLElement/Element} el The container element
14692 * @param {Object} config
14694 Roo.tree.ColumnTree = function(el, config)
14696 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14700 * Fire this event on a container when it resizes
14701 * @param {int} w Width
14702 * @param {int} h Height
14706 this.on('resize', this.onResize, this);
14709 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14713 borderWidth: Roo.isBorderBox ? 0 : 2,
14716 render : function(){
14717 // add the header.....
14719 Roo.tree.ColumnTree.superclass.render.apply(this);
14721 this.el.addClass('x-column-tree');
14723 this.headers = this.el.createChild(
14724 {cls:'x-tree-headers'},this.innerCt.dom);
14726 var cols = this.columns, c;
14727 var totalWidth = 0;
14729 var len = cols.length;
14730 for(var i = 0; i < len; i++){
14732 totalWidth += c.width;
14733 this.headEls.push(this.headers.createChild({
14734 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14736 cls:'x-tree-hd-text',
14739 style:'width:'+(c.width-this.borderWidth)+'px;'
14742 this.headers.createChild({cls:'x-clear'});
14743 // prevent floats from wrapping when clipped
14744 this.headers.setWidth(totalWidth);
14745 //this.innerCt.setWidth(totalWidth);
14746 this.innerCt.setStyle({ overflow: 'auto' });
14747 this.onResize(this.width, this.height);
14751 onResize : function(w,h)
14756 this.innerCt.setWidth(this.width);
14757 this.innerCt.setHeight(this.height-20);
14760 var cols = this.columns, c;
14761 var totalWidth = 0;
14763 var len = cols.length;
14764 for(var i = 0; i < len; i++){
14766 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14767 // it's the expander..
14768 expEl = this.headEls[i];
14771 totalWidth += c.width;
14775 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14777 this.headers.setWidth(w-20);
14786 * Ext JS Library 1.1.1
14787 * Copyright(c) 2006-2007, Ext JS, LLC.
14789 * Originally Released Under LGPL - original licence link has changed is not relivant.
14792 * <script type="text/javascript">
14796 * @class Roo.menu.Menu
14797 * @extends Roo.util.Observable
14798 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14799 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14801 * Creates a new Menu
14802 * @param {Object} config Configuration options
14804 Roo.menu.Menu = function(config){
14806 Roo.menu.Menu.superclass.constructor.call(this, config);
14808 this.id = this.id || Roo.id();
14811 * @event beforeshow
14812 * Fires before this menu is displayed
14813 * @param {Roo.menu.Menu} this
14817 * @event beforehide
14818 * Fires before this menu is hidden
14819 * @param {Roo.menu.Menu} this
14824 * Fires after this menu is displayed
14825 * @param {Roo.menu.Menu} this
14830 * Fires after this menu is hidden
14831 * @param {Roo.menu.Menu} this
14836 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14837 * @param {Roo.menu.Menu} this
14838 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14839 * @param {Roo.EventObject} e
14844 * Fires when the mouse is hovering over this menu
14845 * @param {Roo.menu.Menu} this
14846 * @param {Roo.EventObject} e
14847 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14852 * Fires when the mouse exits this menu
14853 * @param {Roo.menu.Menu} this
14854 * @param {Roo.EventObject} e
14855 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14860 * Fires when a menu item contained in this menu is clicked
14861 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14862 * @param {Roo.EventObject} e
14866 if (this.registerMenu) {
14867 Roo.menu.MenuMgr.register(this);
14870 var mis = this.items;
14871 this.items = new Roo.util.MixedCollection();
14873 this.add.apply(this, mis);
14877 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14879 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14883 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14884 * for bottom-right shadow (defaults to "sides")
14888 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14889 * this menu (defaults to "tl-tr?")
14891 subMenuAlign : "tl-tr?",
14893 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14894 * relative to its element of origin (defaults to "tl-bl?")
14896 defaultAlign : "tl-bl?",
14898 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14900 allowOtherMenus : false,
14902 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14904 registerMenu : true,
14909 render : function(){
14913 var el = this.el = new Roo.Layer({
14915 shadow:this.shadow,
14917 parentEl: this.parentEl || document.body,
14921 this.keyNav = new Roo.menu.MenuNav(this);
14924 el.addClass("x-menu-plain");
14927 el.addClass(this.cls);
14929 // generic focus element
14930 this.focusEl = el.createChild({
14931 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14933 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14934 //disabling touch- as it's causing issues ..
14935 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14936 ul.on('click' , this.onClick, this);
14939 ul.on("mouseover", this.onMouseOver, this);
14940 ul.on("mouseout", this.onMouseOut, this);
14941 this.items.each(function(item){
14946 var li = document.createElement("li");
14947 li.className = "x-menu-list-item";
14948 ul.dom.appendChild(li);
14949 item.render(li, this);
14956 autoWidth : function(){
14957 var el = this.el, ul = this.ul;
14961 var w = this.width;
14964 }else if(Roo.isIE){
14965 el.setWidth(this.minWidth);
14966 var t = el.dom.offsetWidth; // force recalc
14967 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14972 delayAutoWidth : function(){
14975 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14977 this.awTask.delay(20);
14982 findTargetItem : function(e){
14983 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14984 if(t && t.menuItemId){
14985 return this.items.get(t.menuItemId);
14990 onClick : function(e){
14991 Roo.log("menu.onClick");
14992 var t = this.findTargetItem(e);
14997 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14998 if(t == this.activeItem && t.shouldDeactivate(e)){
14999 this.activeItem.deactivate();
15000 delete this.activeItem;
15004 this.setActiveItem(t, true);
15012 this.fireEvent("click", this, t, e);
15016 setActiveItem : function(item, autoExpand){
15017 if(item != this.activeItem){
15018 if(this.activeItem){
15019 this.activeItem.deactivate();
15021 this.activeItem = item;
15022 item.activate(autoExpand);
15023 }else if(autoExpand){
15029 tryActivate : function(start, step){
15030 var items = this.items;
15031 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15032 var item = items.get(i);
15033 if(!item.disabled && item.canActivate){
15034 this.setActiveItem(item, false);
15042 onMouseOver : function(e){
15044 if(t = this.findTargetItem(e)){
15045 if(t.canActivate && !t.disabled){
15046 this.setActiveItem(t, true);
15049 this.fireEvent("mouseover", this, e, t);
15053 onMouseOut : function(e){
15055 if(t = this.findTargetItem(e)){
15056 if(t == this.activeItem && t.shouldDeactivate(e)){
15057 this.activeItem.deactivate();
15058 delete this.activeItem;
15061 this.fireEvent("mouseout", this, e, t);
15065 * Read-only. Returns true if the menu is currently displayed, else false.
15068 isVisible : function(){
15069 return this.el && !this.hidden;
15073 * Displays this menu relative to another element
15074 * @param {String/HTMLElement/Roo.Element} element The element to align to
15075 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15076 * the element (defaults to this.defaultAlign)
15077 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15079 show : function(el, pos, parentMenu){
15080 this.parentMenu = parentMenu;
15084 this.fireEvent("beforeshow", this);
15085 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15089 * Displays this menu at a specific xy position
15090 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15091 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15093 showAt : function(xy, parentMenu, /* private: */_e){
15094 this.parentMenu = parentMenu;
15099 this.fireEvent("beforeshow", this);
15100 xy = this.el.adjustForConstraints(xy);
15104 this.hidden = false;
15106 this.fireEvent("show", this);
15109 focus : function(){
15111 this.doFocus.defer(50, this);
15115 doFocus : function(){
15117 this.focusEl.focus();
15122 * Hides this menu and optionally all parent menus
15123 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15125 hide : function(deep){
15126 if(this.el && this.isVisible()){
15127 this.fireEvent("beforehide", this);
15128 if(this.activeItem){
15129 this.activeItem.deactivate();
15130 this.activeItem = null;
15133 this.hidden = true;
15134 this.fireEvent("hide", this);
15136 if(deep === true && this.parentMenu){
15137 this.parentMenu.hide(true);
15142 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15143 * Any of the following are valid:
15145 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15146 * <li>An HTMLElement object which will be converted to a menu item</li>
15147 * <li>A menu item config object that will be created as a new menu item</li>
15148 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15149 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15154 var menu = new Roo.menu.Menu();
15156 // Create a menu item to add by reference
15157 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15159 // Add a bunch of items at once using different methods.
15160 // Only the last item added will be returned.
15161 var item = menu.add(
15162 menuItem, // add existing item by ref
15163 'Dynamic Item', // new TextItem
15164 '-', // new separator
15165 { text: 'Config Item' } // new item by config
15168 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15169 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15172 var a = arguments, l = a.length, item;
15173 for(var i = 0; i < l; i++){
15175 if ((typeof(el) == "object") && el.xtype && el.xns) {
15176 el = Roo.factory(el, Roo.menu);
15179 if(el.render){ // some kind of Item
15180 item = this.addItem(el);
15181 }else if(typeof el == "string"){ // string
15182 if(el == "separator" || el == "-"){
15183 item = this.addSeparator();
15185 item = this.addText(el);
15187 }else if(el.tagName || el.el){ // element
15188 item = this.addElement(el);
15189 }else if(typeof el == "object"){ // must be menu item config?
15190 item = this.addMenuItem(el);
15197 * Returns this menu's underlying {@link Roo.Element} object
15198 * @return {Roo.Element} The element
15200 getEl : function(){
15208 * Adds a separator bar to the menu
15209 * @return {Roo.menu.Item} The menu item that was added
15211 addSeparator : function(){
15212 return this.addItem(new Roo.menu.Separator());
15216 * Adds an {@link Roo.Element} object to the menu
15217 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15218 * @return {Roo.menu.Item} The menu item that was added
15220 addElement : function(el){
15221 return this.addItem(new Roo.menu.BaseItem(el));
15225 * Adds an existing object based on {@link Roo.menu.Item} to the menu
15226 * @param {Roo.menu.Item} item The menu item to add
15227 * @return {Roo.menu.Item} The menu item that was added
15229 addItem : function(item){
15230 this.items.add(item);
15232 var li = document.createElement("li");
15233 li.className = "x-menu-list-item";
15234 this.ul.dom.appendChild(li);
15235 item.render(li, this);
15236 this.delayAutoWidth();
15242 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15243 * @param {Object} config A MenuItem config object
15244 * @return {Roo.menu.Item} The menu item that was added
15246 addMenuItem : function(config){
15247 if(!(config instanceof Roo.menu.Item)){
15248 if(typeof config.checked == "boolean"){ // must be check menu item config?
15249 config = new Roo.menu.CheckItem(config);
15251 config = new Roo.menu.Item(config);
15254 return this.addItem(config);
15258 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15259 * @param {String} text The text to display in the menu item
15260 * @return {Roo.menu.Item} The menu item that was added
15262 addText : function(text){
15263 return this.addItem(new Roo.menu.TextItem({ text : text }));
15267 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15268 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15269 * @param {Roo.menu.Item} item The menu item to add
15270 * @return {Roo.menu.Item} The menu item that was added
15272 insert : function(index, item){
15273 this.items.insert(index, item);
15275 var li = document.createElement("li");
15276 li.className = "x-menu-list-item";
15277 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15278 item.render(li, this);
15279 this.delayAutoWidth();
15285 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15286 * @param {Roo.menu.Item} item The menu item to remove
15288 remove : function(item){
15289 this.items.removeKey(item.id);
15294 * Removes and destroys all items in the menu
15296 removeAll : function(){
15298 while(f = this.items.first()){
15304 // MenuNav is a private utility class used internally by the Menu
15305 Roo.menu.MenuNav = function(menu){
15306 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15307 this.scope = this.menu = menu;
15310 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15311 doRelay : function(e, h){
15312 var k = e.getKey();
15313 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15314 this.menu.tryActivate(0, 1);
15317 return h.call(this.scope || this, e, this.menu);
15320 up : function(e, m){
15321 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15322 m.tryActivate(m.items.length-1, -1);
15326 down : function(e, m){
15327 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15328 m.tryActivate(0, 1);
15332 right : function(e, m){
15334 m.activeItem.expandMenu(true);
15338 left : function(e, m){
15340 if(m.parentMenu && m.parentMenu.activeItem){
15341 m.parentMenu.activeItem.activate();
15345 enter : function(e, m){
15347 e.stopPropagation();
15348 m.activeItem.onClick(e);
15349 m.fireEvent("click", this, m.activeItem);
15355 * Ext JS Library 1.1.1
15356 * Copyright(c) 2006-2007, Ext JS, LLC.
15358 * Originally Released Under LGPL - original licence link has changed is not relivant.
15361 * <script type="text/javascript">
15365 * @class Roo.menu.MenuMgr
15366 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15369 Roo.menu.MenuMgr = function(){
15370 var menus, active, groups = {}, attached = false, lastShow = new Date();
15372 // private - called when first menu is created
15375 active = new Roo.util.MixedCollection();
15376 Roo.get(document).addKeyListener(27, function(){
15377 if(active.length > 0){
15384 function hideAll(){
15385 if(active && active.length > 0){
15386 var c = active.clone();
15387 c.each(function(m){
15394 function onHide(m){
15396 if(active.length < 1){
15397 Roo.get(document).un("mousedown", onMouseDown);
15403 function onShow(m){
15404 var last = active.last();
15405 lastShow = new Date();
15408 Roo.get(document).on("mousedown", onMouseDown);
15412 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15413 m.parentMenu.activeChild = m;
15414 }else if(last && last.isVisible()){
15415 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15420 function onBeforeHide(m){
15422 m.activeChild.hide();
15424 if(m.autoHideTimer){
15425 clearTimeout(m.autoHideTimer);
15426 delete m.autoHideTimer;
15431 function onBeforeShow(m){
15432 var pm = m.parentMenu;
15433 if(!pm && !m.allowOtherMenus){
15435 }else if(pm && pm.activeChild && active != m){
15436 pm.activeChild.hide();
15441 function onMouseDown(e){
15442 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15448 function onBeforeCheck(mi, state){
15450 var g = groups[mi.group];
15451 for(var i = 0, l = g.length; i < l; i++){
15453 g[i].setChecked(false);
15462 * Hides all menus that are currently visible
15464 hideAll : function(){
15469 register : function(menu){
15473 menus[menu.id] = menu;
15474 menu.on("beforehide", onBeforeHide);
15475 menu.on("hide", onHide);
15476 menu.on("beforeshow", onBeforeShow);
15477 menu.on("show", onShow);
15478 var g = menu.group;
15479 if(g && menu.events["checkchange"]){
15483 groups[g].push(menu);
15484 menu.on("checkchange", onCheck);
15489 * Returns a {@link Roo.menu.Menu} object
15490 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15491 * be used to generate and return a new Menu instance.
15493 get : function(menu){
15494 if(typeof menu == "string"){ // menu id
15495 return menus[menu];
15496 }else if(menu.events){ // menu instance
15498 }else if(typeof menu.length == 'number'){ // array of menu items?
15499 return new Roo.menu.Menu({items:menu});
15500 }else{ // otherwise, must be a config
15501 return new Roo.menu.Menu(menu);
15506 unregister : function(menu){
15507 delete menus[menu.id];
15508 menu.un("beforehide", onBeforeHide);
15509 menu.un("hide", onHide);
15510 menu.un("beforeshow", onBeforeShow);
15511 menu.un("show", onShow);
15512 var g = menu.group;
15513 if(g && menu.events["checkchange"]){
15514 groups[g].remove(menu);
15515 menu.un("checkchange", onCheck);
15520 registerCheckable : function(menuItem){
15521 var g = menuItem.group;
15526 groups[g].push(menuItem);
15527 menuItem.on("beforecheckchange", onBeforeCheck);
15532 unregisterCheckable : function(menuItem){
15533 var g = menuItem.group;
15535 groups[g].remove(menuItem);
15536 menuItem.un("beforecheckchange", onBeforeCheck);
15542 * Ext JS Library 1.1.1
15543 * Copyright(c) 2006-2007, Ext JS, LLC.
15545 * Originally Released Under LGPL - original licence link has changed is not relivant.
15548 * <script type="text/javascript">
15553 * @class Roo.menu.BaseItem
15554 * @extends Roo.Component
15555 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15556 * management and base configuration options shared by all menu components.
15558 * Creates a new BaseItem
15559 * @param {Object} config Configuration options
15561 Roo.menu.BaseItem = function(config){
15562 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15567 * Fires when this item is clicked
15568 * @param {Roo.menu.BaseItem} this
15569 * @param {Roo.EventObject} e
15574 * Fires when this item is activated
15575 * @param {Roo.menu.BaseItem} this
15579 * @event deactivate
15580 * Fires when this item is deactivated
15581 * @param {Roo.menu.BaseItem} this
15587 this.on("click", this.handler, this.scope, true);
15591 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15593 * @cfg {Function} handler
15594 * A function that will handle the click event of this menu item (defaults to undefined)
15597 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15599 canActivate : false,
15602 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15607 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15609 activeClass : "x-menu-item-active",
15611 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15613 hideOnClick : true,
15615 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15620 ctype: "Roo.menu.BaseItem",
15623 actionMode : "container",
15626 render : function(container, parentMenu){
15627 this.parentMenu = parentMenu;
15628 Roo.menu.BaseItem.superclass.render.call(this, container);
15629 this.container.menuItemId = this.id;
15633 onRender : function(container, position){
15634 this.el = Roo.get(this.el);
15635 container.dom.appendChild(this.el.dom);
15639 onClick : function(e){
15640 if(!this.disabled && this.fireEvent("click", this, e) !== false
15641 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15642 this.handleClick(e);
15649 activate : function(){
15653 var li = this.container;
15654 li.addClass(this.activeClass);
15655 this.region = li.getRegion().adjust(2, 2, -2, -2);
15656 this.fireEvent("activate", this);
15661 deactivate : function(){
15662 this.container.removeClass(this.activeClass);
15663 this.fireEvent("deactivate", this);
15667 shouldDeactivate : function(e){
15668 return !this.region || !this.region.contains(e.getPoint());
15672 handleClick : function(e){
15673 if(this.hideOnClick){
15674 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15679 expandMenu : function(autoActivate){
15684 hideMenu : function(){
15689 * Ext JS Library 1.1.1
15690 * Copyright(c) 2006-2007, Ext JS, LLC.
15692 * Originally Released Under LGPL - original licence link has changed is not relivant.
15695 * <script type="text/javascript">
15699 * @class Roo.menu.Adapter
15700 * @extends Roo.menu.BaseItem
15701 * 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.
15702 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15704 * Creates a new Adapter
15705 * @param {Object} config Configuration options
15707 Roo.menu.Adapter = function(component, config){
15708 Roo.menu.Adapter.superclass.constructor.call(this, config);
15709 this.component = component;
15711 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15713 canActivate : true,
15716 onRender : function(container, position){
15717 this.component.render(container);
15718 this.el = this.component.getEl();
15722 activate : function(){
15726 this.component.focus();
15727 this.fireEvent("activate", this);
15732 deactivate : function(){
15733 this.fireEvent("deactivate", this);
15737 disable : function(){
15738 this.component.disable();
15739 Roo.menu.Adapter.superclass.disable.call(this);
15743 enable : function(){
15744 this.component.enable();
15745 Roo.menu.Adapter.superclass.enable.call(this);
15749 * Ext JS Library 1.1.1
15750 * Copyright(c) 2006-2007, Ext JS, LLC.
15752 * Originally Released Under LGPL - original licence link has changed is not relivant.
15755 * <script type="text/javascript">
15759 * @class Roo.menu.TextItem
15760 * @extends Roo.menu.BaseItem
15761 * Adds a static text string to a menu, usually used as either a heading or group separator.
15762 * Note: old style constructor with text is still supported.
15765 * Creates a new TextItem
15766 * @param {Object} cfg Configuration
15768 Roo.menu.TextItem = function(cfg){
15769 if (typeof(cfg) == 'string') {
15772 Roo.apply(this,cfg);
15775 Roo.menu.TextItem.superclass.constructor.call(this);
15778 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15780 * @cfg {Boolean} text Text to show on item.
15785 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15787 hideOnClick : false,
15789 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15791 itemCls : "x-menu-text",
15794 onRender : function(){
15795 var s = document.createElement("span");
15796 s.className = this.itemCls;
15797 s.innerHTML = this.text;
15799 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15803 * Ext JS Library 1.1.1
15804 * Copyright(c) 2006-2007, Ext JS, LLC.
15806 * Originally Released Under LGPL - original licence link has changed is not relivant.
15809 * <script type="text/javascript">
15813 * @class Roo.menu.Separator
15814 * @extends Roo.menu.BaseItem
15815 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15816 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15818 * @param {Object} config Configuration options
15820 Roo.menu.Separator = function(config){
15821 Roo.menu.Separator.superclass.constructor.call(this, config);
15824 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15826 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15828 itemCls : "x-menu-sep",
15830 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15832 hideOnClick : false,
15835 onRender : function(li){
15836 var s = document.createElement("span");
15837 s.className = this.itemCls;
15838 s.innerHTML = " ";
15840 li.addClass("x-menu-sep-li");
15841 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15845 * Ext JS Library 1.1.1
15846 * Copyright(c) 2006-2007, Ext JS, LLC.
15848 * Originally Released Under LGPL - original licence link has changed is not relivant.
15851 * <script type="text/javascript">
15854 * @class Roo.menu.Item
15855 * @extends Roo.menu.BaseItem
15856 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15857 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15858 * activation and click handling.
15860 * Creates a new Item
15861 * @param {Object} config Configuration options
15863 Roo.menu.Item = function(config){
15864 Roo.menu.Item.superclass.constructor.call(this, config);
15866 this.menu = Roo.menu.MenuMgr.get(this.menu);
15869 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15872 * @cfg {String} text
15873 * The text to show on the menu item.
15877 * @cfg {String} HTML to render in menu
15878 * The text to show on the menu item (HTML version).
15882 * @cfg {String} icon
15883 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15887 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15889 itemCls : "x-menu-item",
15891 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15893 canActivate : true,
15895 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15898 // doc'd in BaseItem
15902 ctype: "Roo.menu.Item",
15905 onRender : function(container, position){
15906 var el = document.createElement("a");
15907 el.hideFocus = true;
15908 el.unselectable = "on";
15909 el.href = this.href || "#";
15910 if(this.hrefTarget){
15911 el.target = this.hrefTarget;
15913 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15915 var html = this.html.length ? this.html : String.format('{0}',this.text);
15917 el.innerHTML = String.format(
15918 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15919 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15921 Roo.menu.Item.superclass.onRender.call(this, container, position);
15925 * Sets the text to display in this menu item
15926 * @param {String} text The text to display
15927 * @param {Boolean} isHTML true to indicate text is pure html.
15929 setText : function(text, isHTML){
15937 var html = this.html.length ? this.html : String.format('{0}',this.text);
15939 this.el.update(String.format(
15940 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15941 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15942 this.parentMenu.autoWidth();
15947 handleClick : function(e){
15948 if(!this.href){ // if no link defined, stop the event automatically
15951 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15955 activate : function(autoExpand){
15956 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15966 shouldDeactivate : function(e){
15967 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15968 if(this.menu && this.menu.isVisible()){
15969 return !this.menu.getEl().getRegion().contains(e.getPoint());
15977 deactivate : function(){
15978 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15983 expandMenu : function(autoActivate){
15984 if(!this.disabled && this.menu){
15985 clearTimeout(this.hideTimer);
15986 delete this.hideTimer;
15987 if(!this.menu.isVisible() && !this.showTimer){
15988 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15989 }else if (this.menu.isVisible() && autoActivate){
15990 this.menu.tryActivate(0, 1);
15996 deferExpand : function(autoActivate){
15997 delete this.showTimer;
15998 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
16000 this.menu.tryActivate(0, 1);
16005 hideMenu : function(){
16006 clearTimeout(this.showTimer);
16007 delete this.showTimer;
16008 if(!this.hideTimer && this.menu && this.menu.isVisible()){
16009 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16014 deferHide : function(){
16015 delete this.hideTimer;
16020 * Ext JS Library 1.1.1
16021 * Copyright(c) 2006-2007, Ext JS, LLC.
16023 * Originally Released Under LGPL - original licence link has changed is not relivant.
16026 * <script type="text/javascript">
16030 * @class Roo.menu.CheckItem
16031 * @extends Roo.menu.Item
16032 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16034 * Creates a new CheckItem
16035 * @param {Object} config Configuration options
16037 Roo.menu.CheckItem = function(config){
16038 Roo.menu.CheckItem.superclass.constructor.call(this, config);
16041 * @event beforecheckchange
16042 * Fires before the checked value is set, providing an opportunity to cancel if needed
16043 * @param {Roo.menu.CheckItem} this
16044 * @param {Boolean} checked The new checked value that will be set
16046 "beforecheckchange" : true,
16048 * @event checkchange
16049 * Fires after the checked value has been set
16050 * @param {Roo.menu.CheckItem} this
16051 * @param {Boolean} checked The checked value that was set
16053 "checkchange" : true
16055 if(this.checkHandler){
16056 this.on('checkchange', this.checkHandler, this.scope);
16059 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16061 * @cfg {String} group
16062 * All check items with the same group name will automatically be grouped into a single-select
16063 * radio button group (defaults to '')
16066 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16068 itemCls : "x-menu-item x-menu-check-item",
16070 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16072 groupClass : "x-menu-group-item",
16075 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
16076 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16077 * initialized with checked = true will be rendered as checked.
16082 ctype: "Roo.menu.CheckItem",
16085 onRender : function(c){
16086 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16088 this.el.addClass(this.groupClass);
16090 Roo.menu.MenuMgr.registerCheckable(this);
16092 this.checked = false;
16093 this.setChecked(true, true);
16098 destroy : function(){
16100 Roo.menu.MenuMgr.unregisterCheckable(this);
16102 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16106 * Set the checked state of this item
16107 * @param {Boolean} checked The new checked value
16108 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16110 setChecked : function(state, suppressEvent){
16111 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16112 if(this.container){
16113 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16115 this.checked = state;
16116 if(suppressEvent !== true){
16117 this.fireEvent("checkchange", this, state);
16123 handleClick : function(e){
16124 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16125 this.setChecked(!this.checked);
16127 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16131 * Ext JS Library 1.1.1
16132 * Copyright(c) 2006-2007, Ext JS, LLC.
16134 * Originally Released Under LGPL - original licence link has changed is not relivant.
16137 * <script type="text/javascript">
16141 * @class Roo.menu.DateItem
16142 * @extends Roo.menu.Adapter
16143 * A menu item that wraps the {@link Roo.DatPicker} component.
16145 * Creates a new DateItem
16146 * @param {Object} config Configuration options
16148 Roo.menu.DateItem = function(config){
16149 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16150 /** The Roo.DatePicker object @type Roo.DatePicker */
16151 this.picker = this.component;
16152 this.addEvents({select: true});
16154 this.picker.on("render", function(picker){
16155 picker.getEl().swallowEvent("click");
16156 picker.container.addClass("x-menu-date-item");
16159 this.picker.on("select", this.onSelect, this);
16162 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16164 onSelect : function(picker, date){
16165 this.fireEvent("select", this, date, picker);
16166 Roo.menu.DateItem.superclass.handleClick.call(this);
16170 * Ext JS Library 1.1.1
16171 * Copyright(c) 2006-2007, Ext JS, LLC.
16173 * Originally Released Under LGPL - original licence link has changed is not relivant.
16176 * <script type="text/javascript">
16180 * @class Roo.menu.ColorItem
16181 * @extends Roo.menu.Adapter
16182 * A menu item that wraps the {@link Roo.ColorPalette} component.
16184 * Creates a new ColorItem
16185 * @param {Object} config Configuration options
16187 Roo.menu.ColorItem = function(config){
16188 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16189 /** The Roo.ColorPalette object @type Roo.ColorPalette */
16190 this.palette = this.component;
16191 this.relayEvents(this.palette, ["select"]);
16192 if(this.selectHandler){
16193 this.on('select', this.selectHandler, this.scope);
16196 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16198 * Ext JS Library 1.1.1
16199 * Copyright(c) 2006-2007, Ext JS, LLC.
16201 * Originally Released Under LGPL - original licence link has changed is not relivant.
16204 * <script type="text/javascript">
16209 * @class Roo.menu.DateMenu
16210 * @extends Roo.menu.Menu
16211 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16213 * Creates a new DateMenu
16214 * @param {Object} config Configuration options
16216 Roo.menu.DateMenu = function(config){
16217 Roo.menu.DateMenu.superclass.constructor.call(this, config);
16219 var di = new Roo.menu.DateItem(config);
16222 * The {@link Roo.DatePicker} instance for this DateMenu
16225 this.picker = di.picker;
16228 * @param {DatePicker} picker
16229 * @param {Date} date
16231 this.relayEvents(di, ["select"]);
16232 this.on('beforeshow', function(){
16234 this.picker.hideMonthPicker(false);
16238 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16242 * Ext JS Library 1.1.1
16243 * Copyright(c) 2006-2007, Ext JS, LLC.
16245 * Originally Released Under LGPL - original licence link has changed is not relivant.
16248 * <script type="text/javascript">
16253 * @class Roo.menu.ColorMenu
16254 * @extends Roo.menu.Menu
16255 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16257 * Creates a new ColorMenu
16258 * @param {Object} config Configuration options
16260 Roo.menu.ColorMenu = function(config){
16261 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16263 var ci = new Roo.menu.ColorItem(config);
16266 * The {@link Roo.ColorPalette} instance for this ColorMenu
16267 * @type ColorPalette
16269 this.palette = ci.palette;
16272 * @param {ColorPalette} palette
16273 * @param {String} color
16275 this.relayEvents(ci, ["select"]);
16277 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16279 * Ext JS Library 1.1.1
16280 * Copyright(c) 2006-2007, Ext JS, LLC.
16282 * Originally Released Under LGPL - original licence link has changed is not relivant.
16285 * <script type="text/javascript">
16289 * @class Roo.form.TextItem
16290 * @extends Roo.BoxComponent
16291 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16293 * Creates a new TextItem
16294 * @param {Object} config Configuration options
16296 Roo.form.TextItem = function(config){
16297 Roo.form.TextItem.superclass.constructor.call(this, config);
16300 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
16303 * @cfg {String} tag the tag for this item (default div)
16307 * @cfg {String} html the content for this item
16311 getAutoCreate : function()
16324 onRender : function(ct, position)
16326 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16329 var cfg = this.getAutoCreate();
16331 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16333 if (!cfg.name.length) {
16336 this.el = ct.createChild(cfg, position);
16342 * Ext JS Library 1.1.1
16343 * Copyright(c) 2006-2007, Ext JS, LLC.
16345 * Originally Released Under LGPL - original licence link has changed is not relivant.
16348 * <script type="text/javascript">
16352 * @class Roo.form.Field
16353 * @extends Roo.BoxComponent
16354 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16356 * Creates a new Field
16357 * @param {Object} config Configuration options
16359 Roo.form.Field = function(config){
16360 Roo.form.Field.superclass.constructor.call(this, config);
16363 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16365 * @cfg {String} fieldLabel Label to use when rendering a form.
16368 * @cfg {String} qtip Mouse over tip
16372 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16374 invalidClass : "x-form-invalid",
16376 * @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")
16378 invalidText : "The value in this field is invalid",
16380 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16382 focusClass : "x-form-focus",
16384 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16385 automatic validation (defaults to "keyup").
16387 validationEvent : "keyup",
16389 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16391 validateOnBlur : true,
16393 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16395 validationDelay : 250,
16397 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16398 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16400 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16402 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16404 fieldClass : "x-form-field",
16406 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16409 ----------- ----------------------------------------------------------------------
16410 qtip Display a quick tip when the user hovers over the field
16411 title Display a default browser title attribute popup
16412 under Add a block div beneath the field containing the error text
16413 side Add an error icon to the right of the field with a popup on hover
16414 [element id] Add the error text directly to the innerHTML of the specified element
16417 msgTarget : 'qtip',
16419 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16424 * @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.
16429 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16434 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16436 inputType : undefined,
16439 * @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).
16441 tabIndex : undefined,
16444 isFormField : true,
16449 * @property {Roo.Element} fieldEl
16450 * Element Containing the rendered Field (with label etc.)
16453 * @cfg {Mixed} value A value to initialize this field with.
16458 * @cfg {String} name The field's HTML name attribute.
16461 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16464 loadedValue : false,
16468 initComponent : function(){
16469 Roo.form.Field.superclass.initComponent.call(this);
16473 * Fires when this field receives input focus.
16474 * @param {Roo.form.Field} this
16479 * Fires when this field loses input focus.
16480 * @param {Roo.form.Field} this
16484 * @event specialkey
16485 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16486 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16487 * @param {Roo.form.Field} this
16488 * @param {Roo.EventObject} e The event object
16493 * Fires just before the field blurs if the field value has changed.
16494 * @param {Roo.form.Field} this
16495 * @param {Mixed} newValue The new value
16496 * @param {Mixed} oldValue The original value
16501 * Fires after the field has been marked as invalid.
16502 * @param {Roo.form.Field} this
16503 * @param {String} msg The validation message
16508 * Fires after the field has been validated with no errors.
16509 * @param {Roo.form.Field} this
16514 * Fires after the key up
16515 * @param {Roo.form.Field} this
16516 * @param {Roo.EventObject} e The event Object
16523 * Returns the name attribute of the field if available
16524 * @return {String} name The field name
16526 getName: function(){
16527 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16531 onRender : function(ct, position){
16532 Roo.form.Field.superclass.onRender.call(this, ct, position);
16534 var cfg = this.getAutoCreate();
16536 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16538 if (!cfg.name.length) {
16541 if(this.inputType){
16542 cfg.type = this.inputType;
16544 this.el = ct.createChild(cfg, position);
16546 var type = this.el.dom.type;
16548 if(type == 'password'){
16551 this.el.addClass('x-form-'+type);
16554 this.el.dom.readOnly = true;
16556 if(this.tabIndex !== undefined){
16557 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16560 this.el.addClass([this.fieldClass, this.cls]);
16565 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16566 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16567 * @return {Roo.form.Field} this
16569 applyTo : function(target){
16570 this.allowDomMove = false;
16571 this.el = Roo.get(target);
16572 this.render(this.el.dom.parentNode);
16577 initValue : function(){
16578 if(this.value !== undefined){
16579 this.setValue(this.value);
16580 }else if(this.el.dom.value.length > 0){
16581 this.setValue(this.el.dom.value);
16586 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16587 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16589 isDirty : function() {
16590 if(this.disabled) {
16593 return String(this.getValue()) !== String(this.originalValue);
16597 * stores the current value in loadedValue
16599 resetHasChanged : function()
16601 this.loadedValue = String(this.getValue());
16604 * checks the current value against the 'loaded' value.
16605 * Note - will return false if 'resetHasChanged' has not been called first.
16607 hasChanged : function()
16609 if(this.disabled || this.readOnly) {
16612 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16618 afterRender : function(){
16619 Roo.form.Field.superclass.afterRender.call(this);
16624 fireKey : function(e){
16625 //Roo.log('field ' + e.getKey());
16626 if(e.isNavKeyPress()){
16627 this.fireEvent("specialkey", this, e);
16632 * Resets the current field value to the originally loaded value and clears any validation messages
16634 reset : function(){
16635 this.setValue(this.resetValue);
16636 this.originalValue = this.getValue();
16637 this.clearInvalid();
16641 initEvents : function(){
16642 // safari killled keypress - so keydown is now used..
16643 this.el.on("keydown" , this.fireKey, this);
16644 this.el.on("focus", this.onFocus, this);
16645 this.el.on("blur", this.onBlur, this);
16646 this.el.relayEvent('keyup', this);
16648 // reference to original value for reset
16649 this.originalValue = this.getValue();
16650 this.resetValue = this.getValue();
16654 onFocus : function(){
16655 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16656 this.el.addClass(this.focusClass);
16658 if(!this.hasFocus){
16659 this.hasFocus = true;
16660 this.startValue = this.getValue();
16661 this.fireEvent("focus", this);
16665 beforeBlur : Roo.emptyFn,
16668 onBlur : function(){
16670 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16671 this.el.removeClass(this.focusClass);
16673 this.hasFocus = false;
16674 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16677 var v = this.getValue();
16678 if(String(v) !== String(this.startValue)){
16679 this.fireEvent('change', this, v, this.startValue);
16681 this.fireEvent("blur", this);
16685 * Returns whether or not the field value is currently valid
16686 * @param {Boolean} preventMark True to disable marking the field invalid
16687 * @return {Boolean} True if the value is valid, else false
16689 isValid : function(preventMark){
16693 var restore = this.preventMark;
16694 this.preventMark = preventMark === true;
16695 var v = this.validateValue(this.processValue(this.getRawValue()));
16696 this.preventMark = restore;
16701 * Validates the field value
16702 * @return {Boolean} True if the value is valid, else false
16704 validate : function(){
16705 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16706 this.clearInvalid();
16712 processValue : function(value){
16717 // Subclasses should provide the validation implementation by overriding this
16718 validateValue : function(value){
16723 * Mark this field as invalid
16724 * @param {String} msg The validation message
16726 markInvalid : function(msg){
16727 if(!this.rendered || this.preventMark){ // not rendered
16731 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16733 obj.el.addClass(this.invalidClass);
16734 msg = msg || this.invalidText;
16735 switch(this.msgTarget){
16737 obj.el.dom.qtip = msg;
16738 obj.el.dom.qclass = 'x-form-invalid-tip';
16739 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16740 Roo.QuickTips.enable();
16744 this.el.dom.title = msg;
16748 var elp = this.el.findParent('.x-form-element', 5, true);
16749 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16750 this.errorEl.setWidth(elp.getWidth(true)-20);
16752 this.errorEl.update(msg);
16753 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16756 if(!this.errorIcon){
16757 var elp = this.el.findParent('.x-form-element', 5, true);
16758 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16760 this.alignErrorIcon();
16761 this.errorIcon.dom.qtip = msg;
16762 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16763 this.errorIcon.show();
16764 this.on('resize', this.alignErrorIcon, this);
16767 var t = Roo.getDom(this.msgTarget);
16769 t.style.display = this.msgDisplay;
16772 this.fireEvent('invalid', this, msg);
16776 alignErrorIcon : function(){
16777 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16781 * Clear any invalid styles/messages for this field
16783 clearInvalid : function(){
16784 if(!this.rendered || this.preventMark){ // not rendered
16787 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16789 obj.el.removeClass(this.invalidClass);
16790 switch(this.msgTarget){
16792 obj.el.dom.qtip = '';
16795 this.el.dom.title = '';
16799 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16803 if(this.errorIcon){
16804 this.errorIcon.dom.qtip = '';
16805 this.errorIcon.hide();
16806 this.un('resize', this.alignErrorIcon, this);
16810 var t = Roo.getDom(this.msgTarget);
16812 t.style.display = 'none';
16815 this.fireEvent('valid', this);
16819 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16820 * @return {Mixed} value The field value
16822 getRawValue : function(){
16823 var v = this.el.getValue();
16829 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16830 * @return {Mixed} value The field value
16832 getValue : function(){
16833 var v = this.el.getValue();
16839 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16840 * @param {Mixed} value The value to set
16842 setRawValue : function(v){
16843 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16847 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16848 * @param {Mixed} value The value to set
16850 setValue : function(v){
16853 this.el.dom.value = (v === null || v === undefined ? '' : v);
16858 adjustSize : function(w, h){
16859 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16860 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16864 adjustWidth : function(tag, w){
16865 tag = tag.toLowerCase();
16866 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16867 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16868 if(tag == 'input'){
16871 if(tag == 'textarea'){
16874 }else if(Roo.isOpera){
16875 if(tag == 'input'){
16878 if(tag == 'textarea'){
16888 // anything other than normal should be considered experimental
16889 Roo.form.Field.msgFx = {
16891 show: function(msgEl, f){
16892 msgEl.setDisplayed('block');
16895 hide : function(msgEl, f){
16896 msgEl.setDisplayed(false).update('');
16901 show: function(msgEl, f){
16902 msgEl.slideIn('t', {stopFx:true});
16905 hide : function(msgEl, f){
16906 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16911 show: function(msgEl, f){
16912 msgEl.fixDisplay();
16913 msgEl.alignTo(f.el, 'tl-tr');
16914 msgEl.slideIn('l', {stopFx:true});
16917 hide : function(msgEl, f){
16918 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16923 * Ext JS Library 1.1.1
16924 * Copyright(c) 2006-2007, Ext JS, LLC.
16926 * Originally Released Under LGPL - original licence link has changed is not relivant.
16929 * <script type="text/javascript">
16934 * @class Roo.form.TextField
16935 * @extends Roo.form.Field
16936 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16937 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16939 * Creates a new TextField
16940 * @param {Object} config Configuration options
16942 Roo.form.TextField = function(config){
16943 Roo.form.TextField.superclass.constructor.call(this, config);
16947 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16948 * according to the default logic, but this event provides a hook for the developer to apply additional
16949 * logic at runtime to resize the field if needed.
16950 * @param {Roo.form.Field} this This text field
16951 * @param {Number} width The new field width
16957 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16959 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16963 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16967 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16971 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16975 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16979 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16981 disableKeyFilter : false,
16983 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16987 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16991 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16993 maxLength : Number.MAX_VALUE,
16995 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16997 minLengthText : "The minimum length for this field is {0}",
16999 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17001 maxLengthText : "The maximum length for this field is {0}",
17003 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17005 selectOnFocus : false,
17007 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
17009 allowLeadingSpace : false,
17011 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17013 blankText : "This field is required",
17015 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17016 * If available, this function will be called only after the basic validators all return true, and will be passed the
17017 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17021 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17022 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17023 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
17027 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17031 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17037 initEvents : function()
17039 if (this.emptyText) {
17040 this.el.attr('placeholder', this.emptyText);
17043 Roo.form.TextField.superclass.initEvents.call(this);
17044 if(this.validationEvent == 'keyup'){
17045 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17046 this.el.on('keyup', this.filterValidation, this);
17048 else if(this.validationEvent !== false){
17049 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17052 if(this.selectOnFocus){
17053 this.on("focus", this.preFocus, this);
17055 if (!this.allowLeadingSpace) {
17056 this.on('blur', this.cleanLeadingSpace, this);
17059 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17060 this.el.on("keypress", this.filterKeys, this);
17063 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
17064 this.el.on("click", this.autoSize, this);
17066 if(this.el.is('input[type=password]') && Roo.isSafari){
17067 this.el.on('keydown', this.SafariOnKeyDown, this);
17071 processValue : function(value){
17072 if(this.stripCharsRe){
17073 var newValue = value.replace(this.stripCharsRe, '');
17074 if(newValue !== value){
17075 this.setRawValue(newValue);
17082 filterValidation : function(e){
17083 if(!e.isNavKeyPress()){
17084 this.validationTask.delay(this.validationDelay);
17089 onKeyUp : function(e){
17090 if(!e.isNavKeyPress()){
17094 // private - clean the leading white space
17095 cleanLeadingSpace : function(e)
17097 if ( this.inputType == 'file') {
17101 this.setValue((this.getValue() + '').replace(/^\s+/,''));
17104 * Resets the current field value to the originally-loaded value and clears any validation messages.
17107 reset : function(){
17108 Roo.form.TextField.superclass.reset.call(this);
17112 preFocus : function(){
17114 if(this.selectOnFocus){
17115 this.el.dom.select();
17121 filterKeys : function(e){
17122 var k = e.getKey();
17123 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17126 var c = e.getCharCode(), cc = String.fromCharCode(c);
17127 if(Roo.isIE && (e.isSpecialKey() || !cc)){
17130 if(!this.maskRe.test(cc)){
17135 setValue : function(v){
17137 Roo.form.TextField.superclass.setValue.apply(this, arguments);
17143 * Validates a value according to the field's validation rules and marks the field as invalid
17144 * if the validation fails
17145 * @param {Mixed} value The value to validate
17146 * @return {Boolean} True if the value is valid, else false
17148 validateValue : function(value){
17149 if(value.length < 1) { // if it's blank
17150 if(this.allowBlank){
17151 this.clearInvalid();
17154 this.markInvalid(this.blankText);
17158 if(value.length < this.minLength){
17159 this.markInvalid(String.format(this.minLengthText, this.minLength));
17162 if(value.length > this.maxLength){
17163 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17167 var vt = Roo.form.VTypes;
17168 if(!vt[this.vtype](value, this)){
17169 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17173 if(typeof this.validator == "function"){
17174 var msg = this.validator(value);
17176 this.markInvalid(msg);
17180 if(this.regex && !this.regex.test(value)){
17181 this.markInvalid(this.regexText);
17188 * Selects text in this field
17189 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17190 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17192 selectText : function(start, end){
17193 var v = this.getRawValue();
17195 start = start === undefined ? 0 : start;
17196 end = end === undefined ? v.length : end;
17197 var d = this.el.dom;
17198 if(d.setSelectionRange){
17199 d.setSelectionRange(start, end);
17200 }else if(d.createTextRange){
17201 var range = d.createTextRange();
17202 range.moveStart("character", start);
17203 range.moveEnd("character", v.length-end);
17210 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17211 * This only takes effect if grow = true, and fires the autosize event.
17213 autoSize : function(){
17214 if(!this.grow || !this.rendered){
17218 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17221 var v = el.dom.value;
17222 var d = document.createElement('div');
17223 d.appendChild(document.createTextNode(v));
17227 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17228 this.el.setWidth(w);
17229 this.fireEvent("autosize", this, w);
17233 SafariOnKeyDown : function(event)
17235 // this is a workaround for a password hang bug on chrome/ webkit.
17237 var isSelectAll = false;
17239 if(this.el.dom.selectionEnd > 0){
17240 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17242 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17243 event.preventDefault();
17248 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17250 event.preventDefault();
17251 // this is very hacky as keydown always get's upper case.
17253 var cc = String.fromCharCode(event.getCharCode());
17256 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
17264 * Ext JS Library 1.1.1
17265 * Copyright(c) 2006-2007, Ext JS, LLC.
17267 * Originally Released Under LGPL - original licence link has changed is not relivant.
17270 * <script type="text/javascript">
17274 * @class Roo.form.Hidden
17275 * @extends Roo.form.TextField
17276 * Simple Hidden element used on forms
17278 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17281 * Creates a new Hidden form element.
17282 * @param {Object} config Configuration options
17287 // easy hidden field...
17288 Roo.form.Hidden = function(config){
17289 Roo.form.Hidden.superclass.constructor.call(this, config);
17292 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17294 inputType: 'hidden',
17297 labelSeparator: '',
17299 itemCls : 'x-form-item-display-none'
17307 * Ext JS Library 1.1.1
17308 * Copyright(c) 2006-2007, Ext JS, LLC.
17310 * Originally Released Under LGPL - original licence link has changed is not relivant.
17313 * <script type="text/javascript">
17317 * @class Roo.form.TriggerField
17318 * @extends Roo.form.TextField
17319 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17320 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17321 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17322 * for which you can provide a custom implementation. For example:
17324 var trigger = new Roo.form.TriggerField();
17325 trigger.onTriggerClick = myTriggerFn;
17326 trigger.applyTo('my-field');
17329 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17330 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17331 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17332 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17334 * Create a new TriggerField.
17335 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17336 * to the base TextField)
17338 Roo.form.TriggerField = function(config){
17339 this.mimicing = false;
17340 Roo.form.TriggerField.superclass.constructor.call(this, config);
17343 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17345 * @cfg {String} triggerClass A CSS class to apply to the trigger
17348 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17349 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17351 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17353 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17357 /** @cfg {Boolean} grow @hide */
17358 /** @cfg {Number} growMin @hide */
17359 /** @cfg {Number} growMax @hide */
17365 autoSize: Roo.emptyFn,
17369 deferHeight : true,
17372 actionMode : 'wrap',
17374 onResize : function(w, h){
17375 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17376 if(typeof w == 'number'){
17377 var x = w - this.trigger.getWidth();
17378 this.el.setWidth(this.adjustWidth('input', x));
17379 this.trigger.setStyle('left', x+'px');
17384 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17387 getResizeEl : function(){
17392 getPositionEl : function(){
17397 alignErrorIcon : function(){
17398 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17402 onRender : function(ct, position){
17403 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17404 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17405 this.trigger = this.wrap.createChild(this.triggerConfig ||
17406 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17407 if(this.hideTrigger){
17408 this.trigger.setDisplayed(false);
17410 this.initTrigger();
17412 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17417 initTrigger : function(){
17418 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17419 this.trigger.addClassOnOver('x-form-trigger-over');
17420 this.trigger.addClassOnClick('x-form-trigger-click');
17424 onDestroy : function(){
17426 this.trigger.removeAllListeners();
17427 this.trigger.remove();
17430 this.wrap.remove();
17432 Roo.form.TriggerField.superclass.onDestroy.call(this);
17436 onFocus : function(){
17437 Roo.form.TriggerField.superclass.onFocus.call(this);
17438 if(!this.mimicing){
17439 this.wrap.addClass('x-trigger-wrap-focus');
17440 this.mimicing = true;
17441 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17442 if(this.monitorTab){
17443 this.el.on("keydown", this.checkTab, this);
17449 checkTab : function(e){
17450 if(e.getKey() == e.TAB){
17451 this.triggerBlur();
17456 onBlur : function(){
17461 mimicBlur : function(e, t){
17462 if(!this.wrap.contains(t) && this.validateBlur()){
17463 this.triggerBlur();
17468 triggerBlur : function(){
17469 this.mimicing = false;
17470 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17471 if(this.monitorTab){
17472 this.el.un("keydown", this.checkTab, this);
17474 this.wrap.removeClass('x-trigger-wrap-focus');
17475 Roo.form.TriggerField.superclass.onBlur.call(this);
17479 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17480 validateBlur : function(e, t){
17485 onDisable : function(){
17486 Roo.form.TriggerField.superclass.onDisable.call(this);
17488 this.wrap.addClass('x-item-disabled');
17493 onEnable : function(){
17494 Roo.form.TriggerField.superclass.onEnable.call(this);
17496 this.wrap.removeClass('x-item-disabled');
17501 onShow : function(){
17502 var ae = this.getActionEl();
17505 ae.dom.style.display = '';
17506 ae.dom.style.visibility = 'visible';
17512 onHide : function(){
17513 var ae = this.getActionEl();
17514 ae.dom.style.display = 'none';
17518 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17519 * by an implementing function.
17521 * @param {EventObject} e
17523 onTriggerClick : Roo.emptyFn
17526 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17527 // to be extended by an implementing class. For an example of implementing this class, see the custom
17528 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17529 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17530 initComponent : function(){
17531 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17533 this.triggerConfig = {
17534 tag:'span', cls:'x-form-twin-triggers', cn:[
17535 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17536 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17540 getTrigger : function(index){
17541 return this.triggers[index];
17544 initTrigger : function(){
17545 var ts = this.trigger.select('.x-form-trigger', true);
17546 this.wrap.setStyle('overflow', 'hidden');
17547 var triggerField = this;
17548 ts.each(function(t, all, index){
17549 t.hide = function(){
17550 var w = triggerField.wrap.getWidth();
17551 this.dom.style.display = 'none';
17552 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17554 t.show = function(){
17555 var w = triggerField.wrap.getWidth();
17556 this.dom.style.display = '';
17557 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17559 var triggerIndex = 'Trigger'+(index+1);
17561 if(this['hide'+triggerIndex]){
17562 t.dom.style.display = 'none';
17564 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17565 t.addClassOnOver('x-form-trigger-over');
17566 t.addClassOnClick('x-form-trigger-click');
17568 this.triggers = ts.elements;
17571 onTrigger1Click : Roo.emptyFn,
17572 onTrigger2Click : Roo.emptyFn
17575 * Ext JS Library 1.1.1
17576 * Copyright(c) 2006-2007, Ext JS, LLC.
17578 * Originally Released Under LGPL - original licence link has changed is not relivant.
17581 * <script type="text/javascript">
17585 * @class Roo.form.TextArea
17586 * @extends Roo.form.TextField
17587 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17588 * support for auto-sizing.
17590 * Creates a new TextArea
17591 * @param {Object} config Configuration options
17593 Roo.form.TextArea = function(config){
17594 Roo.form.TextArea.superclass.constructor.call(this, config);
17595 // these are provided exchanges for backwards compat
17596 // minHeight/maxHeight were replaced by growMin/growMax to be
17597 // compatible with TextField growing config values
17598 if(this.minHeight !== undefined){
17599 this.growMin = this.minHeight;
17601 if(this.maxHeight !== undefined){
17602 this.growMax = this.maxHeight;
17606 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17608 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17612 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17616 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17617 * in the field (equivalent to setting overflow: hidden, defaults to false)
17619 preventScrollbars: false,
17621 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17622 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17626 onRender : function(ct, position){
17628 this.defaultAutoCreate = {
17630 style:"width:300px;height:60px;",
17631 autocomplete: "new-password"
17634 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17636 this.textSizeEl = Roo.DomHelper.append(document.body, {
17637 tag: "pre", cls: "x-form-grow-sizer"
17639 if(this.preventScrollbars){
17640 this.el.setStyle("overflow", "hidden");
17642 this.el.setHeight(this.growMin);
17646 onDestroy : function(){
17647 if(this.textSizeEl){
17648 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17650 Roo.form.TextArea.superclass.onDestroy.call(this);
17654 onKeyUp : function(e){
17655 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17661 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17662 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17664 autoSize : function(){
17665 if(!this.grow || !this.textSizeEl){
17669 var v = el.dom.value;
17670 var ts = this.textSizeEl;
17673 ts.appendChild(document.createTextNode(v));
17676 Roo.fly(ts).setWidth(this.el.getWidth());
17678 v = "  ";
17681 v = v.replace(/\n/g, '<p> </p>');
17683 v += " \n ";
17686 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17687 if(h != this.lastHeight){
17688 this.lastHeight = h;
17689 this.el.setHeight(h);
17690 this.fireEvent("autosize", this, h);
17695 * Ext JS Library 1.1.1
17696 * Copyright(c) 2006-2007, Ext JS, LLC.
17698 * Originally Released Under LGPL - original licence link has changed is not relivant.
17701 * <script type="text/javascript">
17706 * @class Roo.form.NumberField
17707 * @extends Roo.form.TextField
17708 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17710 * Creates a new NumberField
17711 * @param {Object} config Configuration options
17713 Roo.form.NumberField = function(config){
17714 Roo.form.NumberField.superclass.constructor.call(this, config);
17717 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17719 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17721 fieldClass: "x-form-field x-form-num-field",
17723 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17725 allowDecimals : true,
17727 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17729 decimalSeparator : ".",
17731 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17733 decimalPrecision : 2,
17735 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17737 allowNegative : true,
17739 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17741 minValue : Number.NEGATIVE_INFINITY,
17743 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17745 maxValue : Number.MAX_VALUE,
17747 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17749 minText : "The minimum value for this field is {0}",
17751 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17753 maxText : "The maximum value for this field is {0}",
17755 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17756 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17758 nanText : "{0} is not a valid number",
17761 initEvents : function(){
17762 Roo.form.NumberField.superclass.initEvents.call(this);
17763 var allowed = "0123456789";
17764 if(this.allowDecimals){
17765 allowed += this.decimalSeparator;
17767 if(this.allowNegative){
17770 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17771 var keyPress = function(e){
17772 var k = e.getKey();
17773 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17776 var c = e.getCharCode();
17777 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17781 this.el.on("keypress", keyPress, this);
17785 validateValue : function(value){
17786 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17789 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17792 var num = this.parseValue(value);
17794 this.markInvalid(String.format(this.nanText, value));
17797 if(num < this.minValue){
17798 this.markInvalid(String.format(this.minText, this.minValue));
17801 if(num > this.maxValue){
17802 this.markInvalid(String.format(this.maxText, this.maxValue));
17808 getValue : function(){
17809 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17813 parseValue : function(value){
17814 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17815 return isNaN(value) ? '' : value;
17819 fixPrecision : function(value){
17820 var nan = isNaN(value);
17821 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17822 return nan ? '' : value;
17824 return parseFloat(value).toFixed(this.decimalPrecision);
17827 setValue : function(v){
17828 v = this.fixPrecision(v);
17829 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17833 decimalPrecisionFcn : function(v){
17834 return Math.floor(v);
17837 beforeBlur : function(){
17838 var v = this.parseValue(this.getRawValue());
17845 * Ext JS Library 1.1.1
17846 * Copyright(c) 2006-2007, Ext JS, LLC.
17848 * Originally Released Under LGPL - original licence link has changed is not relivant.
17851 * <script type="text/javascript">
17855 * @class Roo.form.DateField
17856 * @extends Roo.form.TriggerField
17857 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17859 * Create a new DateField
17860 * @param {Object} config
17862 Roo.form.DateField = function(config)
17864 Roo.form.DateField.superclass.constructor.call(this, config);
17870 * Fires when a date is selected
17871 * @param {Roo.form.DateField} combo This combo box
17872 * @param {Date} date The date selected
17879 if(typeof this.minValue == "string") {
17880 this.minValue = this.parseDate(this.minValue);
17882 if(typeof this.maxValue == "string") {
17883 this.maxValue = this.parseDate(this.maxValue);
17885 this.ddMatch = null;
17886 if(this.disabledDates){
17887 var dd = this.disabledDates;
17889 for(var i = 0; i < dd.length; i++){
17891 if(i != dd.length-1) {
17895 this.ddMatch = new RegExp(re + ")");
17899 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17901 * @cfg {String} format
17902 * The default date format string which can be overriden for localization support. The format must be
17903 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17907 * @cfg {String} altFormats
17908 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17909 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17911 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17913 * @cfg {Array} disabledDays
17914 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17916 disabledDays : null,
17918 * @cfg {String} disabledDaysText
17919 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17921 disabledDaysText : "Disabled",
17923 * @cfg {Array} disabledDates
17924 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17925 * expression so they are very powerful. Some examples:
17927 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17928 * <li>["03/08", "09/16"] would disable those days for every year</li>
17929 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17930 * <li>["03/../2006"] would disable every day in March 2006</li>
17931 * <li>["^03"] would disable every day in every March</li>
17933 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17934 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17936 disabledDates : null,
17938 * @cfg {String} disabledDatesText
17939 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17941 disabledDatesText : "Disabled",
17943 * @cfg {Date/String} minValue
17944 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17945 * valid format (defaults to null).
17949 * @cfg {Date/String} maxValue
17950 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17951 * valid format (defaults to null).
17955 * @cfg {String} minText
17956 * The error text to display when the date in the cell is before minValue (defaults to
17957 * 'The date in this field must be after {minValue}').
17959 minText : "The date in this field must be equal to or after {0}",
17961 * @cfg {String} maxText
17962 * The error text to display when the date in the cell is after maxValue (defaults to
17963 * 'The date in this field must be before {maxValue}').
17965 maxText : "The date in this field must be equal to or before {0}",
17967 * @cfg {String} invalidText
17968 * The error text to display when the date in the field is invalid (defaults to
17969 * '{value} is not a valid date - it must be in the format {format}').
17971 invalidText : "{0} is not a valid date - it must be in the format {1}",
17973 * @cfg {String} triggerClass
17974 * An additional CSS class used to style the trigger button. The trigger will always get the
17975 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17976 * which displays a calendar icon).
17978 triggerClass : 'x-form-date-trigger',
17982 * @cfg {Boolean} useIso
17983 * if enabled, then the date field will use a hidden field to store the
17984 * real value as iso formated date. default (false)
17988 * @cfg {String/Object} autoCreate
17989 * A DomHelper element spec, or true for a default element spec (defaults to
17990 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17993 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17996 hiddenField: false,
17998 onRender : function(ct, position)
18000 Roo.form.DateField.superclass.onRender.call(this, ct, position);
18002 //this.el.dom.removeAttribute('name');
18003 Roo.log("Changing name?");
18004 this.el.dom.setAttribute('name', this.name + '____hidden___' );
18005 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18007 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18008 // prevent input submission
18009 this.hiddenName = this.name;
18016 validateValue : function(value)
18018 value = this.formatDate(value);
18019 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18020 Roo.log('super failed');
18023 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18026 var svalue = value;
18027 value = this.parseDate(value);
18029 Roo.log('parse date failed' + svalue);
18030 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18033 var time = value.getTime();
18034 if(this.minValue && time < this.minValue.getTime()){
18035 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18038 if(this.maxValue && time > this.maxValue.getTime()){
18039 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18042 if(this.disabledDays){
18043 var day = value.getDay();
18044 for(var i = 0; i < this.disabledDays.length; i++) {
18045 if(day === this.disabledDays[i]){
18046 this.markInvalid(this.disabledDaysText);
18051 var fvalue = this.formatDate(value);
18052 if(this.ddMatch && this.ddMatch.test(fvalue)){
18053 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18060 // Provides logic to override the default TriggerField.validateBlur which just returns true
18061 validateBlur : function(){
18062 return !this.menu || !this.menu.isVisible();
18065 getName: function()
18067 // returns hidden if it's set..
18068 if (!this.rendered) {return ''};
18069 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
18074 * Returns the current date value of the date field.
18075 * @return {Date} The date value
18077 getValue : function(){
18079 return this.hiddenField ?
18080 this.hiddenField.value :
18081 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18085 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18086 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18087 * (the default format used is "m/d/y").
18090 //All of these calls set the same date value (May 4, 2006)
18092 //Pass a date object:
18093 var dt = new Date('5/4/06');
18094 dateField.setValue(dt);
18096 //Pass a date string (default format):
18097 dateField.setValue('5/4/06');
18099 //Pass a date string (custom format):
18100 dateField.format = 'Y-m-d';
18101 dateField.setValue('2006-5-4');
18103 * @param {String/Date} date The date or valid date string
18105 setValue : function(date){
18106 if (this.hiddenField) {
18107 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18109 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18110 // make sure the value field is always stored as a date..
18111 this.value = this.parseDate(date);
18117 parseDate : function(value){
18118 if(!value || value instanceof Date){
18121 var v = Date.parseDate(value, this.format);
18122 if (!v && this.useIso) {
18123 v = Date.parseDate(value, 'Y-m-d');
18125 if(!v && this.altFormats){
18126 if(!this.altFormatsArray){
18127 this.altFormatsArray = this.altFormats.split("|");
18129 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18130 v = Date.parseDate(value, this.altFormatsArray[i]);
18137 formatDate : function(date, fmt){
18138 return (!date || !(date instanceof Date)) ?
18139 date : date.dateFormat(fmt || this.format);
18144 select: function(m, d){
18147 this.fireEvent('select', this, d);
18149 show : function(){ // retain focus styling
18153 this.focus.defer(10, this);
18154 var ml = this.menuListeners;
18155 this.menu.un("select", ml.select, this);
18156 this.menu.un("show", ml.show, this);
18157 this.menu.un("hide", ml.hide, this);
18162 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18163 onTriggerClick : function(){
18167 if(this.menu == null){
18168 this.menu = new Roo.menu.DateMenu();
18170 Roo.apply(this.menu.picker, {
18171 showClear: this.allowBlank,
18172 minDate : this.minValue,
18173 maxDate : this.maxValue,
18174 disabledDatesRE : this.ddMatch,
18175 disabledDatesText : this.disabledDatesText,
18176 disabledDays : this.disabledDays,
18177 disabledDaysText : this.disabledDaysText,
18178 format : this.useIso ? 'Y-m-d' : this.format,
18179 minText : String.format(this.minText, this.formatDate(this.minValue)),
18180 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18182 this.menu.on(Roo.apply({}, this.menuListeners, {
18185 this.menu.picker.setValue(this.getValue() || new Date());
18186 this.menu.show(this.el, "tl-bl?");
18189 beforeBlur : function(){
18190 var v = this.parseDate(this.getRawValue());
18200 isDirty : function() {
18201 if(this.disabled) {
18205 if(typeof(this.startValue) === 'undefined'){
18209 return String(this.getValue()) !== String(this.startValue);
18213 cleanLeadingSpace : function(e)
18220 * Ext JS Library 1.1.1
18221 * Copyright(c) 2006-2007, Ext JS, LLC.
18223 * Originally Released Under LGPL - original licence link has changed is not relivant.
18226 * <script type="text/javascript">
18230 * @class Roo.form.MonthField
18231 * @extends Roo.form.TriggerField
18232 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18234 * Create a new MonthField
18235 * @param {Object} config
18237 Roo.form.MonthField = function(config){
18239 Roo.form.MonthField.superclass.constructor.call(this, config);
18245 * Fires when a date is selected
18246 * @param {Roo.form.MonthFieeld} combo This combo box
18247 * @param {Date} date The date selected
18254 if(typeof this.minValue == "string") {
18255 this.minValue = this.parseDate(this.minValue);
18257 if(typeof this.maxValue == "string") {
18258 this.maxValue = this.parseDate(this.maxValue);
18260 this.ddMatch = null;
18261 if(this.disabledDates){
18262 var dd = this.disabledDates;
18264 for(var i = 0; i < dd.length; i++){
18266 if(i != dd.length-1) {
18270 this.ddMatch = new RegExp(re + ")");
18274 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
18276 * @cfg {String} format
18277 * The default date format string which can be overriden for localization support. The format must be
18278 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18282 * @cfg {String} altFormats
18283 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18284 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18286 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18288 * @cfg {Array} disabledDays
18289 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18291 disabledDays : [0,1,2,3,4,5,6],
18293 * @cfg {String} disabledDaysText
18294 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18296 disabledDaysText : "Disabled",
18298 * @cfg {Array} disabledDates
18299 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18300 * expression so they are very powerful. Some examples:
18302 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18303 * <li>["03/08", "09/16"] would disable those days for every year</li>
18304 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18305 * <li>["03/../2006"] would disable every day in March 2006</li>
18306 * <li>["^03"] would disable every day in every March</li>
18308 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18309 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18311 disabledDates : null,
18313 * @cfg {String} disabledDatesText
18314 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18316 disabledDatesText : "Disabled",
18318 * @cfg {Date/String} minValue
18319 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18320 * valid format (defaults to null).
18324 * @cfg {Date/String} maxValue
18325 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18326 * valid format (defaults to null).
18330 * @cfg {String} minText
18331 * The error text to display when the date in the cell is before minValue (defaults to
18332 * 'The date in this field must be after {minValue}').
18334 minText : "The date in this field must be equal to or after {0}",
18336 * @cfg {String} maxTextf
18337 * The error text to display when the date in the cell is after maxValue (defaults to
18338 * 'The date in this field must be before {maxValue}').
18340 maxText : "The date in this field must be equal to or before {0}",
18342 * @cfg {String} invalidText
18343 * The error text to display when the date in the field is invalid (defaults to
18344 * '{value} is not a valid date - it must be in the format {format}').
18346 invalidText : "{0} is not a valid date - it must be in the format {1}",
18348 * @cfg {String} triggerClass
18349 * An additional CSS class used to style the trigger button. The trigger will always get the
18350 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18351 * which displays a calendar icon).
18353 triggerClass : 'x-form-date-trigger',
18357 * @cfg {Boolean} useIso
18358 * if enabled, then the date field will use a hidden field to store the
18359 * real value as iso formated date. default (true)
18363 * @cfg {String/Object} autoCreate
18364 * A DomHelper element spec, or true for a default element spec (defaults to
18365 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18368 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18371 hiddenField: false,
18373 hideMonthPicker : false,
18375 onRender : function(ct, position)
18377 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18379 this.el.dom.removeAttribute('name');
18380 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18382 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18383 // prevent input submission
18384 this.hiddenName = this.name;
18391 validateValue : function(value)
18393 value = this.formatDate(value);
18394 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18397 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18400 var svalue = value;
18401 value = this.parseDate(value);
18403 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18406 var time = value.getTime();
18407 if(this.minValue && time < this.minValue.getTime()){
18408 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18411 if(this.maxValue && time > this.maxValue.getTime()){
18412 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18415 /*if(this.disabledDays){
18416 var day = value.getDay();
18417 for(var i = 0; i < this.disabledDays.length; i++) {
18418 if(day === this.disabledDays[i]){
18419 this.markInvalid(this.disabledDaysText);
18425 var fvalue = this.formatDate(value);
18426 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18427 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18435 // Provides logic to override the default TriggerField.validateBlur which just returns true
18436 validateBlur : function(){
18437 return !this.menu || !this.menu.isVisible();
18441 * Returns the current date value of the date field.
18442 * @return {Date} The date value
18444 getValue : function(){
18448 return this.hiddenField ?
18449 this.hiddenField.value :
18450 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18454 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18455 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18456 * (the default format used is "m/d/y").
18459 //All of these calls set the same date value (May 4, 2006)
18461 //Pass a date object:
18462 var dt = new Date('5/4/06');
18463 monthField.setValue(dt);
18465 //Pass a date string (default format):
18466 monthField.setValue('5/4/06');
18468 //Pass a date string (custom format):
18469 monthField.format = 'Y-m-d';
18470 monthField.setValue('2006-5-4');
18472 * @param {String/Date} date The date or valid date string
18474 setValue : function(date){
18475 Roo.log('month setValue' + date);
18476 // can only be first of month..
18478 var val = this.parseDate(date);
18480 if (this.hiddenField) {
18481 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18483 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18484 this.value = this.parseDate(date);
18488 parseDate : function(value){
18489 if(!value || value instanceof Date){
18490 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18493 var v = Date.parseDate(value, this.format);
18494 if (!v && this.useIso) {
18495 v = Date.parseDate(value, 'Y-m-d');
18499 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18503 if(!v && this.altFormats){
18504 if(!this.altFormatsArray){
18505 this.altFormatsArray = this.altFormats.split("|");
18507 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18508 v = Date.parseDate(value, this.altFormatsArray[i]);
18515 formatDate : function(date, fmt){
18516 return (!date || !(date instanceof Date)) ?
18517 date : date.dateFormat(fmt || this.format);
18522 select: function(m, d){
18524 this.fireEvent('select', this, d);
18526 show : function(){ // retain focus styling
18530 this.focus.defer(10, this);
18531 var ml = this.menuListeners;
18532 this.menu.un("select", ml.select, this);
18533 this.menu.un("show", ml.show, this);
18534 this.menu.un("hide", ml.hide, this);
18538 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18539 onTriggerClick : function(){
18543 if(this.menu == null){
18544 this.menu = new Roo.menu.DateMenu();
18548 Roo.apply(this.menu.picker, {
18550 showClear: this.allowBlank,
18551 minDate : this.minValue,
18552 maxDate : this.maxValue,
18553 disabledDatesRE : this.ddMatch,
18554 disabledDatesText : this.disabledDatesText,
18556 format : this.useIso ? 'Y-m-d' : this.format,
18557 minText : String.format(this.minText, this.formatDate(this.minValue)),
18558 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18561 this.menu.on(Roo.apply({}, this.menuListeners, {
18569 // hide month picker get's called when we called by 'before hide';
18571 var ignorehide = true;
18572 p.hideMonthPicker = function(disableAnim){
18576 if(this.monthPicker){
18577 Roo.log("hideMonthPicker called");
18578 if(disableAnim === true){
18579 this.monthPicker.hide();
18581 this.monthPicker.slideOut('t', {duration:.2});
18582 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18583 p.fireEvent("select", this, this.value);
18589 Roo.log('picker set value');
18590 Roo.log(this.getValue());
18591 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18592 m.show(this.el, 'tl-bl?');
18593 ignorehide = false;
18594 // this will trigger hideMonthPicker..
18597 // hidden the day picker
18598 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18604 p.showMonthPicker.defer(100, p);
18610 beforeBlur : function(){
18611 var v = this.parseDate(this.getRawValue());
18617 /** @cfg {Boolean} grow @hide */
18618 /** @cfg {Number} growMin @hide */
18619 /** @cfg {Number} growMax @hide */
18626 * Ext JS Library 1.1.1
18627 * Copyright(c) 2006-2007, Ext JS, LLC.
18629 * Originally Released Under LGPL - original licence link has changed is not relivant.
18632 * <script type="text/javascript">
18637 * @class Roo.form.ComboBox
18638 * @extends Roo.form.TriggerField
18639 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18641 * Create a new ComboBox.
18642 * @param {Object} config Configuration options
18644 Roo.form.ComboBox = function(config){
18645 Roo.form.ComboBox.superclass.constructor.call(this, config);
18649 * Fires when the dropdown list is expanded
18650 * @param {Roo.form.ComboBox} combo This combo box
18655 * Fires when the dropdown list is collapsed
18656 * @param {Roo.form.ComboBox} combo This combo box
18660 * @event beforeselect
18661 * Fires before a list item is selected. Return false to cancel the selection.
18662 * @param {Roo.form.ComboBox} combo This combo box
18663 * @param {Roo.data.Record} record The data record returned from the underlying store
18664 * @param {Number} index The index of the selected item in the dropdown list
18666 'beforeselect' : true,
18669 * Fires when a list item is selected
18670 * @param {Roo.form.ComboBox} combo This combo box
18671 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18672 * @param {Number} index The index of the selected item in the dropdown list
18676 * @event beforequery
18677 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18678 * The event object passed has these properties:
18679 * @param {Roo.form.ComboBox} combo This combo box
18680 * @param {String} query The query
18681 * @param {Boolean} forceAll true to force "all" query
18682 * @param {Boolean} cancel true to cancel the query
18683 * @param {Object} e The query event object
18685 'beforequery': true,
18688 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18689 * @param {Roo.form.ComboBox} combo This combo box
18694 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18695 * @param {Roo.form.ComboBox} combo This combo box
18696 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18702 if(this.transform){
18703 this.allowDomMove = false;
18704 var s = Roo.getDom(this.transform);
18705 if(!this.hiddenName){
18706 this.hiddenName = s.name;
18709 this.mode = 'local';
18710 var d = [], opts = s.options;
18711 for(var i = 0, len = opts.length;i < len; i++){
18713 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18715 this.value = value;
18717 d.push([value, o.text]);
18719 this.store = new Roo.data.SimpleStore({
18721 fields: ['value', 'text'],
18724 this.valueField = 'value';
18725 this.displayField = 'text';
18727 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18728 if(!this.lazyRender){
18729 this.target = true;
18730 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18731 s.parentNode.removeChild(s); // remove it
18732 this.render(this.el.parentNode);
18734 s.parentNode.removeChild(s); // remove it
18739 this.store = Roo.factory(this.store, Roo.data);
18742 this.selectedIndex = -1;
18743 if(this.mode == 'local'){
18744 if(config.queryDelay === undefined){
18745 this.queryDelay = 10;
18747 if(config.minChars === undefined){
18753 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18755 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18758 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18759 * rendering into an Roo.Editor, defaults to false)
18762 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18763 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18766 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18769 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18770 * the dropdown list (defaults to undefined, with no header element)
18774 * @cfg {String/Roo.Template} tpl The template to use to render the output
18778 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18780 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18782 listWidth: undefined,
18784 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18785 * mode = 'remote' or 'text' if mode = 'local')
18787 displayField: undefined,
18789 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18790 * mode = 'remote' or 'value' if mode = 'local').
18791 * Note: use of a valueField requires the user make a selection
18792 * in order for a value to be mapped.
18794 valueField: undefined,
18798 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18799 * field's data value (defaults to the underlying DOM element's name)
18801 hiddenName: undefined,
18803 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18807 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18809 selectedClass: 'x-combo-selected',
18811 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18812 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18813 * which displays a downward arrow icon).
18815 triggerClass : 'x-form-arrow-trigger',
18817 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18821 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18822 * anchor positions (defaults to 'tl-bl')
18824 listAlign: 'tl-bl?',
18826 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18830 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18831 * query specified by the allQuery config option (defaults to 'query')
18833 triggerAction: 'query',
18835 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18836 * (defaults to 4, does not apply if editable = false)
18840 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18841 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18845 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18846 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18850 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18851 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18855 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18856 * when editable = true (defaults to false)
18858 selectOnFocus:false,
18860 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18862 queryParam: 'query',
18864 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18865 * when mode = 'remote' (defaults to 'Loading...')
18867 loadingText: 'Loading...',
18869 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18873 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18877 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18878 * traditional select (defaults to true)
18882 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18886 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18890 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18891 * listWidth has a higher value)
18895 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18896 * allow the user to set arbitrary text into the field (defaults to false)
18898 forceSelection:false,
18900 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18901 * if typeAhead = true (defaults to 250)
18903 typeAheadDelay : 250,
18905 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18906 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18908 valueNotFoundText : undefined,
18910 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18912 blockFocus : false,
18915 * @cfg {Boolean} disableClear Disable showing of clear button.
18917 disableClear : false,
18919 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18921 alwaysQuery : false,
18927 // element that contains real text value.. (when hidden is used..)
18930 onRender : function(ct, position)
18932 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18934 if(this.hiddenName){
18935 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18937 this.hiddenField.value =
18938 this.hiddenValue !== undefined ? this.hiddenValue :
18939 this.value !== undefined ? this.value : '';
18941 // prevent input submission
18942 this.el.dom.removeAttribute('name');
18948 this.el.dom.setAttribute('autocomplete', 'off');
18951 var cls = 'x-combo-list';
18953 this.list = new Roo.Layer({
18954 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18957 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18958 this.list.setWidth(lw);
18959 this.list.swallowEvent('mousewheel');
18960 this.assetHeight = 0;
18963 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18964 this.assetHeight += this.header.getHeight();
18967 this.innerList = this.list.createChild({cls:cls+'-inner'});
18968 this.innerList.on('mouseover', this.onViewOver, this);
18969 this.innerList.on('mousemove', this.onViewMove, this);
18970 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18972 if(this.allowBlank && !this.pageSize && !this.disableClear){
18973 this.footer = this.list.createChild({cls:cls+'-ft'});
18974 this.pageTb = new Roo.Toolbar(this.footer);
18978 this.footer = this.list.createChild({cls:cls+'-ft'});
18979 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18980 {pageSize: this.pageSize});
18984 if (this.pageTb && this.allowBlank && !this.disableClear) {
18986 this.pageTb.add(new Roo.Toolbar.Fill(), {
18987 cls: 'x-btn-icon x-btn-clear',
18989 handler: function()
18992 _this.clearValue();
18993 _this.onSelect(false, -1);
18998 this.assetHeight += this.footer.getHeight();
19003 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19006 this.view = new Roo.View(this.innerList, this.tpl, {
19009 selectedClass: this.selectedClass
19012 this.view.on('click', this.onViewClick, this);
19014 this.store.on('beforeload', this.onBeforeLoad, this);
19015 this.store.on('load', this.onLoad, this);
19016 this.store.on('loadexception', this.onLoadException, this);
19018 if(this.resizable){
19019 this.resizer = new Roo.Resizable(this.list, {
19020 pinned:true, handles:'se'
19022 this.resizer.on('resize', function(r, w, h){
19023 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19024 this.listWidth = w;
19025 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19026 this.restrictHeight();
19028 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19030 if(!this.editable){
19031 this.editable = true;
19032 this.setEditable(false);
19036 if (typeof(this.events.add.listeners) != 'undefined') {
19038 this.addicon = this.wrap.createChild(
19039 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
19041 this.addicon.on('click', function(e) {
19042 this.fireEvent('add', this);
19045 if (typeof(this.events.edit.listeners) != 'undefined') {
19047 this.editicon = this.wrap.createChild(
19048 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
19049 if (this.addicon) {
19050 this.editicon.setStyle('margin-left', '40px');
19052 this.editicon.on('click', function(e) {
19054 // we fire even if inothing is selected..
19055 this.fireEvent('edit', this, this.lastData );
19065 initEvents : function(){
19066 Roo.form.ComboBox.superclass.initEvents.call(this);
19068 this.keyNav = new Roo.KeyNav(this.el, {
19069 "up" : function(e){
19070 this.inKeyMode = true;
19074 "down" : function(e){
19075 if(!this.isExpanded()){
19076 this.onTriggerClick();
19078 this.inKeyMode = true;
19083 "enter" : function(e){
19084 this.onViewClick();
19088 "esc" : function(e){
19092 "tab" : function(e){
19093 this.onViewClick(false);
19094 this.fireEvent("specialkey", this, e);
19100 doRelay : function(foo, bar, hname){
19101 if(hname == 'down' || this.scope.isExpanded()){
19102 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19109 this.queryDelay = Math.max(this.queryDelay || 10,
19110 this.mode == 'local' ? 10 : 250);
19111 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19112 if(this.typeAhead){
19113 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19115 if(this.editable !== false){
19116 this.el.on("keyup", this.onKeyUp, this);
19118 if(this.forceSelection){
19119 this.on('blur', this.doForce, this);
19123 onDestroy : function(){
19125 this.view.setStore(null);
19126 this.view.el.removeAllListeners();
19127 this.view.el.remove();
19128 this.view.purgeListeners();
19131 this.list.destroy();
19134 this.store.un('beforeload', this.onBeforeLoad, this);
19135 this.store.un('load', this.onLoad, this);
19136 this.store.un('loadexception', this.onLoadException, this);
19138 Roo.form.ComboBox.superclass.onDestroy.call(this);
19142 fireKey : function(e){
19143 if(e.isNavKeyPress() && !this.list.isVisible()){
19144 this.fireEvent("specialkey", this, e);
19149 onResize: function(w, h){
19150 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19152 if(typeof w != 'number'){
19153 // we do not handle it!?!?
19156 var tw = this.trigger.getWidth();
19157 tw += this.addicon ? this.addicon.getWidth() : 0;
19158 tw += this.editicon ? this.editicon.getWidth() : 0;
19160 this.el.setWidth( this.adjustWidth('input', x));
19162 this.trigger.setStyle('left', x+'px');
19164 if(this.list && this.listWidth === undefined){
19165 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19166 this.list.setWidth(lw);
19167 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19175 * Allow or prevent the user from directly editing the field text. If false is passed,
19176 * the user will only be able to select from the items defined in the dropdown list. This method
19177 * is the runtime equivalent of setting the 'editable' config option at config time.
19178 * @param {Boolean} value True to allow the user to directly edit the field text
19180 setEditable : function(value){
19181 if(value == this.editable){
19184 this.editable = value;
19186 this.el.dom.setAttribute('readOnly', true);
19187 this.el.on('mousedown', this.onTriggerClick, this);
19188 this.el.addClass('x-combo-noedit');
19190 this.el.dom.setAttribute('readOnly', false);
19191 this.el.un('mousedown', this.onTriggerClick, this);
19192 this.el.removeClass('x-combo-noedit');
19197 onBeforeLoad : function(){
19198 if(!this.hasFocus){
19201 this.innerList.update(this.loadingText ?
19202 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19203 this.restrictHeight();
19204 this.selectedIndex = -1;
19208 onLoad : function(){
19209 if(!this.hasFocus){
19212 if(this.store.getCount() > 0){
19214 this.restrictHeight();
19215 if(this.lastQuery == this.allQuery){
19217 this.el.dom.select();
19219 if(!this.selectByValue(this.value, true)){
19220 this.select(0, true);
19224 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19225 this.taTask.delay(this.typeAheadDelay);
19229 this.onEmptyResults();
19234 onLoadException : function()
19237 Roo.log(this.store.reader.jsonData);
19238 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19239 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19245 onTypeAhead : function(){
19246 if(this.store.getCount() > 0){
19247 var r = this.store.getAt(0);
19248 var newValue = r.data[this.displayField];
19249 var len = newValue.length;
19250 var selStart = this.getRawValue().length;
19251 if(selStart != len){
19252 this.setRawValue(newValue);
19253 this.selectText(selStart, newValue.length);
19259 onSelect : function(record, index){
19260 if(this.fireEvent('beforeselect', this, record, index) !== false){
19261 this.setFromData(index > -1 ? record.data : false);
19263 this.fireEvent('select', this, record, index);
19268 * Returns the currently selected field value or empty string if no value is set.
19269 * @return {String} value The selected value
19271 getValue : function(){
19272 if(this.valueField){
19273 return typeof this.value != 'undefined' ? this.value : '';
19275 return Roo.form.ComboBox.superclass.getValue.call(this);
19279 * Clears any text/value currently set in the field
19281 clearValue : function(){
19282 if(this.hiddenField){
19283 this.hiddenField.value = '';
19286 this.setRawValue('');
19287 this.lastSelectionText = '';
19292 * Sets the specified value into the field. If the value finds a match, the corresponding record text
19293 * will be displayed in the field. If the value does not match the data value of an existing item,
19294 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19295 * Otherwise the field will be blank (although the value will still be set).
19296 * @param {String} value The value to match
19298 setValue : function(v){
19300 if(this.valueField){
19301 var r = this.findRecord(this.valueField, v);
19303 text = r.data[this.displayField];
19304 }else if(this.valueNotFoundText !== undefined){
19305 text = this.valueNotFoundText;
19308 this.lastSelectionText = text;
19309 if(this.hiddenField){
19310 this.hiddenField.value = v;
19312 Roo.form.ComboBox.superclass.setValue.call(this, text);
19316 * @property {Object} the last set data for the element
19321 * Sets the value of the field based on a object which is related to the record format for the store.
19322 * @param {Object} value the value to set as. or false on reset?
19324 setFromData : function(o){
19325 var dv = ''; // display value
19326 var vv = ''; // value value..
19328 if (this.displayField) {
19329 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19331 // this is an error condition!!!
19332 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19335 if(this.valueField){
19336 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19338 if(this.hiddenField){
19339 this.hiddenField.value = vv;
19341 this.lastSelectionText = dv;
19342 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19346 // no hidden field.. - we store the value in 'value', but still display
19347 // display field!!!!
19348 this.lastSelectionText = dv;
19349 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19355 reset : function(){
19356 // overridden so that last data is reset..
19357 this.setValue(this.resetValue);
19358 this.originalValue = this.getValue();
19359 this.clearInvalid();
19360 this.lastData = false;
19362 this.view.clearSelections();
19366 findRecord : function(prop, value){
19368 if(this.store.getCount() > 0){
19369 this.store.each(function(r){
19370 if(r.data[prop] == value){
19380 getName: function()
19382 // returns hidden if it's set..
19383 if (!this.rendered) {return ''};
19384 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19388 onViewMove : function(e, t){
19389 this.inKeyMode = false;
19393 onViewOver : function(e, t){
19394 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19397 var item = this.view.findItemFromChild(t);
19399 var index = this.view.indexOf(item);
19400 this.select(index, false);
19405 onViewClick : function(doFocus)
19407 var index = this.view.getSelectedIndexes()[0];
19408 var r = this.store.getAt(index);
19410 this.onSelect(r, index);
19412 if(doFocus !== false && !this.blockFocus){
19418 restrictHeight : function(){
19419 this.innerList.dom.style.height = '';
19420 var inner = this.innerList.dom;
19421 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19422 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19423 this.list.beginUpdate();
19424 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19425 this.list.alignTo(this.el, this.listAlign);
19426 this.list.endUpdate();
19430 onEmptyResults : function(){
19435 * Returns true if the dropdown list is expanded, else false.
19437 isExpanded : function(){
19438 return this.list.isVisible();
19442 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19443 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19444 * @param {String} value The data value of the item to select
19445 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19446 * selected item if it is not currently in view (defaults to true)
19447 * @return {Boolean} True if the value matched an item in the list, else false
19449 selectByValue : function(v, scrollIntoView){
19450 if(v !== undefined && v !== null){
19451 var r = this.findRecord(this.valueField || this.displayField, v);
19453 this.select(this.store.indexOf(r), scrollIntoView);
19461 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19462 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19463 * @param {Number} index The zero-based index of the list item to select
19464 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19465 * selected item if it is not currently in view (defaults to true)
19467 select : function(index, scrollIntoView){
19468 this.selectedIndex = index;
19469 this.view.select(index);
19470 if(scrollIntoView !== false){
19471 var el = this.view.getNode(index);
19473 this.innerList.scrollChildIntoView(el, false);
19479 selectNext : function(){
19480 var ct = this.store.getCount();
19482 if(this.selectedIndex == -1){
19484 }else if(this.selectedIndex < ct-1){
19485 this.select(this.selectedIndex+1);
19491 selectPrev : function(){
19492 var ct = this.store.getCount();
19494 if(this.selectedIndex == -1){
19496 }else if(this.selectedIndex != 0){
19497 this.select(this.selectedIndex-1);
19503 onKeyUp : function(e){
19504 if(this.editable !== false && !e.isSpecialKey()){
19505 this.lastKey = e.getKey();
19506 this.dqTask.delay(this.queryDelay);
19511 validateBlur : function(){
19512 return !this.list || !this.list.isVisible();
19516 initQuery : function(){
19517 this.doQuery(this.getRawValue());
19521 doForce : function(){
19522 if(this.el.dom.value.length > 0){
19523 this.el.dom.value =
19524 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19530 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19531 * query allowing the query action to be canceled if needed.
19532 * @param {String} query The SQL query to execute
19533 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19534 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19535 * saved in the current store (defaults to false)
19537 doQuery : function(q, forceAll){
19538 if(q === undefined || q === null){
19543 forceAll: forceAll,
19547 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19551 forceAll = qe.forceAll;
19552 if(forceAll === true || (q.length >= this.minChars)){
19553 if(this.lastQuery != q || this.alwaysQuery){
19554 this.lastQuery = q;
19555 if(this.mode == 'local'){
19556 this.selectedIndex = -1;
19558 this.store.clearFilter();
19560 this.store.filter(this.displayField, q);
19564 this.store.baseParams[this.queryParam] = q;
19566 params: this.getParams(q)
19571 this.selectedIndex = -1;
19578 getParams : function(q){
19580 //p[this.queryParam] = q;
19583 p.limit = this.pageSize;
19589 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19591 collapse : function(){
19592 if(!this.isExpanded()){
19596 Roo.get(document).un('mousedown', this.collapseIf, this);
19597 Roo.get(document).un('mousewheel', this.collapseIf, this);
19598 if (!this.editable) {
19599 Roo.get(document).un('keydown', this.listKeyPress, this);
19601 this.fireEvent('collapse', this);
19605 collapseIf : function(e){
19606 if(!e.within(this.wrap) && !e.within(this.list)){
19612 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19614 expand : function(){
19615 if(this.isExpanded() || !this.hasFocus){
19618 this.list.alignTo(this.el, this.listAlign);
19620 Roo.get(document).on('mousedown', this.collapseIf, this);
19621 Roo.get(document).on('mousewheel', this.collapseIf, this);
19622 if (!this.editable) {
19623 Roo.get(document).on('keydown', this.listKeyPress, this);
19626 this.fireEvent('expand', this);
19630 // Implements the default empty TriggerField.onTriggerClick function
19631 onTriggerClick : function(){
19635 if(this.isExpanded()){
19637 if (!this.blockFocus) {
19642 this.hasFocus = true;
19643 if(this.triggerAction == 'all') {
19644 this.doQuery(this.allQuery, true);
19646 this.doQuery(this.getRawValue());
19648 if (!this.blockFocus) {
19653 listKeyPress : function(e)
19655 //Roo.log('listkeypress');
19656 // scroll to first matching element based on key pres..
19657 if (e.isSpecialKey()) {
19660 var k = String.fromCharCode(e.getKey()).toUpperCase();
19663 var csel = this.view.getSelectedNodes();
19664 var cselitem = false;
19666 var ix = this.view.indexOf(csel[0]);
19667 cselitem = this.store.getAt(ix);
19668 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19674 this.store.each(function(v) {
19676 // start at existing selection.
19677 if (cselitem.id == v.id) {
19683 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19684 match = this.store.indexOf(v);
19689 if (match === false) {
19690 return true; // no more action?
19693 this.view.select(match);
19694 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19695 sn.scrollIntoView(sn.dom.parentNode, false);
19699 * @cfg {Boolean} grow
19703 * @cfg {Number} growMin
19707 * @cfg {Number} growMax
19715 * Copyright(c) 2010-2012, Roo J Solutions Limited
19722 * @class Roo.form.ComboBoxArray
19723 * @extends Roo.form.TextField
19724 * A facebook style adder... for lists of email / people / countries etc...
19725 * pick multiple items from a combo box, and shows each one.
19727 * Fred [x] Brian [x] [Pick another |v]
19730 * For this to work: it needs various extra information
19731 * - normal combo problay has
19733 * + displayField, valueField
19735 * For our purpose...
19738 * If we change from 'extends' to wrapping...
19745 * Create a new ComboBoxArray.
19746 * @param {Object} config Configuration options
19750 Roo.form.ComboBoxArray = function(config)
19754 * @event beforeremove
19755 * Fires before remove the value from the list
19756 * @param {Roo.form.ComboBoxArray} _self This combo box array
19757 * @param {Roo.form.ComboBoxArray.Item} item removed item
19759 'beforeremove' : true,
19762 * Fires when remove the value from the list
19763 * @param {Roo.form.ComboBoxArray} _self This combo box array
19764 * @param {Roo.form.ComboBoxArray.Item} item removed item
19771 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19773 this.items = new Roo.util.MixedCollection(false);
19775 // construct the child combo...
19785 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19788 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19793 // behavies liek a hiddne field
19794 inputType: 'hidden',
19796 * @cfg {Number} width The width of the box that displays the selected element
19803 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19807 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19809 hiddenName : false,
19812 // private the array of items that are displayed..
19814 // private - the hidden field el.
19816 // private - the filed el..
19819 //validateValue : function() { return true; }, // all values are ok!
19820 //onAddClick: function() { },
19822 onRender : function(ct, position)
19825 // create the standard hidden element
19826 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19829 // give fake names to child combo;
19830 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19831 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19833 this.combo = Roo.factory(this.combo, Roo.form);
19834 this.combo.onRender(ct, position);
19835 if (typeof(this.combo.width) != 'undefined') {
19836 this.combo.onResize(this.combo.width,0);
19839 this.combo.initEvents();
19841 // assigned so form know we need to do this..
19842 this.store = this.combo.store;
19843 this.valueField = this.combo.valueField;
19844 this.displayField = this.combo.displayField ;
19847 this.combo.wrap.addClass('x-cbarray-grp');
19849 var cbwrap = this.combo.wrap.createChild(
19850 {tag: 'div', cls: 'x-cbarray-cb'},
19855 this.hiddenEl = this.combo.wrap.createChild({
19856 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19858 this.el = this.combo.wrap.createChild({
19859 tag: 'input', type:'hidden' , name: this.name, value : ''
19861 // this.el.dom.removeAttribute("name");
19864 this.outerWrap = this.combo.wrap;
19865 this.wrap = cbwrap;
19867 this.outerWrap.setWidth(this.width);
19868 this.outerWrap.dom.removeChild(this.el.dom);
19870 this.wrap.dom.appendChild(this.el.dom);
19871 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19872 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19874 this.combo.trigger.setStyle('position','relative');
19875 this.combo.trigger.setStyle('left', '0px');
19876 this.combo.trigger.setStyle('top', '2px');
19878 this.combo.el.setStyle('vertical-align', 'text-bottom');
19880 //this.trigger.setStyle('vertical-align', 'top');
19882 // this should use the code from combo really... on('add' ....)
19886 this.adder = this.outerWrap.createChild(
19887 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19889 this.adder.on('click', function(e) {
19890 _t.fireEvent('adderclick', this, e);
19894 //this.adder.on('click', this.onAddClick, _t);
19897 this.combo.on('select', function(cb, rec, ix) {
19898 this.addItem(rec.data);
19901 cb.el.dom.value = '';
19902 //cb.lastData = rec.data;
19911 getName: function()
19913 // returns hidden if it's set..
19914 if (!this.rendered) {return ''};
19915 return this.hiddenName ? this.hiddenName : this.name;
19920 onResize: function(w, h){
19923 // not sure if this is needed..
19924 //this.combo.onResize(w,h);
19926 if(typeof w != 'number'){
19927 // we do not handle it!?!?
19930 var tw = this.combo.trigger.getWidth();
19931 tw += this.addicon ? this.addicon.getWidth() : 0;
19932 tw += this.editicon ? this.editicon.getWidth() : 0;
19934 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19936 this.combo.trigger.setStyle('left', '0px');
19938 if(this.list && this.listWidth === undefined){
19939 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19940 this.list.setWidth(lw);
19941 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19948 addItem: function(rec)
19950 var valueField = this.combo.valueField;
19951 var displayField = this.combo.displayField;
19953 if (this.items.indexOfKey(rec[valueField]) > -1) {
19954 //console.log("GOT " + rec.data.id);
19958 var x = new Roo.form.ComboBoxArray.Item({
19959 //id : rec[this.idField],
19961 displayField : displayField ,
19962 tipField : displayField ,
19966 this.items.add(rec[valueField],x);
19967 // add it before the element..
19968 this.updateHiddenEl();
19969 x.render(this.outerWrap, this.wrap.dom);
19970 // add the image handler..
19973 updateHiddenEl : function()
19976 if (!this.hiddenEl) {
19980 var idField = this.combo.valueField;
19982 this.items.each(function(f) {
19983 ar.push(f.data[idField]);
19985 this.hiddenEl.dom.value = ar.join(',');
19991 this.items.clear();
19993 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19997 this.el.dom.value = '';
19998 if (this.hiddenEl) {
19999 this.hiddenEl.dom.value = '';
20003 getValue: function()
20005 return this.hiddenEl ? this.hiddenEl.dom.value : '';
20007 setValue: function(v) // not a valid action - must use addItems..
20012 if (this.store.isLocal && (typeof(v) == 'string')) {
20013 // then we can use the store to find the values..
20014 // comma seperated at present.. this needs to allow JSON based encoding..
20015 this.hiddenEl.value = v;
20017 Roo.each(v.split(','), function(k) {
20018 Roo.log("CHECK " + this.valueField + ',' + k);
20019 var li = this.store.query(this.valueField, k);
20024 add[this.valueField] = k;
20025 add[this.displayField] = li.item(0).data[this.displayField];
20031 if (typeof(v) == 'object' ) {
20032 // then let's assume it's an array of objects..
20033 Roo.each(v, function(l) {
20041 setFromData: function(v)
20043 // this recieves an object, if setValues is called.
20045 this.el.dom.value = v[this.displayField];
20046 this.hiddenEl.dom.value = v[this.valueField];
20047 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20050 var kv = v[this.valueField];
20051 var dv = v[this.displayField];
20052 kv = typeof(kv) != 'string' ? '' : kv;
20053 dv = typeof(dv) != 'string' ? '' : dv;
20056 var keys = kv.split(',');
20057 var display = dv.split(',');
20058 for (var i = 0 ; i < keys.length; i++) {
20061 add[this.valueField] = keys[i];
20062 add[this.displayField] = display[i];
20070 * Validates the combox array value
20071 * @return {Boolean} True if the value is valid, else false
20073 validate : function(){
20074 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20075 this.clearInvalid();
20081 validateValue : function(value){
20082 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20090 isDirty : function() {
20091 if(this.disabled) {
20096 var d = Roo.decode(String(this.originalValue));
20098 return String(this.getValue()) !== String(this.originalValue);
20101 var originalValue = [];
20103 for (var i = 0; i < d.length; i++){
20104 originalValue.push(d[i][this.valueField]);
20107 return String(this.getValue()) !== String(originalValue.join(','));
20116 * @class Roo.form.ComboBoxArray.Item
20117 * @extends Roo.BoxComponent
20118 * A selected item in the list
20119 * Fred [x] Brian [x] [Pick another |v]
20122 * Create a new item.
20123 * @param {Object} config Configuration options
20126 Roo.form.ComboBoxArray.Item = function(config) {
20127 config.id = Roo.id();
20128 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20131 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20134 displayField : false,
20138 defaultAutoCreate : {
20140 cls: 'x-cbarray-item',
20147 src : Roo.BLANK_IMAGE_URL ,
20155 onRender : function(ct, position)
20157 Roo.form.Field.superclass.onRender.call(this, ct, position);
20160 var cfg = this.getAutoCreate();
20161 this.el = ct.createChild(cfg, position);
20164 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20166 this.el.child('div').dom.innerHTML = this.cb.renderer ?
20167 this.cb.renderer(this.data) :
20168 String.format('{0}',this.data[this.displayField]);
20171 this.el.child('div').dom.setAttribute('qtip',
20172 String.format('{0}',this.data[this.tipField])
20175 this.el.child('img').on('click', this.remove, this);
20179 remove : function()
20181 if(this.cb.disabled){
20185 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20186 this.cb.items.remove(this);
20187 this.el.child('img').un('click', this.remove, this);
20189 this.cb.updateHiddenEl();
20191 this.cb.fireEvent('remove', this.cb, this);
20196 * RooJS Library 1.1.1
20197 * Copyright(c) 2008-2011 Alan Knowles
20204 * @class Roo.form.ComboNested
20205 * @extends Roo.form.ComboBox
20206 * A combobox for that allows selection of nested items in a list,
20221 * Create a new ComboNested
20222 * @param {Object} config Configuration options
20224 Roo.form.ComboNested = function(config){
20225 Roo.form.ComboCheck.superclass.constructor.call(this, config);
20226 // should verify some data...
20228 // hiddenName = required..
20229 // displayField = required
20230 // valudField == required
20231 var req= [ 'hiddenName', 'displayField', 'valueField' ];
20233 Roo.each(req, function(e) {
20234 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20235 throw "Roo.form.ComboNested : missing value for: " + e;
20242 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20245 * @config {Number} max Number of columns to show
20250 list : null, // the outermost div..
20251 innerLists : null, // the
20255 onRender : function(ct, position)
20257 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20259 if(this.hiddenName){
20260 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
20262 this.hiddenField.value =
20263 this.hiddenValue !== undefined ? this.hiddenValue :
20264 this.value !== undefined ? this.value : '';
20266 // prevent input submission
20267 this.el.dom.removeAttribute('name');
20273 this.el.dom.setAttribute('autocomplete', 'off');
20276 var cls = 'x-combo-list';
20278 this.list = new Roo.Layer({
20279 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20282 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20283 this.list.setWidth(lw);
20284 this.list.swallowEvent('mousewheel');
20285 this.assetHeight = 0;
20288 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20289 this.assetHeight += this.header.getHeight();
20291 this.innerLists = [];
20294 for (var i =0 ; i < this.maxColumns; i++) {
20295 this.onRenderList( cls, i);
20298 // always needs footer, as we are going to have an 'OK' button.
20299 this.footer = this.list.createChild({cls:cls+'-ft'});
20300 this.pageTb = new Roo.Toolbar(this.footer);
20305 handler: function()
20311 if ( this.allowBlank && !this.disableClear) {
20313 this.pageTb.add(new Roo.Toolbar.Fill(), {
20314 cls: 'x-btn-icon x-btn-clear',
20316 handler: function()
20319 _this.clearValue();
20320 _this.onSelect(false, -1);
20325 this.assetHeight += this.footer.getHeight();
20329 onRenderList : function ( cls, i)
20332 var lw = Math.floor(
20333 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20336 this.list.setWidth(lw); // default to '1'
20338 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20339 //il.on('mouseover', this.onViewOver, this, { list: i });
20340 //il.on('mousemove', this.onViewMove, this, { list: i });
20342 il.setStyle({ 'overflow-x' : 'hidden'});
20345 this.tpl = new Roo.Template({
20346 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20347 isEmpty: function (value, allValues) {
20349 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20350 return dl ? 'has-children' : 'no-children'
20355 var store = this.store;
20357 store = new Roo.data.SimpleStore({
20358 //fields : this.store.reader.meta.fields,
20359 reader : this.store.reader,
20363 this.stores[i] = store;
20367 var view = this.views[i] = new Roo.View(
20373 selectedClass: this.selectedClass
20376 view.getEl().setWidth(lw);
20377 view.getEl().setStyle({
20378 position: i < 1 ? 'relative' : 'absolute',
20380 left: (i * lw ) + 'px',
20381 display : i > 0 ? 'none' : 'block'
20383 view.on('selectionchange', this.onSelectChange, this, {list : i });
20384 view.on('dblclick', this.onDoubleClick, this, {list : i });
20385 //view.on('click', this.onViewClick, this, { list : i });
20387 store.on('beforeload', this.onBeforeLoad, this);
20388 store.on('load', this.onLoad, this, { list : i});
20389 store.on('loadexception', this.onLoadException, this);
20391 // hide the other vies..
20396 onResize : function() {},
20398 restrictHeight : function()
20401 Roo.each(this.innerLists, function(il,i) {
20402 var el = this.views[i].getEl();
20403 el.dom.style.height = '';
20404 var inner = el.dom;
20405 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20406 // only adjust heights on other ones..
20409 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20410 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20411 mh = Math.max(el.getHeight(), mh);
20417 this.list.beginUpdate();
20418 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20419 this.list.alignTo(this.el, this.listAlign);
20420 this.list.endUpdate();
20425 // -- store handlers..
20427 onBeforeLoad : function()
20429 if(!this.hasFocus){
20432 this.innerLists[0].update(this.loadingText ?
20433 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20434 this.restrictHeight();
20435 this.selectedIndex = -1;
20438 onLoad : function(a,b,c,d)
20441 if(!this.hasFocus){
20445 if(this.store.getCount() > 0) {
20447 this.restrictHeight();
20449 this.onEmptyResults();
20452 this.stores[1].loadData([]);
20453 this.stores[2].loadData([]);
20462 onLoadException : function()
20465 Roo.log(this.store.reader.jsonData);
20466 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20467 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20475 onSelectChange : function (view, sels, opts )
20477 var ix = view.getSelectedIndexes();
20480 if (opts.list > this.maxColumns - 2) {
20482 this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20487 this.setFromData({});
20488 var str = this.stores[opts.list+1];
20489 str.loadData( str.reader.readerType == 'json' ? { data : [] } : [] );
20493 var rec = view.store.getAt(ix[0]);
20494 this.setFromData(rec.data);
20496 var lw = Math.floor(
20497 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20499 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20500 var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20501 this.stores[opts.list+1].loadData( data );
20502 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20503 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20504 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20505 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20507 onDoubleClick : function()
20509 this.collapse(); //??
20514 findRecord : function (prop,value)
20516 return this.findRecordInStore(this.store, prop,value);
20520 findRecordInStore : function(store, prop, value)
20522 var cstore = new Roo.data.SimpleStore({
20523 //fields : this.store.reader.meta.fields, // we need array reader.. for
20524 reader : this.store.reader,
20528 var record = false;
20529 if(store.getCount() > 0){
20530 store.each(function(r){
20531 if(r.data[prop] == value){
20535 if (r.data.cn && r.data.cn.length) {
20536 cstore.loadData( r.data.cn);
20537 var cret = _this.findRecordInStore(cstore, prop, value);
20538 if (cret !== false) {
20555 * Ext JS Library 1.1.1
20556 * Copyright(c) 2006-2007, Ext JS, LLC.
20558 * Originally Released Under LGPL - original licence link has changed is not relivant.
20561 * <script type="text/javascript">
20564 * @class Roo.form.Checkbox
20565 * @extends Roo.form.Field
20566 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20568 * Creates a new Checkbox
20569 * @param {Object} config Configuration options
20571 Roo.form.Checkbox = function(config){
20572 Roo.form.Checkbox.superclass.constructor.call(this, config);
20576 * Fires when the checkbox is checked or unchecked.
20577 * @param {Roo.form.Checkbox} this This checkbox
20578 * @param {Boolean} checked The new checked value
20584 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20586 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20588 focusClass : undefined,
20590 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20592 fieldClass: "x-form-field",
20594 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20598 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20599 * {tag: "input", type: "checkbox", autocomplete: "off"})
20601 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20603 * @cfg {String} boxLabel The text that appears beside the checkbox
20607 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20611 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20613 valueOff: '0', // value when not checked..
20615 actionMode : 'viewEl',
20618 itemCls : 'x-menu-check-item x-form-item',
20619 groupClass : 'x-menu-group-item',
20620 inputType : 'hidden',
20623 inSetChecked: false, // check that we are not calling self...
20625 inputElement: false, // real input element?
20626 basedOn: false, // ????
20628 isFormField: true, // not sure where this is needed!!!!
20630 onResize : function(){
20631 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20632 if(!this.boxLabel){
20633 this.el.alignTo(this.wrap, 'c-c');
20637 initEvents : function(){
20638 Roo.form.Checkbox.superclass.initEvents.call(this);
20639 this.el.on("click", this.onClick, this);
20640 this.el.on("change", this.onClick, this);
20644 getResizeEl : function(){
20648 getPositionEl : function(){
20653 onRender : function(ct, position){
20654 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20656 if(this.inputValue !== undefined){
20657 this.el.dom.value = this.inputValue;
20660 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20661 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20662 var viewEl = this.wrap.createChild({
20663 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20664 this.viewEl = viewEl;
20665 this.wrap.on('click', this.onClick, this);
20667 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20668 this.el.on('propertychange', this.setFromHidden, this); //ie
20673 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20674 // viewEl.on('click', this.onClick, this);
20676 //if(this.checked){
20677 this.setChecked(this.checked);
20679 //this.checked = this.el.dom;
20685 initValue : Roo.emptyFn,
20688 * Returns the checked state of the checkbox.
20689 * @return {Boolean} True if checked, else false
20691 getValue : function(){
20693 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20695 return this.valueOff;
20700 onClick : function(){
20701 if (this.disabled) {
20704 this.setChecked(!this.checked);
20706 //if(this.el.dom.checked != this.checked){
20707 // this.setValue(this.el.dom.checked);
20712 * Sets the checked state of the checkbox.
20713 * On is always based on a string comparison between inputValue and the param.
20714 * @param {Boolean/String} value - the value to set
20715 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20717 setValue : function(v,suppressEvent){
20720 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20721 //if(this.el && this.el.dom){
20722 // this.el.dom.checked = this.checked;
20723 // this.el.dom.defaultChecked = this.checked;
20725 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20726 //this.fireEvent("check", this, this.checked);
20729 setChecked : function(state,suppressEvent)
20731 if (this.inSetChecked) {
20732 this.checked = state;
20738 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20740 this.checked = state;
20741 if(suppressEvent !== true){
20742 this.fireEvent('check', this, state);
20744 this.inSetChecked = true;
20745 this.el.dom.value = state ? this.inputValue : this.valueOff;
20746 this.inSetChecked = false;
20749 // handle setting of hidden value by some other method!!?!?
20750 setFromHidden: function()
20755 //console.log("SET FROM HIDDEN");
20756 //alert('setFrom hidden');
20757 this.setValue(this.el.dom.value);
20760 onDestroy : function()
20763 Roo.get(this.viewEl).remove();
20766 Roo.form.Checkbox.superclass.onDestroy.call(this);
20769 setBoxLabel : function(str)
20771 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20776 * Ext JS Library 1.1.1
20777 * Copyright(c) 2006-2007, Ext JS, LLC.
20779 * Originally Released Under LGPL - original licence link has changed is not relivant.
20782 * <script type="text/javascript">
20786 * @class Roo.form.Radio
20787 * @extends Roo.form.Checkbox
20788 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20789 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20791 * Creates a new Radio
20792 * @param {Object} config Configuration options
20794 Roo.form.Radio = function(){
20795 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20797 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20798 inputType: 'radio',
20801 * If this radio is part of a group, it will return the selected value
20804 getGroupValue : function(){
20805 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20809 onRender : function(ct, position){
20810 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20812 if(this.inputValue !== undefined){
20813 this.el.dom.value = this.inputValue;
20816 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20817 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20818 //var viewEl = this.wrap.createChild({
20819 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20820 //this.viewEl = viewEl;
20821 //this.wrap.on('click', this.onClick, this);
20823 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20824 //this.el.on('propertychange', this.setFromHidden, this); //ie
20829 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20830 // viewEl.on('click', this.onClick, this);
20833 this.el.dom.checked = 'checked' ;
20839 });//<script type="text/javascript">
20842 * Based Ext JS Library 1.1.1
20843 * Copyright(c) 2006-2007, Ext JS, LLC.
20849 * @class Roo.HtmlEditorCore
20850 * @extends Roo.Component
20851 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20853 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20856 Roo.HtmlEditorCore = function(config){
20859 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20864 * @event initialize
20865 * Fires when the editor is fully initialized (including the iframe)
20866 * @param {Roo.HtmlEditorCore} this
20871 * Fires when the editor is first receives the focus. Any insertion must wait
20872 * until after this event.
20873 * @param {Roo.HtmlEditorCore} this
20877 * @event beforesync
20878 * Fires before the textarea is updated with content from the editor iframe. Return false
20879 * to cancel the sync.
20880 * @param {Roo.HtmlEditorCore} this
20881 * @param {String} html
20885 * @event beforepush
20886 * Fires before the iframe editor is updated with content from the textarea. Return false
20887 * to cancel the push.
20888 * @param {Roo.HtmlEditorCore} this
20889 * @param {String} html
20894 * Fires when the textarea is updated with content from the editor iframe.
20895 * @param {Roo.HtmlEditorCore} this
20896 * @param {String} html
20901 * Fires when the iframe editor is updated with content from the textarea.
20902 * @param {Roo.HtmlEditorCore} this
20903 * @param {String} html
20908 * @event editorevent
20909 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20910 * @param {Roo.HtmlEditorCore} this
20916 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20918 // defaults : white / black...
20919 this.applyBlacklists();
20926 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20930 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20936 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20941 * @cfg {Number} height (in pixels)
20945 * @cfg {Number} width (in pixels)
20950 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20953 stylesheets: false,
20958 // private properties
20959 validationEvent : false,
20961 initialized : false,
20963 sourceEditMode : false,
20964 onFocus : Roo.emptyFn,
20966 hideMode:'offsets',
20970 // blacklist + whitelisted elements..
20977 * Protected method that will not generally be called directly. It
20978 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20979 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20981 getDocMarkup : function(){
20985 // inherit styels from page...??
20986 if (this.stylesheets === false) {
20988 Roo.get(document.head).select('style').each(function(node) {
20989 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20992 Roo.get(document.head).select('link').each(function(node) {
20993 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20996 } else if (!this.stylesheets.length) {
20998 st = '<style type="text/css">' +
20999 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21002 st = '<style type="text/css">' +
21007 st += '<style type="text/css">' +
21008 'IMG { cursor: pointer } ' +
21011 var cls = 'roo-htmleditor-body';
21013 if(this.bodyCls.length){
21014 cls += ' ' + this.bodyCls;
21017 return '<html><head>' + st +
21018 //<style type="text/css">' +
21019 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21021 ' </head><body class="' + cls + '"></body></html>';
21025 onRender : function(ct, position)
21028 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21029 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21032 this.el.dom.style.border = '0 none';
21033 this.el.dom.setAttribute('tabIndex', -1);
21034 this.el.addClass('x-hidden hide');
21038 if(Roo.isIE){ // fix IE 1px bogus margin
21039 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21043 this.frameId = Roo.id();
21047 var iframe = this.owner.wrap.createChild({
21049 cls: 'form-control', // bootstrap..
21051 name: this.frameId,
21052 frameBorder : 'no',
21053 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21058 this.iframe = iframe.dom;
21060 this.assignDocWin();
21062 this.doc.designMode = 'on';
21065 this.doc.write(this.getDocMarkup());
21069 var task = { // must defer to wait for browser to be ready
21071 //console.log("run task?" + this.doc.readyState);
21072 this.assignDocWin();
21073 if(this.doc.body || this.doc.readyState == 'complete'){
21075 this.doc.designMode="on";
21079 Roo.TaskMgr.stop(task);
21080 this.initEditor.defer(10, this);
21087 Roo.TaskMgr.start(task);
21092 onResize : function(w, h)
21094 Roo.log('resize: ' +w + ',' + h );
21095 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21099 if(typeof w == 'number'){
21101 this.iframe.style.width = w + 'px';
21103 if(typeof h == 'number'){
21105 this.iframe.style.height = h + 'px';
21107 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21114 * Toggles the editor between standard and source edit mode.
21115 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21117 toggleSourceEdit : function(sourceEditMode){
21119 this.sourceEditMode = sourceEditMode === true;
21121 if(this.sourceEditMode){
21123 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21126 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21127 //this.iframe.className = '';
21130 //this.setSize(this.owner.wrap.getSize());
21131 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21138 * Protected method that will not generally be called directly. If you need/want
21139 * custom HTML cleanup, this is the method you should override.
21140 * @param {String} html The HTML to be cleaned
21141 * return {String} The cleaned HTML
21143 cleanHtml : function(html){
21144 html = String(html);
21145 if(html.length > 5){
21146 if(Roo.isSafari){ // strip safari nonsense
21147 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21150 if(html == ' '){
21157 * HTML Editor -> Textarea
21158 * Protected method that will not generally be called directly. Syncs the contents
21159 * of the editor iframe with the textarea.
21161 syncValue : function(){
21162 if(this.initialized){
21163 var bd = (this.doc.body || this.doc.documentElement);
21164 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21165 var html = bd.innerHTML;
21167 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21168 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21170 html = '<div style="'+m[0]+'">' + html + '</div>';
21173 html = this.cleanHtml(html);
21174 // fix up the special chars.. normaly like back quotes in word...
21175 // however we do not want to do this with chinese..
21176 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21178 var cc = match.charCodeAt();
21180 // Get the character value, handling surrogate pairs
21181 if (match.length == 2) {
21182 // It's a surrogate pair, calculate the Unicode code point
21183 var high = match.charCodeAt(0) - 0xD800;
21184 var low = match.charCodeAt(1) - 0xDC00;
21185 cc = (high * 0x400) + low + 0x10000;
21187 (cc >= 0x4E00 && cc < 0xA000 ) ||
21188 (cc >= 0x3400 && cc < 0x4E00 ) ||
21189 (cc >= 0xf900 && cc < 0xfb00 )
21194 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21195 return "&#" + cc + ";";
21202 if(this.owner.fireEvent('beforesync', this, html) !== false){
21203 this.el.dom.value = html;
21204 this.owner.fireEvent('sync', this, html);
21210 * Protected method that will not generally be called directly. Pushes the value of the textarea
21211 * into the iframe editor.
21213 pushValue : function(){
21214 if(this.initialized){
21215 var v = this.el.dom.value.trim();
21217 // if(v.length < 1){
21221 if(this.owner.fireEvent('beforepush', this, v) !== false){
21222 var d = (this.doc.body || this.doc.documentElement);
21224 this.cleanUpPaste();
21225 this.el.dom.value = d.innerHTML;
21226 this.owner.fireEvent('push', this, v);
21232 deferFocus : function(){
21233 this.focus.defer(10, this);
21237 focus : function(){
21238 if(this.win && !this.sourceEditMode){
21245 assignDocWin: function()
21247 var iframe = this.iframe;
21250 this.doc = iframe.contentWindow.document;
21251 this.win = iframe.contentWindow;
21253 // if (!Roo.get(this.frameId)) {
21256 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21257 // this.win = Roo.get(this.frameId).dom.contentWindow;
21259 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21263 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21264 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21269 initEditor : function(){
21270 //console.log("INIT EDITOR");
21271 this.assignDocWin();
21275 this.doc.designMode="on";
21277 this.doc.write(this.getDocMarkup());
21280 var dbody = (this.doc.body || this.doc.documentElement);
21281 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21282 // this copies styles from the containing element into thsi one..
21283 // not sure why we need all of this..
21284 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21286 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21287 //ss['background-attachment'] = 'fixed'; // w3c
21288 dbody.bgProperties = 'fixed'; // ie
21289 //Roo.DomHelper.applyStyles(dbody, ss);
21290 Roo.EventManager.on(this.doc, {
21291 //'mousedown': this.onEditorEvent,
21292 'mouseup': this.onEditorEvent,
21293 'dblclick': this.onEditorEvent,
21294 'click': this.onEditorEvent,
21295 'keyup': this.onEditorEvent,
21300 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21302 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21303 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21305 this.initialized = true;
21307 this.owner.fireEvent('initialize', this);
21312 onDestroy : function(){
21318 //for (var i =0; i < this.toolbars.length;i++) {
21319 // // fixme - ask toolbars for heights?
21320 // this.toolbars[i].onDestroy();
21323 //this.wrap.dom.innerHTML = '';
21324 //this.wrap.remove();
21329 onFirstFocus : function(){
21331 this.assignDocWin();
21334 this.activated = true;
21337 if(Roo.isGecko){ // prevent silly gecko errors
21339 var s = this.win.getSelection();
21340 if(!s.focusNode || s.focusNode.nodeType != 3){
21341 var r = s.getRangeAt(0);
21342 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21347 this.execCmd('useCSS', true);
21348 this.execCmd('styleWithCSS', false);
21351 this.owner.fireEvent('activate', this);
21355 adjustFont: function(btn){
21356 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21357 //if(Roo.isSafari){ // safari
21360 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21361 if(Roo.isSafari){ // safari
21362 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21363 v = (v < 10) ? 10 : v;
21364 v = (v > 48) ? 48 : v;
21365 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21370 v = Math.max(1, v+adjust);
21372 this.execCmd('FontSize', v );
21375 onEditorEvent : function(e)
21377 this.owner.fireEvent('editorevent', this, e);
21378 // this.updateToolbar();
21379 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21382 insertTag : function(tg)
21384 // could be a bit smarter... -> wrap the current selected tRoo..
21385 if (tg.toLowerCase() == 'span' ||
21386 tg.toLowerCase() == 'code' ||
21387 tg.toLowerCase() == 'sup' ||
21388 tg.toLowerCase() == 'sub'
21391 range = this.createRange(this.getSelection());
21392 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21393 wrappingNode.appendChild(range.extractContents());
21394 range.insertNode(wrappingNode);
21401 this.execCmd("formatblock", tg);
21405 insertText : function(txt)
21409 var range = this.createRange();
21410 range.deleteContents();
21411 //alert(Sender.getAttribute('label'));
21413 range.insertNode(this.doc.createTextNode(txt));
21419 * Executes a Midas editor command on the editor document and performs necessary focus and
21420 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21421 * @param {String} cmd The Midas command
21422 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21424 relayCmd : function(cmd, value){
21426 this.execCmd(cmd, value);
21427 this.owner.fireEvent('editorevent', this);
21428 //this.updateToolbar();
21429 this.owner.deferFocus();
21433 * Executes a Midas editor command directly on the editor document.
21434 * For visual commands, you should use {@link #relayCmd} instead.
21435 * <b>This should only be called after the editor is initialized.</b>
21436 * @param {String} cmd The Midas command
21437 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21439 execCmd : function(cmd, value){
21440 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21447 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21449 * @param {String} text | dom node..
21451 insertAtCursor : function(text)
21454 if(!this.activated){
21460 var r = this.doc.selection.createRange();
21471 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21475 // from jquery ui (MIT licenced)
21477 var win = this.win;
21479 if (win.getSelection && win.getSelection().getRangeAt) {
21480 range = win.getSelection().getRangeAt(0);
21481 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21482 range.insertNode(node);
21483 } else if (win.document.selection && win.document.selection.createRange) {
21484 // no firefox support
21485 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21486 win.document.selection.createRange().pasteHTML(txt);
21488 // no firefox support
21489 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21490 this.execCmd('InsertHTML', txt);
21499 mozKeyPress : function(e){
21501 var c = e.getCharCode(), cmd;
21504 c = String.fromCharCode(c).toLowerCase();
21518 this.cleanUpPaste.defer(100, this);
21526 e.preventDefault();
21534 fixKeys : function(){ // load time branching for fastest keydown performance
21536 return function(e){
21537 var k = e.getKey(), r;
21540 r = this.doc.selection.createRange();
21543 r.pasteHTML('    ');
21550 r = this.doc.selection.createRange();
21552 var target = r.parentElement();
21553 if(!target || target.tagName.toLowerCase() != 'li'){
21555 r.pasteHTML('<br />');
21561 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21562 this.cleanUpPaste.defer(100, this);
21568 }else if(Roo.isOpera){
21569 return function(e){
21570 var k = e.getKey();
21574 this.execCmd('InsertHTML','    ');
21577 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21578 this.cleanUpPaste.defer(100, this);
21583 }else if(Roo.isSafari){
21584 return function(e){
21585 var k = e.getKey();
21589 this.execCmd('InsertText','\t');
21593 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21594 this.cleanUpPaste.defer(100, this);
21602 getAllAncestors: function()
21604 var p = this.getSelectedNode();
21607 a.push(p); // push blank onto stack..
21608 p = this.getParentElement();
21612 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21616 a.push(this.doc.body);
21620 lastSelNode : false,
21623 getSelection : function()
21625 this.assignDocWin();
21626 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21629 getSelectedNode: function()
21631 // this may only work on Gecko!!!
21633 // should we cache this!!!!
21638 var range = this.createRange(this.getSelection()).cloneRange();
21641 var parent = range.parentElement();
21643 var testRange = range.duplicate();
21644 testRange.moveToElementText(parent);
21645 if (testRange.inRange(range)) {
21648 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21651 parent = parent.parentElement;
21656 // is ancestor a text element.
21657 var ac = range.commonAncestorContainer;
21658 if (ac.nodeType == 3) {
21659 ac = ac.parentNode;
21662 var ar = ac.childNodes;
21665 var other_nodes = [];
21666 var has_other_nodes = false;
21667 for (var i=0;i<ar.length;i++) {
21668 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21671 // fullly contained node.
21673 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21678 // probably selected..
21679 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21680 other_nodes.push(ar[i]);
21684 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21689 has_other_nodes = true;
21691 if (!nodes.length && other_nodes.length) {
21692 nodes= other_nodes;
21694 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21700 createRange: function(sel)
21702 // this has strange effects when using with
21703 // top toolbar - not sure if it's a great idea.
21704 //this.editor.contentWindow.focus();
21705 if (typeof sel != "undefined") {
21707 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21709 return this.doc.createRange();
21712 return this.doc.createRange();
21715 getParentElement: function()
21718 this.assignDocWin();
21719 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21721 var range = this.createRange(sel);
21724 var p = range.commonAncestorContainer;
21725 while (p.nodeType == 3) { // text node
21736 * Range intersection.. the hard stuff...
21740 * [ -- selected range --- ]
21744 * if end is before start or hits it. fail.
21745 * if start is after end or hits it fail.
21747 * if either hits (but other is outside. - then it's not
21753 // @see http://www.thismuchiknow.co.uk/?p=64.
21754 rangeIntersectsNode : function(range, node)
21756 var nodeRange = node.ownerDocument.createRange();
21758 nodeRange.selectNode(node);
21760 nodeRange.selectNodeContents(node);
21763 var rangeStartRange = range.cloneRange();
21764 rangeStartRange.collapse(true);
21766 var rangeEndRange = range.cloneRange();
21767 rangeEndRange.collapse(false);
21769 var nodeStartRange = nodeRange.cloneRange();
21770 nodeStartRange.collapse(true);
21772 var nodeEndRange = nodeRange.cloneRange();
21773 nodeEndRange.collapse(false);
21775 return rangeStartRange.compareBoundaryPoints(
21776 Range.START_TO_START, nodeEndRange) == -1 &&
21777 rangeEndRange.compareBoundaryPoints(
21778 Range.START_TO_START, nodeStartRange) == 1;
21782 rangeCompareNode : function(range, node)
21784 var nodeRange = node.ownerDocument.createRange();
21786 nodeRange.selectNode(node);
21788 nodeRange.selectNodeContents(node);
21792 range.collapse(true);
21794 nodeRange.collapse(true);
21796 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21797 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21799 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21801 var nodeIsBefore = ss == 1;
21802 var nodeIsAfter = ee == -1;
21804 if (nodeIsBefore && nodeIsAfter) {
21807 if (!nodeIsBefore && nodeIsAfter) {
21808 return 1; //right trailed.
21811 if (nodeIsBefore && !nodeIsAfter) {
21812 return 2; // left trailed.
21818 // private? - in a new class?
21819 cleanUpPaste : function()
21821 // cleans up the whole document..
21822 Roo.log('cleanuppaste');
21824 this.cleanUpChildren(this.doc.body);
21825 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21826 if (clean != this.doc.body.innerHTML) {
21827 this.doc.body.innerHTML = clean;
21832 cleanWordChars : function(input) {// change the chars to hex code
21833 var he = Roo.HtmlEditorCore;
21835 var output = input;
21836 Roo.each(he.swapCodes, function(sw) {
21837 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21839 output = output.replace(swapper, sw[1]);
21846 cleanUpChildren : function (n)
21848 if (!n.childNodes.length) {
21851 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21852 this.cleanUpChild(n.childNodes[i]);
21859 cleanUpChild : function (node)
21862 //console.log(node);
21863 if (node.nodeName == "#text") {
21864 // clean up silly Windows -- stuff?
21867 if (node.nodeName == "#comment") {
21868 node.parentNode.removeChild(node);
21869 // clean up silly Windows -- stuff?
21872 var lcname = node.tagName.toLowerCase();
21873 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21874 // whitelist of tags..
21876 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21878 node.parentNode.removeChild(node);
21883 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21885 // spans with no attributes - just remove them..
21886 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21887 remove_keep_children = true;
21890 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21891 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21893 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21894 // remove_keep_children = true;
21897 if (remove_keep_children) {
21898 this.cleanUpChildren(node);
21899 // inserts everything just before this node...
21900 while (node.childNodes.length) {
21901 var cn = node.childNodes[0];
21902 node.removeChild(cn);
21903 node.parentNode.insertBefore(cn, node);
21905 node.parentNode.removeChild(node);
21909 if (!node.attributes || !node.attributes.length) {
21914 this.cleanUpChildren(node);
21918 function cleanAttr(n,v)
21921 if (v.match(/^\./) || v.match(/^\//)) {
21924 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21927 if (v.match(/^#/)) {
21930 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21931 node.removeAttribute(n);
21935 var cwhite = this.cwhite;
21936 var cblack = this.cblack;
21938 function cleanStyle(n,v)
21940 if (v.match(/expression/)) { //XSS?? should we even bother..
21941 node.removeAttribute(n);
21945 var parts = v.split(/;/);
21948 Roo.each(parts, function(p) {
21949 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21953 var l = p.split(':').shift().replace(/\s+/g,'');
21954 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21956 if ( cwhite.length && cblack.indexOf(l) > -1) {
21957 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21958 //node.removeAttribute(n);
21962 // only allow 'c whitelisted system attributes'
21963 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21964 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21965 //node.removeAttribute(n);
21975 if (clean.length) {
21976 node.setAttribute(n, clean.join(';'));
21978 node.removeAttribute(n);
21984 for (var i = node.attributes.length-1; i > -1 ; i--) {
21985 var a = node.attributes[i];
21988 if (a.name.toLowerCase().substr(0,2)=='on') {
21989 node.removeAttribute(a.name);
21992 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21993 node.removeAttribute(a.name);
21996 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21997 cleanAttr(a.name,a.value); // fixme..
22000 if (a.name == 'style') {
22001 cleanStyle(a.name,a.value);
22004 /// clean up MS crap..
22005 // tecnically this should be a list of valid class'es..
22008 if (a.name == 'class') {
22009 if (a.value.match(/^Mso/)) {
22010 node.removeAttribute('class');
22013 if (a.value.match(/^body$/)) {
22014 node.removeAttribute('class');
22025 this.cleanUpChildren(node);
22031 * Clean up MS wordisms...
22033 cleanWord : function(node)
22036 this.cleanWord(this.doc.body);
22041 node.nodeName == 'SPAN' &&
22042 !node.hasAttributes() &&
22043 node.childNodes.length == 1 &&
22044 node.firstChild.nodeName == "#text"
22046 var textNode = node.firstChild;
22047 node.removeChild(textNode);
22048 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22049 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22051 node.parentNode.insertBefore(textNode, node);
22052 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22053 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22055 node.parentNode.removeChild(node);
22058 if (node.nodeName == "#text") {
22059 // clean up silly Windows -- stuff?
22062 if (node.nodeName == "#comment") {
22063 node.parentNode.removeChild(node);
22064 // clean up silly Windows -- stuff?
22068 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22069 node.parentNode.removeChild(node);
22072 //Roo.log(node.tagName);
22073 // remove - but keep children..
22074 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22075 //Roo.log('-- removed');
22076 while (node.childNodes.length) {
22077 var cn = node.childNodes[0];
22078 node.removeChild(cn);
22079 node.parentNode.insertBefore(cn, node);
22080 // move node to parent - and clean it..
22081 this.cleanWord(cn);
22083 node.parentNode.removeChild(node);
22084 /// no need to iterate chidlren = it's got none..
22085 //this.iterateChildren(node, this.cleanWord);
22089 if (node.className.length) {
22091 var cn = node.className.split(/\W+/);
22093 Roo.each(cn, function(cls) {
22094 if (cls.match(/Mso[a-zA-Z]+/)) {
22099 node.className = cna.length ? cna.join(' ') : '';
22101 node.removeAttribute("class");
22105 if (node.hasAttribute("lang")) {
22106 node.removeAttribute("lang");
22109 if (node.hasAttribute("style")) {
22111 var styles = node.getAttribute("style").split(";");
22113 Roo.each(styles, function(s) {
22114 if (!s.match(/:/)) {
22117 var kv = s.split(":");
22118 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22121 // what ever is left... we allow.
22124 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22125 if (!nstyle.length) {
22126 node.removeAttribute('style');
22129 this.iterateChildren(node, this.cleanWord);
22135 * iterateChildren of a Node, calling fn each time, using this as the scole..
22136 * @param {DomNode} node node to iterate children of.
22137 * @param {Function} fn method of this class to call on each item.
22139 iterateChildren : function(node, fn)
22141 if (!node.childNodes.length) {
22144 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22145 fn.call(this, node.childNodes[i])
22151 * cleanTableWidths.
22153 * Quite often pasting from word etc.. results in tables with column and widths.
22154 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22157 cleanTableWidths : function(node)
22162 this.cleanTableWidths(this.doc.body);
22167 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22170 Roo.log(node.tagName);
22171 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22172 this.iterateChildren(node, this.cleanTableWidths);
22175 if (node.hasAttribute('width')) {
22176 node.removeAttribute('width');
22180 if (node.hasAttribute("style")) {
22183 var styles = node.getAttribute("style").split(";");
22185 Roo.each(styles, function(s) {
22186 if (!s.match(/:/)) {
22189 var kv = s.split(":");
22190 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22193 // what ever is left... we allow.
22196 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22197 if (!nstyle.length) {
22198 node.removeAttribute('style');
22202 this.iterateChildren(node, this.cleanTableWidths);
22210 domToHTML : function(currentElement, depth, nopadtext) {
22212 depth = depth || 0;
22213 nopadtext = nopadtext || false;
22215 if (!currentElement) {
22216 return this.domToHTML(this.doc.body);
22219 //Roo.log(currentElement);
22221 var allText = false;
22222 var nodeName = currentElement.nodeName;
22223 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22225 if (nodeName == '#text') {
22227 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22232 if (nodeName != 'BODY') {
22235 // Prints the node tagName, such as <A>, <IMG>, etc
22238 for(i = 0; i < currentElement.attributes.length;i++) {
22240 var aname = currentElement.attributes.item(i).name;
22241 if (!currentElement.attributes.item(i).value.length) {
22244 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22247 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22256 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22259 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22264 // Traverse the tree
22266 var currentElementChild = currentElement.childNodes.item(i);
22267 var allText = true;
22268 var innerHTML = '';
22270 while (currentElementChild) {
22271 // Formatting code (indent the tree so it looks nice on the screen)
22272 var nopad = nopadtext;
22273 if (lastnode == 'SPAN') {
22277 if (currentElementChild.nodeName == '#text') {
22278 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22279 toadd = nopadtext ? toadd : toadd.trim();
22280 if (!nopad && toadd.length > 80) {
22281 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22283 innerHTML += toadd;
22286 currentElementChild = currentElement.childNodes.item(i);
22292 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22294 // Recursively traverse the tree structure of the child node
22295 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22296 lastnode = currentElementChild.nodeName;
22298 currentElementChild=currentElement.childNodes.item(i);
22304 // The remaining code is mostly for formatting the tree
22305 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22310 ret+= "</"+tagName+">";
22316 applyBlacklists : function()
22318 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22319 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22323 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22324 if (b.indexOf(tag) > -1) {
22327 this.white.push(tag);
22331 Roo.each(w, function(tag) {
22332 if (b.indexOf(tag) > -1) {
22335 if (this.white.indexOf(tag) > -1) {
22338 this.white.push(tag);
22343 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22344 if (w.indexOf(tag) > -1) {
22347 this.black.push(tag);
22351 Roo.each(b, function(tag) {
22352 if (w.indexOf(tag) > -1) {
22355 if (this.black.indexOf(tag) > -1) {
22358 this.black.push(tag);
22363 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22364 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22368 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22369 if (b.indexOf(tag) > -1) {
22372 this.cwhite.push(tag);
22376 Roo.each(w, function(tag) {
22377 if (b.indexOf(tag) > -1) {
22380 if (this.cwhite.indexOf(tag) > -1) {
22383 this.cwhite.push(tag);
22388 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22389 if (w.indexOf(tag) > -1) {
22392 this.cblack.push(tag);
22396 Roo.each(b, function(tag) {
22397 if (w.indexOf(tag) > -1) {
22400 if (this.cblack.indexOf(tag) > -1) {
22403 this.cblack.push(tag);
22408 setStylesheets : function(stylesheets)
22410 if(typeof(stylesheets) == 'string'){
22411 Roo.get(this.iframe.contentDocument.head).createChild({
22413 rel : 'stylesheet',
22422 Roo.each(stylesheets, function(s) {
22427 Roo.get(_this.iframe.contentDocument.head).createChild({
22429 rel : 'stylesheet',
22438 removeStylesheets : function()
22442 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22447 setStyle : function(style)
22449 Roo.get(this.iframe.contentDocument.head).createChild({
22458 // hide stuff that is not compatible
22472 * @event specialkey
22476 * @cfg {String} fieldClass @hide
22479 * @cfg {String} focusClass @hide
22482 * @cfg {String} autoCreate @hide
22485 * @cfg {String} inputType @hide
22488 * @cfg {String} invalidClass @hide
22491 * @cfg {String} invalidText @hide
22494 * @cfg {String} msgFx @hide
22497 * @cfg {String} validateOnBlur @hide
22501 Roo.HtmlEditorCore.white = [
22502 'area', 'br', 'img', 'input', 'hr', 'wbr',
22504 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22505 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22506 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22507 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22508 'table', 'ul', 'xmp',
22510 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22513 'dir', 'menu', 'ol', 'ul', 'dl',
22519 Roo.HtmlEditorCore.black = [
22520 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22522 'base', 'basefont', 'bgsound', 'blink', 'body',
22523 'frame', 'frameset', 'head', 'html', 'ilayer',
22524 'iframe', 'layer', 'link', 'meta', 'object',
22525 'script', 'style' ,'title', 'xml' // clean later..
22527 Roo.HtmlEditorCore.clean = [
22528 'script', 'style', 'title', 'xml'
22530 Roo.HtmlEditorCore.remove = [
22535 Roo.HtmlEditorCore.ablack = [
22539 Roo.HtmlEditorCore.aclean = [
22540 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22544 Roo.HtmlEditorCore.pwhite= [
22545 'http', 'https', 'mailto'
22548 // white listed style attributes.
22549 Roo.HtmlEditorCore.cwhite= [
22550 // 'text-align', /// default is to allow most things..
22556 // black listed style attributes.
22557 Roo.HtmlEditorCore.cblack= [
22558 // 'font-size' -- this can be set by the project
22562 Roo.HtmlEditorCore.swapCodes =[
22573 //<script type="text/javascript">
22576 * Ext JS Library 1.1.1
22577 * Copyright(c) 2006-2007, Ext JS, LLC.
22583 Roo.form.HtmlEditor = function(config){
22587 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22589 if (!this.toolbars) {
22590 this.toolbars = [];
22592 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22598 * @class Roo.form.HtmlEditor
22599 * @extends Roo.form.Field
22600 * Provides a lightweight HTML Editor component.
22602 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22604 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22605 * supported by this editor.</b><br/><br/>
22606 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22607 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22609 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22611 * @cfg {Boolean} clearUp
22615 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22620 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22625 * @cfg {Number} height (in pixels)
22629 * @cfg {Number} width (in pixels)
22634 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22637 stylesheets: false,
22641 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22646 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22652 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22657 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22665 // private properties
22666 validationEvent : false,
22668 initialized : false,
22671 onFocus : Roo.emptyFn,
22673 hideMode:'offsets',
22675 actionMode : 'container', // defaults to hiding it...
22677 defaultAutoCreate : { // modified by initCompnoent..
22679 style:"width:500px;height:300px;",
22680 autocomplete: "new-password"
22684 initComponent : function(){
22687 * @event initialize
22688 * Fires when the editor is fully initialized (including the iframe)
22689 * @param {HtmlEditor} this
22694 * Fires when the editor is first receives the focus. Any insertion must wait
22695 * until after this event.
22696 * @param {HtmlEditor} this
22700 * @event beforesync
22701 * Fires before the textarea is updated with content from the editor iframe. Return false
22702 * to cancel the sync.
22703 * @param {HtmlEditor} this
22704 * @param {String} html
22708 * @event beforepush
22709 * Fires before the iframe editor is updated with content from the textarea. Return false
22710 * to cancel the push.
22711 * @param {HtmlEditor} this
22712 * @param {String} html
22717 * Fires when the textarea is updated with content from the editor iframe.
22718 * @param {HtmlEditor} this
22719 * @param {String} html
22724 * Fires when the iframe editor is updated with content from the textarea.
22725 * @param {HtmlEditor} this
22726 * @param {String} html
22730 * @event editmodechange
22731 * Fires when the editor switches edit modes
22732 * @param {HtmlEditor} this
22733 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22735 editmodechange: true,
22737 * @event editorevent
22738 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22739 * @param {HtmlEditor} this
22743 * @event firstfocus
22744 * Fires when on first focus - needed by toolbars..
22745 * @param {HtmlEditor} this
22750 * Auto save the htmlEditor value as a file into Events
22751 * @param {HtmlEditor} this
22755 * @event savedpreview
22756 * preview the saved version of htmlEditor
22757 * @param {HtmlEditor} this
22759 savedpreview: true,
22762 * @event stylesheetsclick
22763 * Fires when press the Sytlesheets button
22764 * @param {Roo.HtmlEditorCore} this
22766 stylesheetsclick: true
22768 this.defaultAutoCreate = {
22770 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22771 autocomplete: "new-password"
22776 * Protected method that will not generally be called directly. It
22777 * is called when the editor creates its toolbar. Override this method if you need to
22778 * add custom toolbar buttons.
22779 * @param {HtmlEditor} editor
22781 createToolbar : function(editor){
22782 Roo.log("create toolbars");
22783 if (!editor.toolbars || !editor.toolbars.length) {
22784 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22787 for (var i =0 ; i < editor.toolbars.length;i++) {
22788 editor.toolbars[i] = Roo.factory(
22789 typeof(editor.toolbars[i]) == 'string' ?
22790 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22791 Roo.form.HtmlEditor);
22792 editor.toolbars[i].init(editor);
22800 onRender : function(ct, position)
22803 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22805 this.wrap = this.el.wrap({
22806 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22809 this.editorcore.onRender(ct, position);
22811 if (this.resizable) {
22812 this.resizeEl = new Roo.Resizable(this.wrap, {
22816 minHeight : this.height,
22817 height: this.height,
22818 handles : this.resizable,
22821 resize : function(r, w, h) {
22822 _t.onResize(w,h); // -something
22828 this.createToolbar(this);
22832 this.setSize(this.wrap.getSize());
22834 if (this.resizeEl) {
22835 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22836 // should trigger onReize..
22839 this.keyNav = new Roo.KeyNav(this.el, {
22841 "tab" : function(e){
22842 e.preventDefault();
22844 var value = this.getValue();
22846 var start = this.el.dom.selectionStart;
22847 var end = this.el.dom.selectionEnd;
22851 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22852 this.el.dom.setSelectionRange(end + 1, end + 1);
22856 var f = value.substring(0, start).split("\t");
22858 if(f.pop().length != 0){
22862 this.setValue(f.join("\t") + value.substring(end));
22863 this.el.dom.setSelectionRange(start - 1, start - 1);
22867 "home" : function(e){
22868 e.preventDefault();
22870 var curr = this.el.dom.selectionStart;
22871 var lines = this.getValue().split("\n");
22878 this.el.dom.setSelectionRange(0, 0);
22884 for (var i = 0; i < lines.length;i++) {
22885 pos += lines[i].length;
22895 pos -= lines[i].length;
22901 this.el.dom.setSelectionRange(pos, pos);
22905 this.el.dom.selectionStart = pos;
22906 this.el.dom.selectionEnd = curr;
22909 "end" : function(e){
22910 e.preventDefault();
22912 var curr = this.el.dom.selectionStart;
22913 var lines = this.getValue().split("\n");
22920 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22926 for (var i = 0; i < lines.length;i++) {
22928 pos += lines[i].length;
22942 this.el.dom.setSelectionRange(pos, pos);
22946 this.el.dom.selectionStart = curr;
22947 this.el.dom.selectionEnd = pos;
22952 doRelay : function(foo, bar, hname){
22953 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22959 // if(this.autosave && this.w){
22960 // this.autoSaveFn = setInterval(this.autosave, 1000);
22965 onResize : function(w, h)
22967 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22972 if(typeof w == 'number'){
22973 var aw = w - this.wrap.getFrameWidth('lr');
22974 this.el.setWidth(this.adjustWidth('textarea', aw));
22977 if(typeof h == 'number'){
22979 for (var i =0; i < this.toolbars.length;i++) {
22980 // fixme - ask toolbars for heights?
22981 tbh += this.toolbars[i].tb.el.getHeight();
22982 if (this.toolbars[i].footer) {
22983 tbh += this.toolbars[i].footer.el.getHeight();
22990 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22991 ah -= 5; // knock a few pixes off for look..
22993 this.el.setHeight(this.adjustWidth('textarea', ah));
22997 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22998 this.editorcore.onResize(ew,eh);
23003 * Toggles the editor between standard and source edit mode.
23004 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23006 toggleSourceEdit : function(sourceEditMode)
23008 this.editorcore.toggleSourceEdit(sourceEditMode);
23010 if(this.editorcore.sourceEditMode){
23011 Roo.log('editor - showing textarea');
23014 // Roo.log(this.syncValue());
23015 this.editorcore.syncValue();
23016 this.el.removeClass('x-hidden');
23017 this.el.dom.removeAttribute('tabIndex');
23020 for (var i = 0; i < this.toolbars.length; i++) {
23021 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23022 this.toolbars[i].tb.hide();
23023 this.toolbars[i].footer.hide();
23028 Roo.log('editor - hiding textarea');
23030 // Roo.log(this.pushValue());
23031 this.editorcore.pushValue();
23033 this.el.addClass('x-hidden');
23034 this.el.dom.setAttribute('tabIndex', -1);
23036 for (var i = 0; i < this.toolbars.length; i++) {
23037 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23038 this.toolbars[i].tb.show();
23039 this.toolbars[i].footer.show();
23043 //this.deferFocus();
23046 this.setSize(this.wrap.getSize());
23047 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23049 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23052 // private (for BoxComponent)
23053 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23055 // private (for BoxComponent)
23056 getResizeEl : function(){
23060 // private (for BoxComponent)
23061 getPositionEl : function(){
23066 initEvents : function(){
23067 this.originalValue = this.getValue();
23071 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23074 markInvalid : Roo.emptyFn,
23076 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23079 clearInvalid : Roo.emptyFn,
23081 setValue : function(v){
23082 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23083 this.editorcore.pushValue();
23088 deferFocus : function(){
23089 this.focus.defer(10, this);
23093 focus : function(){
23094 this.editorcore.focus();
23100 onDestroy : function(){
23106 for (var i =0; i < this.toolbars.length;i++) {
23107 // fixme - ask toolbars for heights?
23108 this.toolbars[i].onDestroy();
23111 this.wrap.dom.innerHTML = '';
23112 this.wrap.remove();
23117 onFirstFocus : function(){
23118 //Roo.log("onFirstFocus");
23119 this.editorcore.onFirstFocus();
23120 for (var i =0; i < this.toolbars.length;i++) {
23121 this.toolbars[i].onFirstFocus();
23127 syncValue : function()
23129 this.editorcore.syncValue();
23132 pushValue : function()
23134 this.editorcore.pushValue();
23137 setStylesheets : function(stylesheets)
23139 this.editorcore.setStylesheets(stylesheets);
23142 removeStylesheets : function()
23144 this.editorcore.removeStylesheets();
23148 // hide stuff that is not compatible
23162 * @event specialkey
23166 * @cfg {String} fieldClass @hide
23169 * @cfg {String} focusClass @hide
23172 * @cfg {String} autoCreate @hide
23175 * @cfg {String} inputType @hide
23178 * @cfg {String} invalidClass @hide
23181 * @cfg {String} invalidText @hide
23184 * @cfg {String} msgFx @hide
23187 * @cfg {String} validateOnBlur @hide
23191 // <script type="text/javascript">
23194 * Ext JS Library 1.1.1
23195 * Copyright(c) 2006-2007, Ext JS, LLC.
23201 * @class Roo.form.HtmlEditorToolbar1
23206 new Roo.form.HtmlEditor({
23209 new Roo.form.HtmlEditorToolbar1({
23210 disable : { fonts: 1 , format: 1, ..., ... , ...],
23216 * @cfg {Object} disable List of elements to disable..
23217 * @cfg {Array} btns List of additional buttons.
23221 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23224 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23227 Roo.apply(this, config);
23229 // default disabled, based on 'good practice'..
23230 this.disable = this.disable || {};
23231 Roo.applyIf(this.disable, {
23234 specialElements : true
23238 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23239 // dont call parent... till later.
23242 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
23249 editorcore : false,
23251 * @cfg {Object} disable List of toolbar elements to disable
23258 * @cfg {String} createLinkText The default text for the create link prompt
23260 createLinkText : 'Please enter the URL for the link:',
23262 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23264 defaultLinkValue : 'http:/'+'/',
23268 * @cfg {Array} fontFamilies An array of available font families
23286 // "á" , ?? a acute?
23291 "°" // , // degrees
23293 // "é" , // e ecute
23294 // "ú" , // u ecute?
23297 specialElements : [
23299 text: "Insert Table",
23302 ihtml : '<table><tr><td>Cell</td></tr></table>'
23306 text: "Insert Image",
23309 ihtml : '<img src="about:blank"/>'
23318 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23319 "input:submit", "input:button", "select", "textarea", "label" ],
23322 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23324 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23333 * @cfg {String} defaultFont default font to use.
23335 defaultFont: 'tahoma',
23337 fontSelect : false,
23340 formatCombo : false,
23342 init : function(editor)
23344 this.editor = editor;
23345 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23346 var editorcore = this.editorcore;
23350 var fid = editorcore.frameId;
23352 function btn(id, toggle, handler){
23353 var xid = fid + '-'+ id ;
23357 cls : 'x-btn-icon x-edit-'+id,
23358 enableToggle:toggle !== false,
23359 scope: _t, // was editor...
23360 handler:handler||_t.relayBtnCmd,
23361 clickEvent:'mousedown',
23362 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23369 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23371 // stop form submits
23372 tb.el.on('click', function(e){
23373 e.preventDefault(); // what does this do?
23376 if(!this.disable.font) { // && !Roo.isSafari){
23377 /* why no safari for fonts
23378 editor.fontSelect = tb.el.createChild({
23381 cls:'x-font-select',
23382 html: this.createFontOptions()
23385 editor.fontSelect.on('change', function(){
23386 var font = editor.fontSelect.dom.value;
23387 editor.relayCmd('fontname', font);
23388 editor.deferFocus();
23392 editor.fontSelect.dom,
23398 if(!this.disable.formats){
23399 this.formatCombo = new Roo.form.ComboBox({
23400 store: new Roo.data.SimpleStore({
23403 data : this.formats // from states.js
23407 //autoCreate : {tag: "div", size: "20"},
23408 displayField:'tag',
23412 triggerAction: 'all',
23413 emptyText:'Add tag',
23414 selectOnFocus:true,
23417 'select': function(c, r, i) {
23418 editorcore.insertTag(r.get('tag'));
23424 tb.addField(this.formatCombo);
23428 if(!this.disable.format){
23433 btn('strikethrough')
23436 if(!this.disable.fontSize){
23441 btn('increasefontsize', false, editorcore.adjustFont),
23442 btn('decreasefontsize', false, editorcore.adjustFont)
23447 if(!this.disable.colors){
23450 id:editorcore.frameId +'-forecolor',
23451 cls:'x-btn-icon x-edit-forecolor',
23452 clickEvent:'mousedown',
23453 tooltip: this.buttonTips['forecolor'] || undefined,
23455 menu : new Roo.menu.ColorMenu({
23456 allowReselect: true,
23457 focus: Roo.emptyFn,
23460 selectHandler: function(cp, color){
23461 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23462 editor.deferFocus();
23465 clickEvent:'mousedown'
23468 id:editorcore.frameId +'backcolor',
23469 cls:'x-btn-icon x-edit-backcolor',
23470 clickEvent:'mousedown',
23471 tooltip: this.buttonTips['backcolor'] || undefined,
23473 menu : new Roo.menu.ColorMenu({
23474 focus: Roo.emptyFn,
23477 allowReselect: true,
23478 selectHandler: function(cp, color){
23480 editorcore.execCmd('useCSS', false);
23481 editorcore.execCmd('hilitecolor', color);
23482 editorcore.execCmd('useCSS', true);
23483 editor.deferFocus();
23485 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23486 Roo.isSafari || Roo.isIE ? '#'+color : color);
23487 editor.deferFocus();
23491 clickEvent:'mousedown'
23496 // now add all the items...
23499 if(!this.disable.alignments){
23502 btn('justifyleft'),
23503 btn('justifycenter'),
23504 btn('justifyright')
23508 //if(!Roo.isSafari){
23509 if(!this.disable.links){
23512 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23516 if(!this.disable.lists){
23519 btn('insertorderedlist'),
23520 btn('insertunorderedlist')
23523 if(!this.disable.sourceEdit){
23526 btn('sourceedit', true, function(btn){
23527 this.toggleSourceEdit(btn.pressed);
23534 // special menu.. - needs to be tidied up..
23535 if (!this.disable.special) {
23538 cls: 'x-edit-none',
23544 for (var i =0; i < this.specialChars.length; i++) {
23545 smenu.menu.items.push({
23547 html: this.specialChars[i],
23548 handler: function(a,b) {
23549 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23550 //editor.insertAtCursor(a.html);
23564 if (!this.disable.cleanStyles) {
23566 cls: 'x-btn-icon x-btn-clear',
23572 for (var i =0; i < this.cleanStyles.length; i++) {
23573 cmenu.menu.items.push({
23574 actiontype : this.cleanStyles[i],
23575 html: 'Remove ' + this.cleanStyles[i],
23576 handler: function(a,b) {
23579 var c = Roo.get(editorcore.doc.body);
23580 c.select('[style]').each(function(s) {
23581 s.dom.style.removeProperty(a.actiontype);
23583 editorcore.syncValue();
23588 cmenu.menu.items.push({
23589 actiontype : 'tablewidths',
23590 html: 'Remove Table Widths',
23591 handler: function(a,b) {
23592 editorcore.cleanTableWidths();
23593 editorcore.syncValue();
23597 cmenu.menu.items.push({
23598 actiontype : 'word',
23599 html: 'Remove MS Word Formating',
23600 handler: function(a,b) {
23601 editorcore.cleanWord();
23602 editorcore.syncValue();
23607 cmenu.menu.items.push({
23608 actiontype : 'all',
23609 html: 'Remove All Styles',
23610 handler: function(a,b) {
23612 var c = Roo.get(editorcore.doc.body);
23613 c.select('[style]').each(function(s) {
23614 s.dom.removeAttribute('style');
23616 editorcore.syncValue();
23621 cmenu.menu.items.push({
23622 actiontype : 'all',
23623 html: 'Remove All CSS Classes',
23624 handler: function(a,b) {
23626 var c = Roo.get(editorcore.doc.body);
23627 c.select('[class]').each(function(s) {
23628 s.dom.removeAttribute('class');
23630 editorcore.cleanWord();
23631 editorcore.syncValue();
23636 cmenu.menu.items.push({
23637 actiontype : 'tidy',
23638 html: 'Tidy HTML Source',
23639 handler: function(a,b) {
23640 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23641 editorcore.syncValue();
23650 if (!this.disable.specialElements) {
23653 cls: 'x-edit-none',
23658 for (var i =0; i < this.specialElements.length; i++) {
23659 semenu.menu.items.push(
23661 handler: function(a,b) {
23662 editor.insertAtCursor(this.ihtml);
23664 }, this.specialElements[i])
23676 for(var i =0; i< this.btns.length;i++) {
23677 var b = Roo.factory(this.btns[i],Roo.form);
23678 b.cls = 'x-edit-none';
23680 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23681 b.cls += ' x-init-enable';
23684 b.scope = editorcore;
23692 // disable everything...
23694 this.tb.items.each(function(item){
23697 item.id != editorcore.frameId+ '-sourceedit' &&
23698 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23704 this.rendered = true;
23706 // the all the btns;
23707 editor.on('editorevent', this.updateToolbar, this);
23708 // other toolbars need to implement this..
23709 //editor.on('editmodechange', this.updateToolbar, this);
23713 relayBtnCmd : function(btn) {
23714 this.editorcore.relayCmd(btn.cmd);
23716 // private used internally
23717 createLink : function(){
23718 Roo.log("create link?");
23719 var url = prompt(this.createLinkText, this.defaultLinkValue);
23720 if(url && url != 'http:/'+'/'){
23721 this.editorcore.relayCmd('createlink', url);
23727 * Protected method that will not generally be called directly. It triggers
23728 * a toolbar update by reading the markup state of the current selection in the editor.
23730 updateToolbar: function(){
23732 if(!this.editorcore.activated){
23733 this.editor.onFirstFocus();
23737 var btns = this.tb.items.map,
23738 doc = this.editorcore.doc,
23739 frameId = this.editorcore.frameId;
23741 if(!this.disable.font && !Roo.isSafari){
23743 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23744 if(name != this.fontSelect.dom.value){
23745 this.fontSelect.dom.value = name;
23749 if(!this.disable.format){
23750 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23751 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23752 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23753 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23755 if(!this.disable.alignments){
23756 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23757 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23758 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23760 if(!Roo.isSafari && !this.disable.lists){
23761 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23762 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23765 var ans = this.editorcore.getAllAncestors();
23766 if (this.formatCombo) {
23769 var store = this.formatCombo.store;
23770 this.formatCombo.setValue("");
23771 for (var i =0; i < ans.length;i++) {
23772 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23774 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23782 // hides menus... - so this cant be on a menu...
23783 Roo.menu.MenuMgr.hideAll();
23785 //this.editorsyncValue();
23789 createFontOptions : function(){
23790 var buf = [], fs = this.fontFamilies, ff, lc;
23794 for(var i = 0, len = fs.length; i< len; i++){
23796 lc = ff.toLowerCase();
23798 '<option value="',lc,'" style="font-family:',ff,';"',
23799 (this.defaultFont == lc ? ' selected="true">' : '>'),
23804 return buf.join('');
23807 toggleSourceEdit : function(sourceEditMode){
23809 Roo.log("toolbar toogle");
23810 if(sourceEditMode === undefined){
23811 sourceEditMode = !this.sourceEditMode;
23813 this.sourceEditMode = sourceEditMode === true;
23814 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23815 // just toggle the button?
23816 if(btn.pressed !== this.sourceEditMode){
23817 btn.toggle(this.sourceEditMode);
23821 if(sourceEditMode){
23822 Roo.log("disabling buttons");
23823 this.tb.items.each(function(item){
23824 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23830 Roo.log("enabling buttons");
23831 if(this.editorcore.initialized){
23832 this.tb.items.each(function(item){
23838 Roo.log("calling toggole on editor");
23839 // tell the editor that it's been pressed..
23840 this.editor.toggleSourceEdit(sourceEditMode);
23844 * Object collection of toolbar tooltips for the buttons in the editor. The key
23845 * is the command id associated with that button and the value is a valid QuickTips object.
23850 title: 'Bold (Ctrl+B)',
23851 text: 'Make the selected text bold.',
23852 cls: 'x-html-editor-tip'
23855 title: 'Italic (Ctrl+I)',
23856 text: 'Make the selected text italic.',
23857 cls: 'x-html-editor-tip'
23865 title: 'Bold (Ctrl+B)',
23866 text: 'Make the selected text bold.',
23867 cls: 'x-html-editor-tip'
23870 title: 'Italic (Ctrl+I)',
23871 text: 'Make the selected text italic.',
23872 cls: 'x-html-editor-tip'
23875 title: 'Underline (Ctrl+U)',
23876 text: 'Underline the selected text.',
23877 cls: 'x-html-editor-tip'
23880 title: 'Strikethrough',
23881 text: 'Strikethrough the selected text.',
23882 cls: 'x-html-editor-tip'
23884 increasefontsize : {
23885 title: 'Grow Text',
23886 text: 'Increase the font size.',
23887 cls: 'x-html-editor-tip'
23889 decreasefontsize : {
23890 title: 'Shrink Text',
23891 text: 'Decrease the font size.',
23892 cls: 'x-html-editor-tip'
23895 title: 'Text Highlight Color',
23896 text: 'Change the background color of the selected text.',
23897 cls: 'x-html-editor-tip'
23900 title: 'Font Color',
23901 text: 'Change the color of the selected text.',
23902 cls: 'x-html-editor-tip'
23905 title: 'Align Text Left',
23906 text: 'Align text to the left.',
23907 cls: 'x-html-editor-tip'
23910 title: 'Center Text',
23911 text: 'Center text in the editor.',
23912 cls: 'x-html-editor-tip'
23915 title: 'Align Text Right',
23916 text: 'Align text to the right.',
23917 cls: 'x-html-editor-tip'
23919 insertunorderedlist : {
23920 title: 'Bullet List',
23921 text: 'Start a bulleted list.',
23922 cls: 'x-html-editor-tip'
23924 insertorderedlist : {
23925 title: 'Numbered List',
23926 text: 'Start a numbered list.',
23927 cls: 'x-html-editor-tip'
23930 title: 'Hyperlink',
23931 text: 'Make the selected text a hyperlink.',
23932 cls: 'x-html-editor-tip'
23935 title: 'Source Edit',
23936 text: 'Switch to source editing mode.',
23937 cls: 'x-html-editor-tip'
23941 onDestroy : function(){
23944 this.tb.items.each(function(item){
23946 item.menu.removeAll();
23948 item.menu.el.destroy();
23956 onFirstFocus: function() {
23957 this.tb.items.each(function(item){
23966 // <script type="text/javascript">
23969 * Ext JS Library 1.1.1
23970 * Copyright(c) 2006-2007, Ext JS, LLC.
23977 * @class Roo.form.HtmlEditor.ToolbarContext
23982 new Roo.form.HtmlEditor({
23985 { xtype: 'ToolbarStandard', styles : {} }
23986 { xtype: 'ToolbarContext', disable : {} }
23992 * @config : {Object} disable List of elements to disable.. (not done yet.)
23993 * @config : {Object} styles Map of styles available.
23997 Roo.form.HtmlEditor.ToolbarContext = function(config)
24000 Roo.apply(this, config);
24001 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24002 // dont call parent... till later.
24003 this.styles = this.styles || {};
24008 Roo.form.HtmlEditor.ToolbarContext.types = {
24020 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24086 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24091 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24101 style : 'fontFamily',
24102 displayField: 'display',
24103 optname : 'font-family',
24152 // should we really allow this??
24153 // should this just be
24164 style : 'fontFamily',
24165 displayField: 'display',
24166 optname : 'font-family',
24173 style : 'fontFamily',
24174 displayField: 'display',
24175 optname : 'font-family',
24182 style : 'fontFamily',
24183 displayField: 'display',
24184 optname : 'font-family',
24195 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24196 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24198 Roo.form.HtmlEditor.ToolbarContext.options = {
24200 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24201 [ 'Courier New', 'Courier New'],
24202 [ 'Tahoma', 'Tahoma'],
24203 [ 'Times New Roman,serif', 'Times'],
24204 [ 'Verdana','Verdana' ]
24208 // fixme - these need to be configurable..
24211 //Roo.form.HtmlEditor.ToolbarContext.types
24214 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
24221 editorcore : false,
24223 * @cfg {Object} disable List of toolbar elements to disable
24228 * @cfg {Object} styles List of styles
24229 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
24231 * These must be defined in the page, so they get rendered correctly..
24242 init : function(editor)
24244 this.editor = editor;
24245 this.editorcore = editor.editorcore ? editor.editorcore : editor;
24246 var editorcore = this.editorcore;
24248 var fid = editorcore.frameId;
24250 function btn(id, toggle, handler){
24251 var xid = fid + '-'+ id ;
24255 cls : 'x-btn-icon x-edit-'+id,
24256 enableToggle:toggle !== false,
24257 scope: editorcore, // was editor...
24258 handler:handler||editorcore.relayBtnCmd,
24259 clickEvent:'mousedown',
24260 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24264 // create a new element.
24265 var wdiv = editor.wrap.createChild({
24267 }, editor.wrap.dom.firstChild.nextSibling, true);
24269 // can we do this more than once??
24271 // stop form submits
24274 // disable everything...
24275 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24276 this.toolbars = {};
24278 for (var i in ty) {
24280 this.toolbars[i] = this.buildToolbar(ty[i],i);
24282 this.tb = this.toolbars.BODY;
24284 this.buildFooter();
24285 this.footer.show();
24286 editor.on('hide', function( ) { this.footer.hide() }, this);
24287 editor.on('show', function( ) { this.footer.show() }, this);
24290 this.rendered = true;
24292 // the all the btns;
24293 editor.on('editorevent', this.updateToolbar, this);
24294 // other toolbars need to implement this..
24295 //editor.on('editmodechange', this.updateToolbar, this);
24301 * Protected method that will not generally be called directly. It triggers
24302 * a toolbar update by reading the markup state of the current selection in the editor.
24304 * Note you can force an update by calling on('editorevent', scope, false)
24306 updateToolbar: function(editor,ev,sel){
24309 // capture mouse up - this is handy for selecting images..
24310 // perhaps should go somewhere else...
24311 if(!this.editorcore.activated){
24312 this.editor.onFirstFocus();
24318 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24319 // selectNode - might want to handle IE?
24321 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24322 ev.target && ev.target.tagName == 'IMG') {
24323 // they have click on an image...
24324 // let's see if we can change the selection...
24327 var nodeRange = sel.ownerDocument.createRange();
24329 nodeRange.selectNode(sel);
24331 nodeRange.selectNodeContents(sel);
24333 //nodeRange.collapse(true);
24334 var s = this.editorcore.win.getSelection();
24335 s.removeAllRanges();
24336 s.addRange(nodeRange);
24340 var updateFooter = sel ? false : true;
24343 var ans = this.editorcore.getAllAncestors();
24346 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24349 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24350 sel = sel ? sel : this.editorcore.doc.body;
24351 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24354 // pick a menu that exists..
24355 var tn = sel.tagName.toUpperCase();
24356 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24358 tn = sel.tagName.toUpperCase();
24360 var lastSel = this.tb.selectedNode;
24362 this.tb.selectedNode = sel;
24364 // if current menu does not match..
24366 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24369 ///console.log("show: " + tn);
24370 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24373 this.tb.items.first().el.innerHTML = tn + ': ';
24376 // update attributes
24377 if (this.tb.fields) {
24378 this.tb.fields.each(function(e) {
24380 e.setValue(sel.style[e.stylename]);
24383 e.setValue(sel.getAttribute(e.attrname));
24387 var hasStyles = false;
24388 for(var i in this.styles) {
24395 var st = this.tb.fields.item(0);
24397 st.store.removeAll();
24400 var cn = sel.className.split(/\s+/);
24403 if (this.styles['*']) {
24405 Roo.each(this.styles['*'], function(v) {
24406 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24409 if (this.styles[tn]) {
24410 Roo.each(this.styles[tn], function(v) {
24411 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24415 st.store.loadData(avs);
24419 // flag our selected Node.
24420 this.tb.selectedNode = sel;
24423 Roo.menu.MenuMgr.hideAll();
24427 if (!updateFooter) {
24428 //this.footDisp.dom.innerHTML = '';
24431 // update the footer
24435 this.footerEls = ans.reverse();
24436 Roo.each(this.footerEls, function(a,i) {
24437 if (!a) { return; }
24438 html += html.length ? ' > ' : '';
24440 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24445 var sz = this.footDisp.up('td').getSize();
24446 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24447 this.footDisp.dom.style.marginLeft = '5px';
24449 this.footDisp.dom.style.overflow = 'hidden';
24451 this.footDisp.dom.innerHTML = html;
24453 //this.editorsyncValue();
24460 onDestroy : function(){
24463 this.tb.items.each(function(item){
24465 item.menu.removeAll();
24467 item.menu.el.destroy();
24475 onFirstFocus: function() {
24476 // need to do this for all the toolbars..
24477 this.tb.items.each(function(item){
24481 buildToolbar: function(tlist, nm)
24483 var editor = this.editor;
24484 var editorcore = this.editorcore;
24485 // create a new element.
24486 var wdiv = editor.wrap.createChild({
24488 }, editor.wrap.dom.firstChild.nextSibling, true);
24491 var tb = new Roo.Toolbar(wdiv);
24494 tb.add(nm+ ": ");
24497 for(var i in this.styles) {
24502 if (styles && styles.length) {
24504 // this needs a multi-select checkbox...
24505 tb.addField( new Roo.form.ComboBox({
24506 store: new Roo.data.SimpleStore({
24508 fields: ['val', 'selected'],
24511 name : '-roo-edit-className',
24512 attrname : 'className',
24513 displayField: 'val',
24517 triggerAction: 'all',
24518 emptyText:'Select Style',
24519 selectOnFocus:true,
24522 'select': function(c, r, i) {
24523 // initial support only for on class per el..
24524 tb.selectedNode.className = r ? r.get('val') : '';
24525 editorcore.syncValue();
24532 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24533 var tbops = tbc.options;
24535 for (var i in tlist) {
24537 var item = tlist[i];
24538 tb.add(item.title + ": ");
24541 //optname == used so you can configure the options available..
24542 var opts = item.opts ? item.opts : false;
24543 if (item.optname) {
24544 opts = tbops[item.optname];
24549 // opts == pulldown..
24550 tb.addField( new Roo.form.ComboBox({
24551 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24553 fields: ['val', 'display'],
24556 name : '-roo-edit-' + i,
24558 stylename : item.style ? item.style : false,
24559 displayField: item.displayField ? item.displayField : 'val',
24560 valueField : 'val',
24562 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24564 triggerAction: 'all',
24565 emptyText:'Select',
24566 selectOnFocus:true,
24567 width: item.width ? item.width : 130,
24569 'select': function(c, r, i) {
24571 tb.selectedNode.style[c.stylename] = r.get('val');
24574 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24583 tb.addField( new Roo.form.TextField({
24586 //allowBlank:false,
24591 tb.addField( new Roo.form.TextField({
24592 name: '-roo-edit-' + i,
24599 'change' : function(f, nv, ov) {
24600 tb.selectedNode.setAttribute(f.attrname, nv);
24601 editorcore.syncValue();
24614 text: 'Stylesheets',
24617 click : function ()
24619 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24627 text: 'Remove Tag',
24630 click : function ()
24633 // undo does not work.
24635 var sn = tb.selectedNode;
24637 var pn = sn.parentNode;
24639 var stn = sn.childNodes[0];
24640 var en = sn.childNodes[sn.childNodes.length - 1 ];
24641 while (sn.childNodes.length) {
24642 var node = sn.childNodes[0];
24643 sn.removeChild(node);
24645 pn.insertBefore(node, sn);
24648 pn.removeChild(sn);
24649 var range = editorcore.createRange();
24651 range.setStart(stn,0);
24652 range.setEnd(en,0); //????
24653 //range.selectNode(sel);
24656 var selection = editorcore.getSelection();
24657 selection.removeAllRanges();
24658 selection.addRange(range);
24662 //_this.updateToolbar(null, null, pn);
24663 _this.updateToolbar(null, null, null);
24664 _this.footDisp.dom.innerHTML = '';
24674 tb.el.on('click', function(e){
24675 e.preventDefault(); // what does this do?
24677 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24680 // dont need to disable them... as they will get hidden
24685 buildFooter : function()
24688 var fel = this.editor.wrap.createChild();
24689 this.footer = new Roo.Toolbar(fel);
24690 // toolbar has scrolly on left / right?
24691 var footDisp= new Roo.Toolbar.Fill();
24697 handler : function() {
24698 _t.footDisp.scrollTo('left',0,true)
24702 this.footer.add( footDisp );
24707 handler : function() {
24709 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24713 var fel = Roo.get(footDisp.el);
24714 fel.addClass('x-editor-context');
24715 this.footDispWrap = fel;
24716 this.footDispWrap.overflow = 'hidden';
24718 this.footDisp = fel.createChild();
24719 this.footDispWrap.on('click', this.onContextClick, this)
24723 onContextClick : function (ev,dom)
24725 ev.preventDefault();
24726 var cn = dom.className;
24728 if (!cn.match(/x-ed-loc-/)) {
24731 var n = cn.split('-').pop();
24732 var ans = this.footerEls;
24736 var range = this.editorcore.createRange();
24738 range.selectNodeContents(sel);
24739 //range.selectNode(sel);
24742 var selection = this.editorcore.getSelection();
24743 selection.removeAllRanges();
24744 selection.addRange(range);
24748 this.updateToolbar(null, null, sel);
24765 * Ext JS Library 1.1.1
24766 * Copyright(c) 2006-2007, Ext JS, LLC.
24768 * Originally Released Under LGPL - original licence link has changed is not relivant.
24771 * <script type="text/javascript">
24775 * @class Roo.form.BasicForm
24776 * @extends Roo.util.Observable
24777 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24779 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24780 * @param {Object} config Configuration options
24782 Roo.form.BasicForm = function(el, config){
24783 this.allItems = [];
24784 this.childForms = [];
24785 Roo.apply(this, config);
24787 * The Roo.form.Field items in this form.
24788 * @type MixedCollection
24792 this.items = new Roo.util.MixedCollection(false, function(o){
24793 return o.id || (o.id = Roo.id());
24797 * @event beforeaction
24798 * Fires before any action is performed. Return false to cancel the action.
24799 * @param {Form} this
24800 * @param {Action} action The action to be performed
24802 beforeaction: true,
24804 * @event actionfailed
24805 * Fires when an action fails.
24806 * @param {Form} this
24807 * @param {Action} action The action that failed
24809 actionfailed : true,
24811 * @event actioncomplete
24812 * Fires when an action is completed.
24813 * @param {Form} this
24814 * @param {Action} action The action that completed
24816 actioncomplete : true
24821 Roo.form.BasicForm.superclass.constructor.call(this);
24823 Roo.form.BasicForm.popover.apply();
24826 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24828 * @cfg {String} method
24829 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24832 * @cfg {DataReader} reader
24833 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24834 * This is optional as there is built-in support for processing JSON.
24837 * @cfg {DataReader} errorReader
24838 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24839 * This is completely optional as there is built-in support for processing JSON.
24842 * @cfg {String} url
24843 * The URL to use for form actions if one isn't supplied in the action options.
24846 * @cfg {Boolean} fileUpload
24847 * Set to true if this form is a file upload.
24851 * @cfg {Object} baseParams
24852 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24857 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24862 activeAction : null,
24865 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24866 * or setValues() data instead of when the form was first created.
24868 trackResetOnLoad : false,
24872 * childForms - used for multi-tab forms
24875 childForms : false,
24878 * allItems - full list of fields.
24884 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24885 * element by passing it or its id or mask the form itself by passing in true.
24888 waitMsgTarget : false,
24893 disableMask : false,
24896 * @cfg {Boolean} errorMask (true|false) default false
24901 * @cfg {Number} maskOffset Default 100
24906 initEl : function(el){
24907 this.el = Roo.get(el);
24908 this.id = this.el.id || Roo.id();
24909 this.el.on('submit', this.onSubmit, this);
24910 this.el.addClass('x-form');
24914 onSubmit : function(e){
24919 * Returns true if client-side validation on the form is successful.
24922 isValid : function(){
24924 var target = false;
24925 this.items.each(function(f){
24932 if(!target && f.el.isVisible(true)){
24937 if(this.errorMask && !valid){
24938 Roo.form.BasicForm.popover.mask(this, target);
24945 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24948 isDirty : function(){
24950 this.items.each(function(f){
24960 * Returns true if any fields in this form have changed since their original load. (New version)
24964 hasChanged : function()
24967 this.items.each(function(f){
24968 if(f.hasChanged()){
24977 * Resets all hasChanged to 'false' -
24978 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24979 * So hasChanged storage is only to be used for this purpose
24982 resetHasChanged : function()
24984 this.items.each(function(f){
24985 f.resetHasChanged();
24992 * Performs a predefined action (submit or load) or custom actions you define on this form.
24993 * @param {String} actionName The name of the action type
24994 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24995 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24996 * accept other config options):
24998 Property Type Description
24999 ---------------- --------------- ----------------------------------------------------------------------------------
25000 url String The url for the action (defaults to the form's url)
25001 method String The form method to use (defaults to the form's method, or POST if not defined)
25002 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
25003 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
25004 validate the form on the client (defaults to false)
25006 * @return {BasicForm} this
25008 doAction : function(action, options){
25009 if(typeof action == 'string'){
25010 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25012 if(this.fireEvent('beforeaction', this, action) !== false){
25013 this.beforeAction(action);
25014 action.run.defer(100, action);
25020 * Shortcut to do a submit action.
25021 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25022 * @return {BasicForm} this
25024 submit : function(options){
25025 this.doAction('submit', options);
25030 * Shortcut to do a load action.
25031 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25032 * @return {BasicForm} this
25034 load : function(options){
25035 this.doAction('load', options);
25040 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25041 * @param {Record} record The record to edit
25042 * @return {BasicForm} this
25044 updateRecord : function(record){
25045 record.beginEdit();
25046 var fs = record.fields;
25047 fs.each(function(f){
25048 var field = this.findField(f.name);
25050 record.set(f.name, field.getValue());
25058 * Loads an Roo.data.Record into this form.
25059 * @param {Record} record The record to load
25060 * @return {BasicForm} this
25062 loadRecord : function(record){
25063 this.setValues(record.data);
25068 beforeAction : function(action){
25069 var o = action.options;
25071 if(!this.disableMask) {
25072 if(this.waitMsgTarget === true){
25073 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25074 }else if(this.waitMsgTarget){
25075 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25076 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25078 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25086 afterAction : function(action, success){
25087 this.activeAction = null;
25088 var o = action.options;
25090 if(!this.disableMask) {
25091 if(this.waitMsgTarget === true){
25093 }else if(this.waitMsgTarget){
25094 this.waitMsgTarget.unmask();
25096 Roo.MessageBox.updateProgress(1);
25097 Roo.MessageBox.hide();
25105 Roo.callback(o.success, o.scope, [this, action]);
25106 this.fireEvent('actioncomplete', this, action);
25110 // failure condition..
25111 // we have a scenario where updates need confirming.
25112 // eg. if a locking scenario exists..
25113 // we look for { errors : { needs_confirm : true }} in the response.
25115 (typeof(action.result) != 'undefined') &&
25116 (typeof(action.result.errors) != 'undefined') &&
25117 (typeof(action.result.errors.needs_confirm) != 'undefined')
25120 Roo.MessageBox.confirm(
25121 "Change requires confirmation",
25122 action.result.errorMsg,
25127 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
25137 Roo.callback(o.failure, o.scope, [this, action]);
25138 // show an error message if no failed handler is set..
25139 if (!this.hasListener('actionfailed')) {
25140 Roo.MessageBox.alert("Error",
25141 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25142 action.result.errorMsg :
25143 "Saving Failed, please check your entries or try again"
25147 this.fireEvent('actionfailed', this, action);
25153 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25154 * @param {String} id The value to search for
25157 findField : function(id){
25158 var field = this.items.get(id);
25160 this.items.each(function(f){
25161 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25167 return field || null;
25171 * Add a secondary form to this one,
25172 * Used to provide tabbed forms. One form is primary, with hidden values
25173 * which mirror the elements from the other forms.
25175 * @param {Roo.form.Form} form to add.
25178 addForm : function(form)
25181 if (this.childForms.indexOf(form) > -1) {
25185 this.childForms.push(form);
25187 Roo.each(form.allItems, function (fe) {
25189 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25190 if (this.findField(n)) { // already added..
25193 var add = new Roo.form.Hidden({
25196 add.render(this.el);
25203 * Mark fields in this form invalid in bulk.
25204 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25205 * @return {BasicForm} this
25207 markInvalid : function(errors){
25208 if(errors instanceof Array){
25209 for(var i = 0, len = errors.length; i < len; i++){
25210 var fieldError = errors[i];
25211 var f = this.findField(fieldError.id);
25213 f.markInvalid(fieldError.msg);
25219 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25220 field.markInvalid(errors[id]);
25224 Roo.each(this.childForms || [], function (f) {
25225 f.markInvalid(errors);
25232 * Set values for fields in this form in bulk.
25233 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25234 * @return {BasicForm} this
25236 setValues : function(values){
25237 if(values instanceof Array){ // array of objects
25238 for(var i = 0, len = values.length; i < len; i++){
25240 var f = this.findField(v.id);
25242 f.setValue(v.value);
25243 if(this.trackResetOnLoad){
25244 f.originalValue = f.getValue();
25248 }else{ // object hash
25251 if(typeof values[id] != 'function' && (field = this.findField(id))){
25253 if (field.setFromData &&
25254 field.valueField &&
25255 field.displayField &&
25256 // combos' with local stores can
25257 // be queried via setValue()
25258 // to set their value..
25259 (field.store && !field.store.isLocal)
25263 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25264 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25265 field.setFromData(sd);
25268 field.setValue(values[id]);
25272 if(this.trackResetOnLoad){
25273 field.originalValue = field.getValue();
25278 this.resetHasChanged();
25281 Roo.each(this.childForms || [], function (f) {
25282 f.setValues(values);
25283 f.resetHasChanged();
25290 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25291 * they are returned as an array.
25292 * @param {Boolean} asString
25295 getValues : function(asString){
25296 if (this.childForms) {
25297 // copy values from the child forms
25298 Roo.each(this.childForms, function (f) {
25299 this.setValues(f.getValues());
25304 if (typeof(FormData) != 'undefined' && asString !== true) {
25305 var fd = (new FormData(this.el.dom)).entries();
25307 var ent = fd.next();
25308 while (!ent.done) {
25309 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25316 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25317 if(asString === true){
25320 return Roo.urlDecode(fs);
25324 * Returns the fields in this form as an object with key/value pairs.
25325 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25328 getFieldValues : function(with_hidden)
25330 if (this.childForms) {
25331 // copy values from the child forms
25332 // should this call getFieldValues - probably not as we do not currently copy
25333 // hidden fields when we generate..
25334 Roo.each(this.childForms, function (f) {
25335 this.setValues(f.getValues());
25340 this.items.each(function(f){
25341 if (!f.getName()) {
25344 var v = f.getValue();
25345 if (f.inputType =='radio') {
25346 if (typeof(ret[f.getName()]) == 'undefined') {
25347 ret[f.getName()] = ''; // empty..
25350 if (!f.el.dom.checked) {
25354 v = f.el.dom.value;
25358 // not sure if this supported any more..
25359 if ((typeof(v) == 'object') && f.getRawValue) {
25360 v = f.getRawValue() ; // dates..
25362 // combo boxes where name != hiddenName...
25363 if (f.name != f.getName()) {
25364 ret[f.name] = f.getRawValue();
25366 ret[f.getName()] = v;
25373 * Clears all invalid messages in this form.
25374 * @return {BasicForm} this
25376 clearInvalid : function(){
25377 this.items.each(function(f){
25381 Roo.each(this.childForms || [], function (f) {
25390 * Resets this form.
25391 * @return {BasicForm} this
25393 reset : function(){
25394 this.items.each(function(f){
25398 Roo.each(this.childForms || [], function (f) {
25401 this.resetHasChanged();
25407 * Add Roo.form components to this form.
25408 * @param {Field} field1
25409 * @param {Field} field2 (optional)
25410 * @param {Field} etc (optional)
25411 * @return {BasicForm} this
25414 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25420 * Removes a field from the items collection (does NOT remove its markup).
25421 * @param {Field} field
25422 * @return {BasicForm} this
25424 remove : function(field){
25425 this.items.remove(field);
25430 * Looks at the fields in this form, checks them for an id attribute,
25431 * and calls applyTo on the existing dom element with that id.
25432 * @return {BasicForm} this
25434 render : function(){
25435 this.items.each(function(f){
25436 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25444 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25445 * @param {Object} values
25446 * @return {BasicForm} this
25448 applyToFields : function(o){
25449 this.items.each(function(f){
25456 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25457 * @param {Object} values
25458 * @return {BasicForm} this
25460 applyIfToFields : function(o){
25461 this.items.each(function(f){
25469 Roo.BasicForm = Roo.form.BasicForm;
25471 Roo.apply(Roo.form.BasicForm, {
25485 intervalID : false,
25491 if(this.isApplied){
25496 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25497 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25498 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25499 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25502 this.maskEl.top.enableDisplayMode("block");
25503 this.maskEl.left.enableDisplayMode("block");
25504 this.maskEl.bottom.enableDisplayMode("block");
25505 this.maskEl.right.enableDisplayMode("block");
25507 Roo.get(document.body).on('click', function(){
25511 Roo.get(document.body).on('touchstart', function(){
25515 this.isApplied = true
25518 mask : function(form, target)
25522 this.target = target;
25524 if(!this.form.errorMask || !target.el){
25528 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25530 var ot = this.target.el.calcOffsetsTo(scrollable);
25532 var scrollTo = ot[1] - this.form.maskOffset;
25534 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25536 scrollable.scrollTo('top', scrollTo);
25538 var el = this.target.wrap || this.target.el;
25540 var box = el.getBox();
25542 this.maskEl.top.setStyle('position', 'absolute');
25543 this.maskEl.top.setStyle('z-index', 10000);
25544 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25545 this.maskEl.top.setLeft(0);
25546 this.maskEl.top.setTop(0);
25547 this.maskEl.top.show();
25549 this.maskEl.left.setStyle('position', 'absolute');
25550 this.maskEl.left.setStyle('z-index', 10000);
25551 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25552 this.maskEl.left.setLeft(0);
25553 this.maskEl.left.setTop(box.y - this.padding);
25554 this.maskEl.left.show();
25556 this.maskEl.bottom.setStyle('position', 'absolute');
25557 this.maskEl.bottom.setStyle('z-index', 10000);
25558 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25559 this.maskEl.bottom.setLeft(0);
25560 this.maskEl.bottom.setTop(box.bottom + this.padding);
25561 this.maskEl.bottom.show();
25563 this.maskEl.right.setStyle('position', 'absolute');
25564 this.maskEl.right.setStyle('z-index', 10000);
25565 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25566 this.maskEl.right.setLeft(box.right + this.padding);
25567 this.maskEl.right.setTop(box.y - this.padding);
25568 this.maskEl.right.show();
25570 this.intervalID = window.setInterval(function() {
25571 Roo.form.BasicForm.popover.unmask();
25574 window.onwheel = function(){ return false;};
25576 (function(){ this.isMasked = true; }).defer(500, this);
25580 unmask : function()
25582 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25586 this.maskEl.top.setStyle('position', 'absolute');
25587 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25588 this.maskEl.top.hide();
25590 this.maskEl.left.setStyle('position', 'absolute');
25591 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25592 this.maskEl.left.hide();
25594 this.maskEl.bottom.setStyle('position', 'absolute');
25595 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25596 this.maskEl.bottom.hide();
25598 this.maskEl.right.setStyle('position', 'absolute');
25599 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25600 this.maskEl.right.hide();
25602 window.onwheel = function(){ return true;};
25604 if(this.intervalID){
25605 window.clearInterval(this.intervalID);
25606 this.intervalID = false;
25609 this.isMasked = false;
25617 * Ext JS Library 1.1.1
25618 * Copyright(c) 2006-2007, Ext JS, LLC.
25620 * Originally Released Under LGPL - original licence link has changed is not relivant.
25623 * <script type="text/javascript">
25627 * @class Roo.form.Form
25628 * @extends Roo.form.BasicForm
25629 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25631 * @param {Object} config Configuration options
25633 Roo.form.Form = function(config){
25635 if (config.items) {
25636 xitems = config.items;
25637 delete config.items;
25641 Roo.form.Form.superclass.constructor.call(this, null, config);
25642 this.url = this.url || this.action;
25644 this.root = new Roo.form.Layout(Roo.applyIf({
25648 this.active = this.root;
25650 * Array of all the buttons that have been added to this form via {@link addButton}
25654 this.allItems = [];
25657 * @event clientvalidation
25658 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25659 * @param {Form} this
25660 * @param {Boolean} valid true if the form has passed client-side validation
25662 clientvalidation: true,
25665 * Fires when the form is rendered
25666 * @param {Roo.form.Form} form
25671 if (this.progressUrl) {
25672 // push a hidden field onto the list of fields..
25676 name : 'UPLOAD_IDENTIFIER'
25681 Roo.each(xitems, this.addxtype, this);
25685 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25687 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25690 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25693 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25695 buttonAlign:'center',
25698 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25703 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25704 * This property cascades to child containers if not set.
25709 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25710 * fires a looping event with that state. This is required to bind buttons to the valid
25711 * state using the config value formBind:true on the button.
25713 monitorValid : false,
25716 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25721 * @cfg {String} progressUrl - Url to return progress data
25724 progressUrl : false,
25726 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25727 * sending a formdata with extra parameters - eg uploaded elements.
25733 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25734 * fields are added and the column is closed. If no fields are passed the column remains open
25735 * until end() is called.
25736 * @param {Object} config The config to pass to the column
25737 * @param {Field} field1 (optional)
25738 * @param {Field} field2 (optional)
25739 * @param {Field} etc (optional)
25740 * @return Column The column container object
25742 column : function(c){
25743 var col = new Roo.form.Column(c);
25745 if(arguments.length > 1){ // duplicate code required because of Opera
25746 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25753 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25754 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25755 * until end() is called.
25756 * @param {Object} config The config to pass to the fieldset
25757 * @param {Field} field1 (optional)
25758 * @param {Field} field2 (optional)
25759 * @param {Field} etc (optional)
25760 * @return FieldSet The fieldset container object
25762 fieldset : function(c){
25763 var fs = new Roo.form.FieldSet(c);
25765 if(arguments.length > 1){ // duplicate code required because of Opera
25766 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25773 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25774 * fields are added and the container is closed. If no fields are passed the container remains open
25775 * until end() is called.
25776 * @param {Object} config The config to pass to the Layout
25777 * @param {Field} field1 (optional)
25778 * @param {Field} field2 (optional)
25779 * @param {Field} etc (optional)
25780 * @return Layout The container object
25782 container : function(c){
25783 var l = new Roo.form.Layout(c);
25785 if(arguments.length > 1){ // duplicate code required because of Opera
25786 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25793 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25794 * @param {Object} container A Roo.form.Layout or subclass of Layout
25795 * @return {Form} this
25797 start : function(c){
25798 // cascade label info
25799 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25800 this.active.stack.push(c);
25801 c.ownerCt = this.active;
25807 * Closes the current open container
25808 * @return {Form} this
25811 if(this.active == this.root){
25814 this.active = this.active.ownerCt;
25819 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25820 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25821 * as the label of the field.
25822 * @param {Field} field1
25823 * @param {Field} field2 (optional)
25824 * @param {Field} etc. (optional)
25825 * @return {Form} this
25828 this.active.stack.push.apply(this.active.stack, arguments);
25829 this.allItems.push.apply(this.allItems,arguments);
25831 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25832 if(a[i].isFormField){
25837 Roo.form.Form.superclass.add.apply(this, r);
25847 * Find any element that has been added to a form, using it's ID or name
25848 * This can include framesets, columns etc. along with regular fields..
25849 * @param {String} id - id or name to find.
25851 * @return {Element} e - or false if nothing found.
25853 findbyId : function(id)
25859 Roo.each(this.allItems, function(f){
25860 if (f.id == id || f.name == id ){
25871 * Render this form into the passed container. This should only be called once!
25872 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25873 * @return {Form} this
25875 render : function(ct)
25881 var o = this.autoCreate || {
25883 method : this.method || 'POST',
25884 id : this.id || Roo.id()
25886 this.initEl(ct.createChild(o));
25888 this.root.render(this.el);
25892 this.items.each(function(f){
25893 f.render('x-form-el-'+f.id);
25896 if(this.buttons.length > 0){
25897 // tables are required to maintain order and for correct IE layout
25898 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25899 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25900 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25902 var tr = tb.getElementsByTagName('tr')[0];
25903 for(var i = 0, len = this.buttons.length; i < len; i++) {
25904 var b = this.buttons[i];
25905 var td = document.createElement('td');
25906 td.className = 'x-form-btn-td';
25907 b.render(tr.appendChild(td));
25910 if(this.monitorValid){ // initialize after render
25911 this.startMonitoring();
25913 this.fireEvent('rendered', this);
25918 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25919 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25920 * object or a valid Roo.DomHelper element config
25921 * @param {Function} handler The function called when the button is clicked
25922 * @param {Object} scope (optional) The scope of the handler function
25923 * @return {Roo.Button}
25925 addButton : function(config, handler, scope){
25929 minWidth: this.minButtonWidth,
25932 if(typeof config == "string"){
25935 Roo.apply(bc, config);
25937 var btn = new Roo.Button(null, bc);
25938 this.buttons.push(btn);
25943 * Adds a series of form elements (using the xtype property as the factory method.
25944 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25945 * @param {Object} config
25948 addxtype : function()
25950 var ar = Array.prototype.slice.call(arguments, 0);
25952 for(var i = 0; i < ar.length; i++) {
25954 continue; // skip -- if this happends something invalid got sent, we
25955 // should ignore it, as basically that interface element will not show up
25956 // and that should be pretty obvious!!
25959 if (Roo.form[ar[i].xtype]) {
25961 var fe = Roo.factory(ar[i], Roo.form);
25967 fe.store.form = this;
25972 this.allItems.push(fe);
25973 if (fe.items && fe.addxtype) {
25974 fe.addxtype.apply(fe, fe.items);
25984 // console.log('adding ' + ar[i].xtype);
25986 if (ar[i].xtype == 'Button') {
25987 //console.log('adding button');
25988 //console.log(ar[i]);
25989 this.addButton(ar[i]);
25990 this.allItems.push(fe);
25994 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25995 alert('end is not supported on xtype any more, use items');
25997 // //console.log('adding end');
26005 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26006 * option "monitorValid"
26008 startMonitoring : function(){
26011 Roo.TaskMgr.start({
26012 run : this.bindHandler,
26013 interval : this.monitorPoll || 200,
26020 * Stops monitoring of the valid state of this form
26022 stopMonitoring : function(){
26023 this.bound = false;
26027 bindHandler : function(){
26029 return false; // stops binding
26032 this.items.each(function(f){
26033 if(!f.isValid(true)){
26038 for(var i = 0, len = this.buttons.length; i < len; i++){
26039 var btn = this.buttons[i];
26040 if(btn.formBind === true && btn.disabled === valid){
26041 btn.setDisabled(!valid);
26044 this.fireEvent('clientvalidation', this, valid);
26058 Roo.Form = Roo.form.Form;
26061 * Ext JS Library 1.1.1
26062 * Copyright(c) 2006-2007, Ext JS, LLC.
26064 * Originally Released Under LGPL - original licence link has changed is not relivant.
26067 * <script type="text/javascript">
26070 // as we use this in bootstrap.
26071 Roo.namespace('Roo.form');
26073 * @class Roo.form.Action
26074 * Internal Class used to handle form actions
26076 * @param {Roo.form.BasicForm} el The form element or its id
26077 * @param {Object} config Configuration options
26082 // define the action interface
26083 Roo.form.Action = function(form, options){
26085 this.options = options || {};
26088 * Client Validation Failed
26091 Roo.form.Action.CLIENT_INVALID = 'client';
26093 * Server Validation Failed
26096 Roo.form.Action.SERVER_INVALID = 'server';
26098 * Connect to Server Failed
26101 Roo.form.Action.CONNECT_FAILURE = 'connect';
26103 * Reading Data from Server Failed
26106 Roo.form.Action.LOAD_FAILURE = 'load';
26108 Roo.form.Action.prototype = {
26110 failureType : undefined,
26111 response : undefined,
26112 result : undefined,
26114 // interface method
26115 run : function(options){
26119 // interface method
26120 success : function(response){
26124 // interface method
26125 handleResponse : function(response){
26129 // default connection failure
26130 failure : function(response){
26132 this.response = response;
26133 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26134 this.form.afterAction(this, false);
26137 processResponse : function(response){
26138 this.response = response;
26139 if(!response.responseText){
26142 this.result = this.handleResponse(response);
26143 return this.result;
26146 // utility functions used internally
26147 getUrl : function(appendParams){
26148 var url = this.options.url || this.form.url || this.form.el.dom.action;
26150 var p = this.getParams();
26152 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26158 getMethod : function(){
26159 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26162 getParams : function(){
26163 var bp = this.form.baseParams;
26164 var p = this.options.params;
26166 if(typeof p == "object"){
26167 p = Roo.urlEncode(Roo.applyIf(p, bp));
26168 }else if(typeof p == 'string' && bp){
26169 p += '&' + Roo.urlEncode(bp);
26172 p = Roo.urlEncode(bp);
26177 createCallback : function(){
26179 success: this.success,
26180 failure: this.failure,
26182 timeout: (this.form.timeout*1000),
26183 upload: this.form.fileUpload ? this.success : undefined
26188 Roo.form.Action.Submit = function(form, options){
26189 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26192 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26195 haveProgress : false,
26196 uploadComplete : false,
26198 // uploadProgress indicator.
26199 uploadProgress : function()
26201 if (!this.form.progressUrl) {
26205 if (!this.haveProgress) {
26206 Roo.MessageBox.progress("Uploading", "Uploading");
26208 if (this.uploadComplete) {
26209 Roo.MessageBox.hide();
26213 this.haveProgress = true;
26215 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26217 var c = new Roo.data.Connection();
26219 url : this.form.progressUrl,
26224 success : function(req){
26225 //console.log(data);
26229 rdata = Roo.decode(req.responseText)
26231 Roo.log("Invalid data from server..");
26235 if (!rdata || !rdata.success) {
26237 Roo.MessageBox.alert(Roo.encode(rdata));
26240 var data = rdata.data;
26242 if (this.uploadComplete) {
26243 Roo.MessageBox.hide();
26248 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26249 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26252 this.uploadProgress.defer(2000,this);
26255 failure: function(data) {
26256 Roo.log('progress url failed ');
26267 // run get Values on the form, so it syncs any secondary forms.
26268 this.form.getValues();
26270 var o = this.options;
26271 var method = this.getMethod();
26272 var isPost = method == 'POST';
26273 if(o.clientValidation === false || this.form.isValid()){
26275 if (this.form.progressUrl) {
26276 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26277 (new Date() * 1) + '' + Math.random());
26282 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26283 form:this.form.el.dom,
26284 url:this.getUrl(!isPost),
26286 params:isPost ? this.getParams() : null,
26287 isUpload: this.form.fileUpload,
26288 formData : this.form.formData
26291 this.uploadProgress();
26293 }else if (o.clientValidation !== false){ // client validation failed
26294 this.failureType = Roo.form.Action.CLIENT_INVALID;
26295 this.form.afterAction(this, false);
26299 success : function(response)
26301 this.uploadComplete= true;
26302 if (this.haveProgress) {
26303 Roo.MessageBox.hide();
26307 var result = this.processResponse(response);
26308 if(result === true || result.success){
26309 this.form.afterAction(this, true);
26313 this.form.markInvalid(result.errors);
26314 this.failureType = Roo.form.Action.SERVER_INVALID;
26316 this.form.afterAction(this, false);
26318 failure : function(response)
26320 this.uploadComplete= true;
26321 if (this.haveProgress) {
26322 Roo.MessageBox.hide();
26325 this.response = response;
26326 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26327 this.form.afterAction(this, false);
26330 handleResponse : function(response){
26331 if(this.form.errorReader){
26332 var rs = this.form.errorReader.read(response);
26335 for(var i = 0, len = rs.records.length; i < len; i++) {
26336 var r = rs.records[i];
26337 errors[i] = r.data;
26340 if(errors.length < 1){
26344 success : rs.success,
26350 ret = Roo.decode(response.responseText);
26354 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26364 Roo.form.Action.Load = function(form, options){
26365 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26366 this.reader = this.form.reader;
26369 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26374 Roo.Ajax.request(Roo.apply(
26375 this.createCallback(), {
26376 method:this.getMethod(),
26377 url:this.getUrl(false),
26378 params:this.getParams()
26382 success : function(response){
26384 var result = this.processResponse(response);
26385 if(result === true || !result.success || !result.data){
26386 this.failureType = Roo.form.Action.LOAD_FAILURE;
26387 this.form.afterAction(this, false);
26390 this.form.clearInvalid();
26391 this.form.setValues(result.data);
26392 this.form.afterAction(this, true);
26395 handleResponse : function(response){
26396 if(this.form.reader){
26397 var rs = this.form.reader.read(response);
26398 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26400 success : rs.success,
26404 return Roo.decode(response.responseText);
26408 Roo.form.Action.ACTION_TYPES = {
26409 'load' : Roo.form.Action.Load,
26410 'submit' : Roo.form.Action.Submit
26413 * Ext JS Library 1.1.1
26414 * Copyright(c) 2006-2007, Ext JS, LLC.
26416 * Originally Released Under LGPL - original licence link has changed is not relivant.
26419 * <script type="text/javascript">
26423 * @class Roo.form.Layout
26424 * @extends Roo.Component
26425 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26427 * @param {Object} config Configuration options
26429 Roo.form.Layout = function(config){
26431 if (config.items) {
26432 xitems = config.items;
26433 delete config.items;
26435 Roo.form.Layout.superclass.constructor.call(this, config);
26437 Roo.each(xitems, this.addxtype, this);
26441 Roo.extend(Roo.form.Layout, Roo.Component, {
26443 * @cfg {String/Object} autoCreate
26444 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26447 * @cfg {String/Object/Function} style
26448 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26449 * a function which returns such a specification.
26452 * @cfg {String} labelAlign
26453 * Valid values are "left," "top" and "right" (defaults to "left")
26456 * @cfg {Number} labelWidth
26457 * Fixed width in pixels of all field labels (defaults to undefined)
26460 * @cfg {Boolean} clear
26461 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26465 * @cfg {String} labelSeparator
26466 * The separator to use after field labels (defaults to ':')
26468 labelSeparator : ':',
26470 * @cfg {Boolean} hideLabels
26471 * True to suppress the display of field labels in this layout (defaults to false)
26473 hideLabels : false,
26476 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26481 onRender : function(ct, position){
26482 if(this.el){ // from markup
26483 this.el = Roo.get(this.el);
26484 }else { // generate
26485 var cfg = this.getAutoCreate();
26486 this.el = ct.createChild(cfg, position);
26489 this.el.applyStyles(this.style);
26491 if(this.labelAlign){
26492 this.el.addClass('x-form-label-'+this.labelAlign);
26494 if(this.hideLabels){
26495 this.labelStyle = "display:none";
26496 this.elementStyle = "padding-left:0;";
26498 if(typeof this.labelWidth == 'number'){
26499 this.labelStyle = "width:"+this.labelWidth+"px;";
26500 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26502 if(this.labelAlign == 'top'){
26503 this.labelStyle = "width:auto;";
26504 this.elementStyle = "padding-left:0;";
26507 var stack = this.stack;
26508 var slen = stack.length;
26510 if(!this.fieldTpl){
26511 var t = new Roo.Template(
26512 '<div class="x-form-item {5}">',
26513 '<label for="{0}" style="{2}">{1}{4}</label>',
26514 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26516 '</div><div class="x-form-clear-left"></div>'
26518 t.disableFormats = true;
26520 Roo.form.Layout.prototype.fieldTpl = t;
26522 for(var i = 0; i < slen; i++) {
26523 if(stack[i].isFormField){
26524 this.renderField(stack[i]);
26526 this.renderComponent(stack[i]);
26531 this.el.createChild({cls:'x-form-clear'});
26536 renderField : function(f){
26537 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26540 f.labelStyle||this.labelStyle||'', //2
26541 this.elementStyle||'', //3
26542 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26543 f.itemCls||this.itemCls||'' //5
26544 ], true).getPrevSibling());
26548 renderComponent : function(c){
26549 c.render(c.isLayout ? this.el : this.el.createChild());
26552 * Adds a object form elements (using the xtype property as the factory method.)
26553 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26554 * @param {Object} config
26556 addxtype : function(o)
26558 // create the lement.
26559 o.form = this.form;
26560 var fe = Roo.factory(o, Roo.form);
26561 this.form.allItems.push(fe);
26562 this.stack.push(fe);
26564 if (fe.isFormField) {
26565 this.form.items.add(fe);
26573 * @class Roo.form.Column
26574 * @extends Roo.form.Layout
26575 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26577 * @param {Object} config Configuration options
26579 Roo.form.Column = function(config){
26580 Roo.form.Column.superclass.constructor.call(this, config);
26583 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26585 * @cfg {Number/String} width
26586 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26589 * @cfg {String/Object} autoCreate
26590 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26594 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26597 onRender : function(ct, position){
26598 Roo.form.Column.superclass.onRender.call(this, ct, position);
26600 this.el.setWidth(this.width);
26607 * @class Roo.form.Row
26608 * @extends Roo.form.Layout
26609 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26611 * @param {Object} config Configuration options
26615 Roo.form.Row = function(config){
26616 Roo.form.Row.superclass.constructor.call(this, config);
26619 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26621 * @cfg {Number/String} width
26622 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26625 * @cfg {Number/String} height
26626 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26628 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26632 onRender : function(ct, position){
26633 //console.log('row render');
26635 var t = new Roo.Template(
26636 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26637 '<label for="{0}" style="{2}">{1}{4}</label>',
26638 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26642 t.disableFormats = true;
26644 Roo.form.Layout.prototype.rowTpl = t;
26646 this.fieldTpl = this.rowTpl;
26648 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26649 var labelWidth = 100;
26651 if ((this.labelAlign != 'top')) {
26652 if (typeof this.labelWidth == 'number') {
26653 labelWidth = this.labelWidth
26655 this.padWidth = 20 + labelWidth;
26659 Roo.form.Column.superclass.onRender.call(this, ct, position);
26661 this.el.setWidth(this.width);
26664 this.el.setHeight(this.height);
26669 renderField : function(f){
26670 f.fieldEl = this.fieldTpl.append(this.el, [
26671 f.id, f.fieldLabel,
26672 f.labelStyle||this.labelStyle||'',
26673 this.elementStyle||'',
26674 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26675 f.itemCls||this.itemCls||'',
26676 f.width ? f.width + this.padWidth : 160 + this.padWidth
26683 * @class Roo.form.FieldSet
26684 * @extends Roo.form.Layout
26685 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26687 * @param {Object} config Configuration options
26689 Roo.form.FieldSet = function(config){
26690 Roo.form.FieldSet.superclass.constructor.call(this, config);
26693 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26695 * @cfg {String} legend
26696 * The text to display as the legend for the FieldSet (defaults to '')
26699 * @cfg {String/Object} autoCreate
26700 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26704 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26707 onRender : function(ct, position){
26708 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26710 this.setLegend(this.legend);
26715 setLegend : function(text){
26717 this.el.child('legend').update(text);
26722 * Ext JS Library 1.1.1
26723 * Copyright(c) 2006-2007, Ext JS, LLC.
26725 * Originally Released Under LGPL - original licence link has changed is not relivant.
26728 * <script type="text/javascript">
26731 * @class Roo.form.VTypes
26732 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26735 Roo.form.VTypes = function(){
26736 // closure these in so they are only created once.
26737 var alpha = /^[a-zA-Z_]+$/;
26738 var alphanum = /^[a-zA-Z0-9_]+$/;
26739 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26740 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26742 // All these messages and functions are configurable
26745 * The function used to validate email addresses
26746 * @param {String} value The email address
26748 'email' : function(v){
26749 return email.test(v);
26752 * The error text to display when the email validation function returns false
26755 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26757 * The keystroke filter mask to be applied on email input
26760 'emailMask' : /[a-z0-9_\.\-@]/i,
26763 * The function used to validate URLs
26764 * @param {String} value The URL
26766 'url' : function(v){
26767 return url.test(v);
26770 * The error text to display when the url validation function returns false
26773 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26776 * The function used to validate alpha values
26777 * @param {String} value The value
26779 'alpha' : function(v){
26780 return alpha.test(v);
26783 * The error text to display when the alpha validation function returns false
26786 'alphaText' : 'This field should only contain letters and _',
26788 * The keystroke filter mask to be applied on alpha input
26791 'alphaMask' : /[a-z_]/i,
26794 * The function used to validate alphanumeric values
26795 * @param {String} value The value
26797 'alphanum' : function(v){
26798 return alphanum.test(v);
26801 * The error text to display when the alphanumeric validation function returns false
26804 'alphanumText' : 'This field should only contain letters, numbers and _',
26806 * The keystroke filter mask to be applied on alphanumeric input
26809 'alphanumMask' : /[a-z0-9_]/i
26811 }();//<script type="text/javascript">
26814 * @class Roo.form.FCKeditor
26815 * @extends Roo.form.TextArea
26816 * Wrapper around the FCKEditor http://www.fckeditor.net
26818 * Creates a new FCKeditor
26819 * @param {Object} config Configuration options
26821 Roo.form.FCKeditor = function(config){
26822 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26825 * @event editorinit
26826 * Fired when the editor is initialized - you can add extra handlers here..
26827 * @param {FCKeditor} this
26828 * @param {Object} the FCK object.
26835 Roo.form.FCKeditor.editors = { };
26836 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26838 //defaultAutoCreate : {
26839 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26843 * @cfg {Object} fck options - see fck manual for details.
26848 * @cfg {Object} fck toolbar set (Basic or Default)
26850 toolbarSet : 'Basic',
26852 * @cfg {Object} fck BasePath
26854 basePath : '/fckeditor/',
26862 onRender : function(ct, position)
26865 this.defaultAutoCreate = {
26867 style:"width:300px;height:60px;",
26868 autocomplete: "new-password"
26871 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26874 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26875 if(this.preventScrollbars){
26876 this.el.setStyle("overflow", "hidden");
26878 this.el.setHeight(this.growMin);
26881 //console.log('onrender' + this.getId() );
26882 Roo.form.FCKeditor.editors[this.getId()] = this;
26885 this.replaceTextarea() ;
26889 getEditor : function() {
26890 return this.fckEditor;
26893 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26894 * @param {Mixed} value The value to set
26898 setValue : function(value)
26900 //console.log('setValue: ' + value);
26902 if(typeof(value) == 'undefined') { // not sure why this is happending...
26905 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26907 //if(!this.el || !this.getEditor()) {
26908 // this.value = value;
26909 //this.setValue.defer(100,this,[value]);
26913 if(!this.getEditor()) {
26917 this.getEditor().SetData(value);
26924 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26925 * @return {Mixed} value The field value
26927 getValue : function()
26930 if (this.frame && this.frame.dom.style.display == 'none') {
26931 return Roo.form.FCKeditor.superclass.getValue.call(this);
26934 if(!this.el || !this.getEditor()) {
26936 // this.getValue.defer(100,this);
26941 var value=this.getEditor().GetData();
26942 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26943 return Roo.form.FCKeditor.superclass.getValue.call(this);
26949 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26950 * @return {Mixed} value The field value
26952 getRawValue : function()
26954 if (this.frame && this.frame.dom.style.display == 'none') {
26955 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26958 if(!this.el || !this.getEditor()) {
26959 //this.getRawValue.defer(100,this);
26966 var value=this.getEditor().GetData();
26967 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26968 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26972 setSize : function(w,h) {
26976 //if (this.frame && this.frame.dom.style.display == 'none') {
26977 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26980 //if(!this.el || !this.getEditor()) {
26981 // this.setSize.defer(100,this, [w,h]);
26987 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26989 this.frame.dom.setAttribute('width', w);
26990 this.frame.dom.setAttribute('height', h);
26991 this.frame.setSize(w,h);
26995 toggleSourceEdit : function(value) {
26999 this.el.dom.style.display = value ? '' : 'none';
27000 this.frame.dom.style.display = value ? 'none' : '';
27005 focus: function(tag)
27007 if (this.frame.dom.style.display == 'none') {
27008 return Roo.form.FCKeditor.superclass.focus.call(this);
27010 if(!this.el || !this.getEditor()) {
27011 this.focus.defer(100,this, [tag]);
27018 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27019 this.getEditor().Focus();
27021 if (!this.getEditor().Selection.GetSelection()) {
27022 this.focus.defer(100,this, [tag]);
27027 var r = this.getEditor().EditorDocument.createRange();
27028 r.setStart(tgs[0],0);
27029 r.setEnd(tgs[0],0);
27030 this.getEditor().Selection.GetSelection().removeAllRanges();
27031 this.getEditor().Selection.GetSelection().addRange(r);
27032 this.getEditor().Focus();
27039 replaceTextarea : function()
27041 if ( document.getElementById( this.getId() + '___Frame' ) ) {
27044 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27046 // We must check the elements firstly using the Id and then the name.
27047 var oTextarea = document.getElementById( this.getId() );
27049 var colElementsByName = document.getElementsByName( this.getId() ) ;
27051 oTextarea.style.display = 'none' ;
27053 if ( oTextarea.tabIndex ) {
27054 this.TabIndex = oTextarea.tabIndex ;
27057 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27058 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27059 this.frame = Roo.get(this.getId() + '___Frame')
27062 _getConfigHtml : function()
27066 for ( var o in this.fckconfig ) {
27067 sConfig += sConfig.length > 0 ? '&' : '';
27068 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27071 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27075 _getIFrameHtml : function()
27077 var sFile = 'fckeditor.html' ;
27078 /* no idea what this is about..
27081 if ( (/fcksource=true/i).test( window.top.location.search ) )
27082 sFile = 'fckeditor.original.html' ;
27087 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27088 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
27091 var html = '<iframe id="' + this.getId() +
27092 '___Frame" src="' + sLink +
27093 '" width="' + this.width +
27094 '" height="' + this.height + '"' +
27095 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
27096 ' frameborder="0" scrolling="no"></iframe>' ;
27101 _insertHtmlBefore : function( html, element )
27103 if ( element.insertAdjacentHTML ) {
27105 element.insertAdjacentHTML( 'beforeBegin', html ) ;
27107 var oRange = document.createRange() ;
27108 oRange.setStartBefore( element ) ;
27109 var oFragment = oRange.createContextualFragment( html );
27110 element.parentNode.insertBefore( oFragment, element ) ;
27123 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27125 function FCKeditor_OnComplete(editorInstance){
27126 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27127 f.fckEditor = editorInstance;
27128 //console.log("loaded");
27129 f.fireEvent('editorinit', f, editorInstance);
27149 //<script type="text/javascript">
27151 * @class Roo.form.GridField
27152 * @extends Roo.form.Field
27153 * Embed a grid (or editable grid into a form)
27156 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27158 * xgrid.store = Roo.data.Store
27159 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27160 * xgrid.store.reader = Roo.data.JsonReader
27164 * Creates a new GridField
27165 * @param {Object} config Configuration options
27167 Roo.form.GridField = function(config){
27168 Roo.form.GridField.superclass.constructor.call(this, config);
27172 Roo.extend(Roo.form.GridField, Roo.form.Field, {
27174 * @cfg {Number} width - used to restrict width of grid..
27178 * @cfg {Number} height - used to restrict height of grid..
27182 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27188 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27189 * {tag: "input", type: "checkbox", autocomplete: "off"})
27191 // defaultAutoCreate : { tag: 'div' },
27192 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27194 * @cfg {String} addTitle Text to include for adding a title.
27198 onResize : function(){
27199 Roo.form.Field.superclass.onResize.apply(this, arguments);
27202 initEvents : function(){
27203 // Roo.form.Checkbox.superclass.initEvents.call(this);
27204 // has no events...
27209 getResizeEl : function(){
27213 getPositionEl : function(){
27218 onRender : function(ct, position){
27220 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27221 var style = this.style;
27224 Roo.form.GridField.superclass.onRender.call(this, ct, position);
27225 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27226 this.viewEl = this.wrap.createChild({ tag: 'div' });
27228 this.viewEl.applyStyles(style);
27231 this.viewEl.setWidth(this.width);
27234 this.viewEl.setHeight(this.height);
27236 //if(this.inputValue !== undefined){
27237 //this.setValue(this.value);
27240 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27243 this.grid.render();
27244 this.grid.getDataSource().on('remove', this.refreshValue, this);
27245 this.grid.getDataSource().on('update', this.refreshValue, this);
27246 this.grid.on('afteredit', this.refreshValue, this);
27252 * Sets the value of the item.
27253 * @param {String} either an object or a string..
27255 setValue : function(v){
27257 v = v || []; // empty set..
27258 // this does not seem smart - it really only affects memoryproxy grids..
27259 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27260 var ds = this.grid.getDataSource();
27261 // assumes a json reader..
27263 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27264 ds.loadData( data);
27266 // clear selection so it does not get stale.
27267 if (this.grid.sm) {
27268 this.grid.sm.clearSelections();
27271 Roo.form.GridField.superclass.setValue.call(this, v);
27272 this.refreshValue();
27273 // should load data in the grid really....
27277 refreshValue: function() {
27279 this.grid.getDataSource().each(function(r) {
27282 this.el.dom.value = Roo.encode(val);
27290 * Ext JS Library 1.1.1
27291 * Copyright(c) 2006-2007, Ext JS, LLC.
27293 * Originally Released Under LGPL - original licence link has changed is not relivant.
27296 * <script type="text/javascript">
27299 * @class Roo.form.DisplayField
27300 * @extends Roo.form.Field
27301 * A generic Field to display non-editable data.
27302 * @cfg {Boolean} closable (true|false) default false
27304 * Creates a new Display Field item.
27305 * @param {Object} config Configuration options
27307 Roo.form.DisplayField = function(config){
27308 Roo.form.DisplayField.superclass.constructor.call(this, config);
27313 * Fires after the click the close btn
27314 * @param {Roo.form.DisplayField} this
27320 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27321 inputType: 'hidden',
27327 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27329 focusClass : undefined,
27331 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27333 fieldClass: 'x-form-field',
27336 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27338 valueRenderer: undefined,
27342 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27343 * {tag: "input", type: "checkbox", autocomplete: "off"})
27346 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27350 onResize : function(){
27351 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27355 initEvents : function(){
27356 // Roo.form.Checkbox.superclass.initEvents.call(this);
27357 // has no events...
27360 this.closeEl.on('click', this.onClose, this);
27366 getResizeEl : function(){
27370 getPositionEl : function(){
27375 onRender : function(ct, position){
27377 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27378 //if(this.inputValue !== undefined){
27379 this.wrap = this.el.wrap();
27381 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27384 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27387 if (this.bodyStyle) {
27388 this.viewEl.applyStyles(this.bodyStyle);
27390 //this.viewEl.setStyle('padding', '2px');
27392 this.setValue(this.value);
27397 initValue : Roo.emptyFn,
27402 onClick : function(){
27407 * Sets the checked state of the checkbox.
27408 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27410 setValue : function(v){
27412 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27413 // this might be called before we have a dom element..
27414 if (!this.viewEl) {
27417 this.viewEl.dom.innerHTML = html;
27418 Roo.form.DisplayField.superclass.setValue.call(this, v);
27422 onClose : function(e)
27424 e.preventDefault();
27426 this.fireEvent('close', this);
27435 * @class Roo.form.DayPicker
27436 * @extends Roo.form.Field
27437 * A Day picker show [M] [T] [W] ....
27439 * Creates a new Day Picker
27440 * @param {Object} config Configuration options
27442 Roo.form.DayPicker= function(config){
27443 Roo.form.DayPicker.superclass.constructor.call(this, config);
27447 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27449 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27451 focusClass : undefined,
27453 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27455 fieldClass: "x-form-field",
27458 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27459 * {tag: "input", type: "checkbox", autocomplete: "off"})
27461 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27464 actionMode : 'viewEl',
27468 inputType : 'hidden',
27471 inputElement: false, // real input element?
27472 basedOn: false, // ????
27474 isFormField: true, // not sure where this is needed!!!!
27476 onResize : function(){
27477 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27478 if(!this.boxLabel){
27479 this.el.alignTo(this.wrap, 'c-c');
27483 initEvents : function(){
27484 Roo.form.Checkbox.superclass.initEvents.call(this);
27485 this.el.on("click", this.onClick, this);
27486 this.el.on("change", this.onClick, this);
27490 getResizeEl : function(){
27494 getPositionEl : function(){
27500 onRender : function(ct, position){
27501 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27503 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27505 var r1 = '<table><tr>';
27506 var r2 = '<tr class="x-form-daypick-icons">';
27507 for (var i=0; i < 7; i++) {
27508 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27509 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27512 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27513 viewEl.select('img').on('click', this.onClick, this);
27514 this.viewEl = viewEl;
27517 // this will not work on Chrome!!!
27518 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27519 this.el.on('propertychange', this.setFromHidden, this); //ie
27527 initValue : Roo.emptyFn,
27530 * Returns the checked state of the checkbox.
27531 * @return {Boolean} True if checked, else false
27533 getValue : function(){
27534 return this.el.dom.value;
27539 onClick : function(e){
27540 //this.setChecked(!this.checked);
27541 Roo.get(e.target).toggleClass('x-menu-item-checked');
27542 this.refreshValue();
27543 //if(this.el.dom.checked != this.checked){
27544 // this.setValue(this.el.dom.checked);
27549 refreshValue : function()
27552 this.viewEl.select('img',true).each(function(e,i,n) {
27553 val += e.is(".x-menu-item-checked") ? String(n) : '';
27555 this.setValue(val, true);
27559 * Sets the checked state of the checkbox.
27560 * On is always based on a string comparison between inputValue and the param.
27561 * @param {Boolean/String} value - the value to set
27562 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27564 setValue : function(v,suppressEvent){
27565 if (!this.el.dom) {
27568 var old = this.el.dom.value ;
27569 this.el.dom.value = v;
27570 if (suppressEvent) {
27574 // update display..
27575 this.viewEl.select('img',true).each(function(e,i,n) {
27577 var on = e.is(".x-menu-item-checked");
27578 var newv = v.indexOf(String(n)) > -1;
27580 e.toggleClass('x-menu-item-checked');
27586 this.fireEvent('change', this, v, old);
27591 // handle setting of hidden value by some other method!!?!?
27592 setFromHidden: function()
27597 //console.log("SET FROM HIDDEN");
27598 //alert('setFrom hidden');
27599 this.setValue(this.el.dom.value);
27602 onDestroy : function()
27605 Roo.get(this.viewEl).remove();
27608 Roo.form.DayPicker.superclass.onDestroy.call(this);
27612 * RooJS Library 1.1.1
27613 * Copyright(c) 2008-2011 Alan Knowles
27620 * @class Roo.form.ComboCheck
27621 * @extends Roo.form.ComboBox
27622 * A combobox for multiple select items.
27624 * FIXME - could do with a reset button..
27627 * Create a new ComboCheck
27628 * @param {Object} config Configuration options
27630 Roo.form.ComboCheck = function(config){
27631 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27632 // should verify some data...
27634 // hiddenName = required..
27635 // displayField = required
27636 // valudField == required
27637 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27639 Roo.each(req, function(e) {
27640 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27641 throw "Roo.form.ComboCheck : missing value for: " + e;
27648 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27653 selectedClass: 'x-menu-item-checked',
27656 onRender : function(ct, position){
27662 var cls = 'x-combo-list';
27665 this.tpl = new Roo.Template({
27666 html : '<div class="'+cls+'-item x-menu-check-item">' +
27667 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27668 '<span>{' + this.displayField + '}</span>' +
27675 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27676 this.view.singleSelect = false;
27677 this.view.multiSelect = true;
27678 this.view.toggleSelect = true;
27679 this.pageTb.add(new Roo.Toolbar.Fill(), {
27682 handler: function()
27689 onViewOver : function(e, t){
27695 onViewClick : function(doFocus,index){
27699 select: function () {
27700 //Roo.log("SELECT CALLED");
27703 selectByValue : function(xv, scrollIntoView){
27704 var ar = this.getValueArray();
27707 Roo.each(ar, function(v) {
27708 if(v === undefined || v === null){
27711 var r = this.findRecord(this.valueField, v);
27713 sels.push(this.store.indexOf(r))
27717 this.view.select(sels);
27723 onSelect : function(record, index){
27724 // Roo.log("onselect Called");
27725 // this is only called by the clear button now..
27726 this.view.clearSelections();
27727 this.setValue('[]');
27728 if (this.value != this.valueBefore) {
27729 this.fireEvent('change', this, this.value, this.valueBefore);
27730 this.valueBefore = this.value;
27733 getValueArray : function()
27738 //Roo.log(this.value);
27739 if (typeof(this.value) == 'undefined') {
27742 var ar = Roo.decode(this.value);
27743 return ar instanceof Array ? ar : []; //?? valid?
27746 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27751 expand : function ()
27754 Roo.form.ComboCheck.superclass.expand.call(this);
27755 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27756 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27761 collapse : function(){
27762 Roo.form.ComboCheck.superclass.collapse.call(this);
27763 var sl = this.view.getSelectedIndexes();
27764 var st = this.store;
27768 Roo.each(sl, function(i) {
27770 nv.push(r.get(this.valueField));
27772 this.setValue(Roo.encode(nv));
27773 if (this.value != this.valueBefore) {
27775 this.fireEvent('change', this, this.value, this.valueBefore);
27776 this.valueBefore = this.value;
27781 setValue : function(v){
27785 var vals = this.getValueArray();
27787 Roo.each(vals, function(k) {
27788 var r = this.findRecord(this.valueField, k);
27790 tv.push(r.data[this.displayField]);
27791 }else if(this.valueNotFoundText !== undefined){
27792 tv.push( this.valueNotFoundText );
27797 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27798 this.hiddenField.value = v;
27804 * Ext JS Library 1.1.1
27805 * Copyright(c) 2006-2007, Ext JS, LLC.
27807 * Originally Released Under LGPL - original licence link has changed is not relivant.
27810 * <script type="text/javascript">
27814 * @class Roo.form.Signature
27815 * @extends Roo.form.Field
27819 * @param {Object} config Configuration options
27822 Roo.form.Signature = function(config){
27823 Roo.form.Signature.superclass.constructor.call(this, config);
27825 this.addEvents({// not in used??
27828 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27829 * @param {Roo.form.Signature} combo This combo box
27834 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27835 * @param {Roo.form.ComboBox} combo This combo box
27836 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27842 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27844 * @cfg {Object} labels Label to use when rendering a form.
27848 * confirm : "Confirm"
27853 confirm : "Confirm"
27856 * @cfg {Number} width The signature panel width (defaults to 300)
27860 * @cfg {Number} height The signature panel height (defaults to 100)
27864 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27866 allowBlank : false,
27869 // {Object} signPanel The signature SVG panel element (defaults to {})
27871 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27872 isMouseDown : false,
27873 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27874 isConfirmed : false,
27875 // {String} signatureTmp SVG mapping string (defaults to empty string)
27879 defaultAutoCreate : { // modified by initCompnoent..
27885 onRender : function(ct, position){
27887 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27889 this.wrap = this.el.wrap({
27890 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27893 this.createToolbar(this);
27894 this.signPanel = this.wrap.createChild({
27896 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27900 this.svgID = Roo.id();
27901 this.svgEl = this.signPanel.createChild({
27902 xmlns : 'http://www.w3.org/2000/svg',
27904 id : this.svgID + "-svg",
27906 height: this.height,
27907 viewBox: '0 0 '+this.width+' '+this.height,
27911 id: this.svgID + "-svg-r",
27913 height: this.height,
27918 id: this.svgID + "-svg-l",
27920 y1: (this.height*0.8), // start set the line in 80% of height
27921 x2: this.width, // end
27922 y2: (this.height*0.8), // end set the line in 80% of height
27924 'stroke-width': "1",
27925 'stroke-dasharray': "3",
27926 'shape-rendering': "crispEdges",
27927 'pointer-events': "none"
27931 id: this.svgID + "-svg-p",
27933 'stroke-width': "3",
27935 'pointer-events': 'none'
27940 this.svgBox = this.svgEl.dom.getScreenCTM();
27942 createSVG : function(){
27943 var svg = this.signPanel;
27944 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27947 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27948 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27949 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27950 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27951 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27952 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27953 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27956 isTouchEvent : function(e){
27957 return e.type.match(/^touch/);
27959 getCoords : function (e) {
27960 var pt = this.svgEl.dom.createSVGPoint();
27963 if (this.isTouchEvent(e)) {
27964 pt.x = e.targetTouches[0].clientX;
27965 pt.y = e.targetTouches[0].clientY;
27967 var a = this.svgEl.dom.getScreenCTM();
27968 var b = a.inverse();
27969 var mx = pt.matrixTransform(b);
27970 return mx.x + ',' + mx.y;
27972 //mouse event headler
27973 down : function (e) {
27974 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27975 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27977 this.isMouseDown = true;
27979 e.preventDefault();
27981 move : function (e) {
27982 if (this.isMouseDown) {
27983 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27984 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27987 e.preventDefault();
27989 up : function (e) {
27990 this.isMouseDown = false;
27991 var sp = this.signatureTmp.split(' ');
27994 if(!sp[sp.length-2].match(/^L/)){
27998 this.signatureTmp = sp.join(" ");
28001 if(this.getValue() != this.signatureTmp){
28002 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28003 this.isConfirmed = false;
28005 e.preventDefault();
28009 * Protected method that will not generally be called directly. It
28010 * is called when the editor creates its toolbar. Override this method if you need to
28011 * add custom toolbar buttons.
28012 * @param {HtmlEditor} editor
28014 createToolbar : function(editor){
28015 function btn(id, toggle, handler){
28016 var xid = fid + '-'+ id ;
28020 cls : 'x-btn-icon x-edit-'+id,
28021 enableToggle:toggle !== false,
28022 scope: editor, // was editor...
28023 handler:handler||editor.relayBtnCmd,
28024 clickEvent:'mousedown',
28025 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28031 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28035 cls : ' x-signature-btn x-signature-'+id,
28036 scope: editor, // was editor...
28037 handler: this.reset,
28038 clickEvent:'mousedown',
28039 text: this.labels.clear
28046 cls : ' x-signature-btn x-signature-'+id,
28047 scope: editor, // was editor...
28048 handler: this.confirmHandler,
28049 clickEvent:'mousedown',
28050 text: this.labels.confirm
28057 * when user is clicked confirm then show this image.....
28059 * @return {String} Image Data URI
28061 getImageDataURI : function(){
28062 var svg = this.svgEl.dom.parentNode.innerHTML;
28063 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28068 * @return {Boolean} this.isConfirmed
28070 getConfirmed : function(){
28071 return this.isConfirmed;
28075 * @return {Number} this.width
28077 getWidth : function(){
28082 * @return {Number} this.height
28084 getHeight : function(){
28085 return this.height;
28088 getSignature : function(){
28089 return this.signatureTmp;
28092 reset : function(){
28093 this.signatureTmp = '';
28094 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28095 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28096 this.isConfirmed = false;
28097 Roo.form.Signature.superclass.reset.call(this);
28099 setSignature : function(s){
28100 this.signatureTmp = s;
28101 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28102 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28104 this.isConfirmed = false;
28105 Roo.form.Signature.superclass.reset.call(this);
28108 // Roo.log(this.signPanel.dom.contentWindow.up())
28111 setConfirmed : function(){
28115 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28118 confirmHandler : function(){
28119 if(!this.getSignature()){
28123 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28124 this.setValue(this.getSignature());
28125 this.isConfirmed = true;
28127 this.fireEvent('confirm', this);
28130 // Subclasses should provide the validation implementation by overriding this
28131 validateValue : function(value){
28132 if(this.allowBlank){
28136 if(this.isConfirmed){
28143 * Ext JS Library 1.1.1
28144 * Copyright(c) 2006-2007, Ext JS, LLC.
28146 * Originally Released Under LGPL - original licence link has changed is not relivant.
28149 * <script type="text/javascript">
28154 * @class Roo.form.ComboBox
28155 * @extends Roo.form.TriggerField
28156 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28158 * Create a new ComboBox.
28159 * @param {Object} config Configuration options
28161 Roo.form.Select = function(config){
28162 Roo.form.Select.superclass.constructor.call(this, config);
28166 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28168 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28171 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28172 * rendering into an Roo.Editor, defaults to false)
28175 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28176 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28179 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28182 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28183 * the dropdown list (defaults to undefined, with no header element)
28187 * @cfg {String/Roo.Template} tpl The template to use to render the output
28191 defaultAutoCreate : {tag: "select" },
28193 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28195 listWidth: undefined,
28197 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28198 * mode = 'remote' or 'text' if mode = 'local')
28200 displayField: undefined,
28202 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28203 * mode = 'remote' or 'value' if mode = 'local').
28204 * Note: use of a valueField requires the user make a selection
28205 * in order for a value to be mapped.
28207 valueField: undefined,
28211 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28212 * field's data value (defaults to the underlying DOM element's name)
28214 hiddenName: undefined,
28216 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28220 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28222 selectedClass: 'x-combo-selected',
28224 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
28225 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28226 * which displays a downward arrow icon).
28228 triggerClass : 'x-form-arrow-trigger',
28230 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28234 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28235 * anchor positions (defaults to 'tl-bl')
28237 listAlign: 'tl-bl?',
28239 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28243 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
28244 * query specified by the allQuery config option (defaults to 'query')
28246 triggerAction: 'query',
28248 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28249 * (defaults to 4, does not apply if editable = false)
28253 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28254 * delay (typeAheadDelay) if it matches a known value (defaults to false)
28258 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28259 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28263 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28264 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28268 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28269 * when editable = true (defaults to false)
28271 selectOnFocus:false,
28273 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28275 queryParam: 'query',
28277 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28278 * when mode = 'remote' (defaults to 'Loading...')
28280 loadingText: 'Loading...',
28282 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28286 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28290 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28291 * traditional select (defaults to true)
28295 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28299 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28303 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28304 * listWidth has a higher value)
28308 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28309 * allow the user to set arbitrary text into the field (defaults to false)
28311 forceSelection:false,
28313 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28314 * if typeAhead = true (defaults to 250)
28316 typeAheadDelay : 250,
28318 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28319 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28321 valueNotFoundText : undefined,
28324 * @cfg {String} defaultValue The value displayed after loading the store.
28329 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28331 blockFocus : false,
28334 * @cfg {Boolean} disableClear Disable showing of clear button.
28336 disableClear : false,
28338 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28340 alwaysQuery : false,
28346 // element that contains real text value.. (when hidden is used..)
28349 onRender : function(ct, position){
28350 Roo.form.Field.prototype.onRender.call(this, ct, position);
28353 this.store.on('beforeload', this.onBeforeLoad, this);
28354 this.store.on('load', this.onLoad, this);
28355 this.store.on('loadexception', this.onLoadException, this);
28356 this.store.load({});
28364 initEvents : function(){
28365 //Roo.form.ComboBox.superclass.initEvents.call(this);
28369 onDestroy : function(){
28372 this.store.un('beforeload', this.onBeforeLoad, this);
28373 this.store.un('load', this.onLoad, this);
28374 this.store.un('loadexception', this.onLoadException, this);
28376 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28380 fireKey : function(e){
28381 if(e.isNavKeyPress() && !this.list.isVisible()){
28382 this.fireEvent("specialkey", this, e);
28387 onResize: function(w, h){
28395 * Allow or prevent the user from directly editing the field text. If false is passed,
28396 * the user will only be able to select from the items defined in the dropdown list. This method
28397 * is the runtime equivalent of setting the 'editable' config option at config time.
28398 * @param {Boolean} value True to allow the user to directly edit the field text
28400 setEditable : function(value){
28405 onBeforeLoad : function(){
28407 Roo.log("Select before load");
28410 this.innerList.update(this.loadingText ?
28411 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28412 //this.restrictHeight();
28413 this.selectedIndex = -1;
28417 onLoad : function(){
28420 var dom = this.el.dom;
28421 dom.innerHTML = '';
28422 var od = dom.ownerDocument;
28424 if (this.emptyText) {
28425 var op = od.createElement('option');
28426 op.setAttribute('value', '');
28427 op.innerHTML = String.format('{0}', this.emptyText);
28428 dom.appendChild(op);
28430 if(this.store.getCount() > 0){
28432 var vf = this.valueField;
28433 var df = this.displayField;
28434 this.store.data.each(function(r) {
28435 // which colmsn to use... testing - cdoe / title..
28436 var op = od.createElement('option');
28437 op.setAttribute('value', r.data[vf]);
28438 op.innerHTML = String.format('{0}', r.data[df]);
28439 dom.appendChild(op);
28441 if (typeof(this.defaultValue != 'undefined')) {
28442 this.setValue(this.defaultValue);
28447 //this.onEmptyResults();
28452 onLoadException : function()
28454 dom.innerHTML = '';
28456 Roo.log("Select on load exception");
28460 Roo.log(this.store.reader.jsonData);
28461 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28462 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28468 onTypeAhead : function(){
28473 onSelect : function(record, index){
28474 Roo.log('on select?');
28476 if(this.fireEvent('beforeselect', this, record, index) !== false){
28477 this.setFromData(index > -1 ? record.data : false);
28479 this.fireEvent('select', this, record, index);
28484 * Returns the currently selected field value or empty string if no value is set.
28485 * @return {String} value The selected value
28487 getValue : function(){
28488 var dom = this.el.dom;
28489 this.value = dom.options[dom.selectedIndex].value;
28495 * Clears any text/value currently set in the field
28497 clearValue : function(){
28499 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28504 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28505 * will be displayed in the field. If the value does not match the data value of an existing item,
28506 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28507 * Otherwise the field will be blank (although the value will still be set).
28508 * @param {String} value The value to match
28510 setValue : function(v){
28511 var d = this.el.dom;
28512 for (var i =0; i < d.options.length;i++) {
28513 if (v == d.options[i].value) {
28514 d.selectedIndex = i;
28522 * @property {Object} the last set data for the element
28527 * Sets the value of the field based on a object which is related to the record format for the store.
28528 * @param {Object} value the value to set as. or false on reset?
28530 setFromData : function(o){
28531 Roo.log('setfrom data?');
28537 reset : function(){
28541 findRecord : function(prop, value){
28546 if(this.store.getCount() > 0){
28547 this.store.each(function(r){
28548 if(r.data[prop] == value){
28558 getName: function()
28560 // returns hidden if it's set..
28561 if (!this.rendered) {return ''};
28562 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28570 onEmptyResults : function(){
28571 Roo.log('empty results');
28576 * Returns true if the dropdown list is expanded, else false.
28578 isExpanded : function(){
28583 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28584 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28585 * @param {String} value The data value of the item to select
28586 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28587 * selected item if it is not currently in view (defaults to true)
28588 * @return {Boolean} True if the value matched an item in the list, else false
28590 selectByValue : function(v, scrollIntoView){
28591 Roo.log('select By Value');
28594 if(v !== undefined && v !== null){
28595 var r = this.findRecord(this.valueField || this.displayField, v);
28597 this.select(this.store.indexOf(r), scrollIntoView);
28605 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28606 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28607 * @param {Number} index The zero-based index of the list item to select
28608 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28609 * selected item if it is not currently in view (defaults to true)
28611 select : function(index, scrollIntoView){
28612 Roo.log('select ');
28615 this.selectedIndex = index;
28616 this.view.select(index);
28617 if(scrollIntoView !== false){
28618 var el = this.view.getNode(index);
28620 this.innerList.scrollChildIntoView(el, false);
28628 validateBlur : function(){
28635 initQuery : function(){
28636 this.doQuery(this.getRawValue());
28640 doForce : function(){
28641 if(this.el.dom.value.length > 0){
28642 this.el.dom.value =
28643 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28649 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28650 * query allowing the query action to be canceled if needed.
28651 * @param {String} query The SQL query to execute
28652 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28653 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28654 * saved in the current store (defaults to false)
28656 doQuery : function(q, forceAll){
28658 Roo.log('doQuery?');
28659 if(q === undefined || q === null){
28664 forceAll: forceAll,
28668 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28672 forceAll = qe.forceAll;
28673 if(forceAll === true || (q.length >= this.minChars)){
28674 if(this.lastQuery != q || this.alwaysQuery){
28675 this.lastQuery = q;
28676 if(this.mode == 'local'){
28677 this.selectedIndex = -1;
28679 this.store.clearFilter();
28681 this.store.filter(this.displayField, q);
28685 this.store.baseParams[this.queryParam] = q;
28687 params: this.getParams(q)
28692 this.selectedIndex = -1;
28699 getParams : function(q){
28701 //p[this.queryParam] = q;
28704 p.limit = this.pageSize;
28710 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28712 collapse : function(){
28717 collapseIf : function(e){
28722 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28724 expand : function(){
28732 * @cfg {Boolean} grow
28736 * @cfg {Number} growMin
28740 * @cfg {Number} growMax
28748 setWidth : function()
28752 getResizeEl : function(){
28755 });//<script type="text/javasscript">
28759 * @class Roo.DDView
28760 * A DnD enabled version of Roo.View.
28761 * @param {Element/String} container The Element in which to create the View.
28762 * @param {String} tpl The template string used to create the markup for each element of the View
28763 * @param {Object} config The configuration properties. These include all the config options of
28764 * {@link Roo.View} plus some specific to this class.<br>
28766 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28767 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28769 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28770 .x-view-drag-insert-above {
28771 border-top:1px dotted #3366cc;
28773 .x-view-drag-insert-below {
28774 border-bottom:1px dotted #3366cc;
28780 Roo.DDView = function(container, tpl, config) {
28781 Roo.DDView.superclass.constructor.apply(this, arguments);
28782 this.getEl().setStyle("outline", "0px none");
28783 this.getEl().unselectable();
28784 if (this.dragGroup) {
28785 this.setDraggable(this.dragGroup.split(","));
28787 if (this.dropGroup) {
28788 this.setDroppable(this.dropGroup.split(","));
28790 if (this.deletable) {
28791 this.setDeletable();
28793 this.isDirtyFlag = false;
28799 Roo.extend(Roo.DDView, Roo.View, {
28800 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28801 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28802 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28803 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28807 reset: Roo.emptyFn,
28809 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28811 validate: function() {
28815 destroy: function() {
28816 this.purgeListeners();
28817 this.getEl.removeAllListeners();
28818 this.getEl().remove();
28819 if (this.dragZone) {
28820 if (this.dragZone.destroy) {
28821 this.dragZone.destroy();
28824 if (this.dropZone) {
28825 if (this.dropZone.destroy) {
28826 this.dropZone.destroy();
28831 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28832 getName: function() {
28836 /** Loads the View from a JSON string representing the Records to put into the Store. */
28837 setValue: function(v) {
28839 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28842 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28843 this.store.proxy = new Roo.data.MemoryProxy(data);
28847 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28848 getValue: function() {
28850 this.store.each(function(rec) {
28851 result += rec.id + ',';
28853 return result.substr(0, result.length - 1) + ')';
28856 getIds: function() {
28857 var i = 0, result = new Array(this.store.getCount());
28858 this.store.each(function(rec) {
28859 result[i++] = rec.id;
28864 isDirty: function() {
28865 return this.isDirtyFlag;
28869 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28870 * whole Element becomes the target, and this causes the drop gesture to append.
28872 getTargetFromEvent : function(e) {
28873 var target = e.getTarget();
28874 while ((target !== null) && (target.parentNode != this.el.dom)) {
28875 target = target.parentNode;
28878 target = this.el.dom.lastChild || this.el.dom;
28884 * Create the drag data which consists of an object which has the property "ddel" as
28885 * the drag proxy element.
28887 getDragData : function(e) {
28888 var target = this.findItemFromChild(e.getTarget());
28890 this.handleSelection(e);
28891 var selNodes = this.getSelectedNodes();
28894 copy: this.copy || (this.allowCopy && e.ctrlKey),
28898 var selectedIndices = this.getSelectedIndexes();
28899 for (var i = 0; i < selectedIndices.length; i++) {
28900 dragData.records.push(this.store.getAt(selectedIndices[i]));
28902 if (selNodes.length == 1) {
28903 dragData.ddel = target.cloneNode(true); // the div element
28905 var div = document.createElement('div'); // create the multi element drag "ghost"
28906 div.className = 'multi-proxy';
28907 for (var i = 0, len = selNodes.length; i < len; i++) {
28908 div.appendChild(selNodes[i].cloneNode(true));
28910 dragData.ddel = div;
28912 //console.log(dragData)
28913 //console.log(dragData.ddel.innerHTML)
28916 //console.log('nodragData')
28920 /** Specify to which ddGroup items in this DDView may be dragged. */
28921 setDraggable: function(ddGroup) {
28922 if (ddGroup instanceof Array) {
28923 Roo.each(ddGroup, this.setDraggable, this);
28926 if (this.dragZone) {
28927 this.dragZone.addToGroup(ddGroup);
28929 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28930 containerScroll: true,
28934 // Draggability implies selection. DragZone's mousedown selects the element.
28935 if (!this.multiSelect) { this.singleSelect = true; }
28937 // Wire the DragZone's handlers up to methods in *this*
28938 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28942 /** Specify from which ddGroup this DDView accepts drops. */
28943 setDroppable: function(ddGroup) {
28944 if (ddGroup instanceof Array) {
28945 Roo.each(ddGroup, this.setDroppable, this);
28948 if (this.dropZone) {
28949 this.dropZone.addToGroup(ddGroup);
28951 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28952 containerScroll: true,
28956 // Wire the DropZone's handlers up to methods in *this*
28957 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28958 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28959 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28960 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28961 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28965 /** Decide whether to drop above or below a View node. */
28966 getDropPoint : function(e, n, dd){
28967 if (n == this.el.dom) { return "above"; }
28968 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28969 var c = t + (b - t) / 2;
28970 var y = Roo.lib.Event.getPageY(e);
28978 onNodeEnter : function(n, dd, e, data){
28982 onNodeOver : function(n, dd, e, data){
28983 var pt = this.getDropPoint(e, n, dd);
28984 // set the insert point style on the target node
28985 var dragElClass = this.dropNotAllowed;
28988 if (pt == "above"){
28989 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28990 targetElClass = "x-view-drag-insert-above";
28992 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28993 targetElClass = "x-view-drag-insert-below";
28995 if (this.lastInsertClass != targetElClass){
28996 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28997 this.lastInsertClass = targetElClass;
29000 return dragElClass;
29003 onNodeOut : function(n, dd, e, data){
29004 this.removeDropIndicators(n);
29007 onNodeDrop : function(n, dd, e, data){
29008 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29011 var pt = this.getDropPoint(e, n, dd);
29012 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29013 if (pt == "below") { insertAt++; }
29014 for (var i = 0; i < data.records.length; i++) {
29015 var r = data.records[i];
29016 var dup = this.store.getById(r.id);
29017 if (dup && (dd != this.dragZone)) {
29018 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29021 this.store.insert(insertAt++, r.copy());
29023 data.source.isDirtyFlag = true;
29025 this.store.insert(insertAt++, r);
29027 this.isDirtyFlag = true;
29030 this.dragZone.cachedTarget = null;
29034 removeDropIndicators : function(n){
29036 Roo.fly(n).removeClass([
29037 "x-view-drag-insert-above",
29038 "x-view-drag-insert-below"]);
29039 this.lastInsertClass = "_noclass";
29044 * Utility method. Add a delete option to the DDView's context menu.
29045 * @param {String} imageUrl The URL of the "delete" icon image.
29047 setDeletable: function(imageUrl) {
29048 if (!this.singleSelect && !this.multiSelect) {
29049 this.singleSelect = true;
29051 var c = this.getContextMenu();
29052 this.contextMenu.on("itemclick", function(item) {
29055 this.remove(this.getSelectedIndexes());
29059 this.contextMenu.add({
29066 /** Return the context menu for this DDView. */
29067 getContextMenu: function() {
29068 if (!this.contextMenu) {
29069 // Create the View's context menu
29070 this.contextMenu = new Roo.menu.Menu({
29071 id: this.id + "-contextmenu"
29073 this.el.on("contextmenu", this.showContextMenu, this);
29075 return this.contextMenu;
29078 disableContextMenu: function() {
29079 if (this.contextMenu) {
29080 this.el.un("contextmenu", this.showContextMenu, this);
29084 showContextMenu: function(e, item) {
29085 item = this.findItemFromChild(e.getTarget());
29088 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29089 this.contextMenu.showAt(e.getXY());
29094 * Remove {@link Roo.data.Record}s at the specified indices.
29095 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29097 remove: function(selectedIndices) {
29098 selectedIndices = [].concat(selectedIndices);
29099 for (var i = 0; i < selectedIndices.length; i++) {
29100 var rec = this.store.getAt(selectedIndices[i]);
29101 this.store.remove(rec);
29106 * Double click fires the event, but also, if this is draggable, and there is only one other
29107 * related DropZone, it transfers the selected node.
29109 onDblClick : function(e){
29110 var item = this.findItemFromChild(e.getTarget());
29112 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29115 if (this.dragGroup) {
29116 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29117 while (targets.indexOf(this.dropZone) > -1) {
29118 targets.remove(this.dropZone);
29120 if (targets.length == 1) {
29121 this.dragZone.cachedTarget = null;
29122 var el = Roo.get(targets[0].getEl());
29123 var box = el.getBox(true);
29124 targets[0].onNodeDrop(el.dom, {
29126 xy: [box.x, box.y + box.height - 1]
29127 }, null, this.getDragData(e));
29133 handleSelection: function(e) {
29134 this.dragZone.cachedTarget = null;
29135 var item = this.findItemFromChild(e.getTarget());
29137 this.clearSelections(true);
29140 if (item && (this.multiSelect || this.singleSelect)){
29141 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29142 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29143 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29144 this.unselect(item);
29146 this.select(item, this.multiSelect && e.ctrlKey);
29147 this.lastSelection = item;
29152 onItemClick : function(item, index, e){
29153 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29159 unselect : function(nodeInfo, suppressEvent){
29160 var node = this.getNode(nodeInfo);
29161 if(node && this.isSelected(node)){
29162 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29163 Roo.fly(node).removeClass(this.selectedClass);
29164 this.selections.remove(node);
29165 if(!suppressEvent){
29166 this.fireEvent("selectionchange", this, this.selections);
29174 * Ext JS Library 1.1.1
29175 * Copyright(c) 2006-2007, Ext JS, LLC.
29177 * Originally Released Under LGPL - original licence link has changed is not relivant.
29180 * <script type="text/javascript">
29184 * @class Roo.LayoutManager
29185 * @extends Roo.util.Observable
29186 * Base class for layout managers.
29188 Roo.LayoutManager = function(container, config){
29189 Roo.LayoutManager.superclass.constructor.call(this);
29190 this.el = Roo.get(container);
29191 // ie scrollbar fix
29192 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29193 document.body.scroll = "no";
29194 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29195 this.el.position('relative');
29197 this.id = this.el.id;
29198 this.el.addClass("x-layout-container");
29199 /** false to disable window resize monitoring @type Boolean */
29200 this.monitorWindowResize = true;
29205 * Fires when a layout is performed.
29206 * @param {Roo.LayoutManager} this
29210 * @event regionresized
29211 * Fires when the user resizes a region.
29212 * @param {Roo.LayoutRegion} region The resized region
29213 * @param {Number} newSize The new size (width for east/west, height for north/south)
29215 "regionresized" : true,
29217 * @event regioncollapsed
29218 * Fires when a region is collapsed.
29219 * @param {Roo.LayoutRegion} region The collapsed region
29221 "regioncollapsed" : true,
29223 * @event regionexpanded
29224 * Fires when a region is expanded.
29225 * @param {Roo.LayoutRegion} region The expanded region
29227 "regionexpanded" : true
29229 this.updating = false;
29230 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29233 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29235 * Returns true if this layout is currently being updated
29236 * @return {Boolean}
29238 isUpdating : function(){
29239 return this.updating;
29243 * Suspend the LayoutManager from doing auto-layouts while
29244 * making multiple add or remove calls
29246 beginUpdate : function(){
29247 this.updating = true;
29251 * Restore auto-layouts and optionally disable the manager from performing a layout
29252 * @param {Boolean} noLayout true to disable a layout update
29254 endUpdate : function(noLayout){
29255 this.updating = false;
29261 layout: function(){
29265 onRegionResized : function(region, newSize){
29266 this.fireEvent("regionresized", region, newSize);
29270 onRegionCollapsed : function(region){
29271 this.fireEvent("regioncollapsed", region);
29274 onRegionExpanded : function(region){
29275 this.fireEvent("regionexpanded", region);
29279 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29280 * performs box-model adjustments.
29281 * @return {Object} The size as an object {width: (the width), height: (the height)}
29283 getViewSize : function(){
29285 if(this.el.dom != document.body){
29286 size = this.el.getSize();
29288 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29290 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29291 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29296 * Returns the Element this layout is bound to.
29297 * @return {Roo.Element}
29299 getEl : function(){
29304 * Returns the specified region.
29305 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29306 * @return {Roo.LayoutRegion}
29308 getRegion : function(target){
29309 return this.regions[target.toLowerCase()];
29312 onWindowResize : function(){
29313 if(this.monitorWindowResize){
29319 * Ext JS Library 1.1.1
29320 * Copyright(c) 2006-2007, Ext JS, LLC.
29322 * Originally Released Under LGPL - original licence link has changed is not relivant.
29325 * <script type="text/javascript">
29328 * @class Roo.BorderLayout
29329 * @extends Roo.LayoutManager
29330 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29331 * please see: <br><br>
29332 * <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>
29333 * <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>
29336 var layout = new Roo.BorderLayout(document.body, {
29370 preferredTabWidth: 150
29375 var CP = Roo.ContentPanel;
29377 layout.beginUpdate();
29378 layout.add("north", new CP("north", "North"));
29379 layout.add("south", new CP("south", {title: "South", closable: true}));
29380 layout.add("west", new CP("west", {title: "West"}));
29381 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29382 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29383 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29384 layout.getRegion("center").showPanel("center1");
29385 layout.endUpdate();
29388 <b>The container the layout is rendered into can be either the body element or any other element.
29389 If it is not the body element, the container needs to either be an absolute positioned element,
29390 or you will need to add "position:relative" to the css of the container. You will also need to specify
29391 the container size if it is not the body element.</b>
29394 * Create a new BorderLayout
29395 * @param {String/HTMLElement/Element} container The container this layout is bound to
29396 * @param {Object} config Configuration options
29398 Roo.BorderLayout = function(container, config){
29399 config = config || {};
29400 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29401 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29402 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29403 var target = this.factory.validRegions[i];
29404 if(config[target]){
29405 this.addRegion(target, config[target]);
29410 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29412 * Creates and adds a new region if it doesn't already exist.
29413 * @param {String} target The target region key (north, south, east, west or center).
29414 * @param {Object} config The regions config object
29415 * @return {BorderLayoutRegion} The new region
29417 addRegion : function(target, config){
29418 if(!this.regions[target]){
29419 var r = this.factory.create(target, this, config);
29420 this.bindRegion(target, r);
29422 return this.regions[target];
29426 bindRegion : function(name, r){
29427 this.regions[name] = r;
29428 r.on("visibilitychange", this.layout, this);
29429 r.on("paneladded", this.layout, this);
29430 r.on("panelremoved", this.layout, this);
29431 r.on("invalidated", this.layout, this);
29432 r.on("resized", this.onRegionResized, this);
29433 r.on("collapsed", this.onRegionCollapsed, this);
29434 r.on("expanded", this.onRegionExpanded, this);
29438 * Performs a layout update.
29440 layout : function(){
29441 if(this.updating) {
29444 var size = this.getViewSize();
29445 var w = size.width;
29446 var h = size.height;
29451 //var x = 0, y = 0;
29453 var rs = this.regions;
29454 var north = rs["north"];
29455 var south = rs["south"];
29456 var west = rs["west"];
29457 var east = rs["east"];
29458 var center = rs["center"];
29459 //if(this.hideOnLayout){ // not supported anymore
29460 //c.el.setStyle("display", "none");
29462 if(north && north.isVisible()){
29463 var b = north.getBox();
29464 var m = north.getMargins();
29465 b.width = w - (m.left+m.right);
29468 centerY = b.height + b.y + m.bottom;
29469 centerH -= centerY;
29470 north.updateBox(this.safeBox(b));
29472 if(south && south.isVisible()){
29473 var b = south.getBox();
29474 var m = south.getMargins();
29475 b.width = w - (m.left+m.right);
29477 var totalHeight = (b.height + m.top + m.bottom);
29478 b.y = h - totalHeight + m.top;
29479 centerH -= totalHeight;
29480 south.updateBox(this.safeBox(b));
29482 if(west && west.isVisible()){
29483 var b = west.getBox();
29484 var m = west.getMargins();
29485 b.height = centerH - (m.top+m.bottom);
29487 b.y = centerY + m.top;
29488 var totalWidth = (b.width + m.left + m.right);
29489 centerX += totalWidth;
29490 centerW -= totalWidth;
29491 west.updateBox(this.safeBox(b));
29493 if(east && east.isVisible()){
29494 var b = east.getBox();
29495 var m = east.getMargins();
29496 b.height = centerH - (m.top+m.bottom);
29497 var totalWidth = (b.width + m.left + m.right);
29498 b.x = w - totalWidth + m.left;
29499 b.y = centerY + m.top;
29500 centerW -= totalWidth;
29501 east.updateBox(this.safeBox(b));
29504 var m = center.getMargins();
29506 x: centerX + m.left,
29507 y: centerY + m.top,
29508 width: centerW - (m.left+m.right),
29509 height: centerH - (m.top+m.bottom)
29511 //if(this.hideOnLayout){
29512 //center.el.setStyle("display", "block");
29514 center.updateBox(this.safeBox(centerBox));
29517 this.fireEvent("layout", this);
29521 safeBox : function(box){
29522 box.width = Math.max(0, box.width);
29523 box.height = Math.max(0, box.height);
29528 * Adds a ContentPanel (or subclass) to this layout.
29529 * @param {String} target The target region key (north, south, east, west or center).
29530 * @param {Roo.ContentPanel} panel The panel to add
29531 * @return {Roo.ContentPanel} The added panel
29533 add : function(target, panel){
29535 target = target.toLowerCase();
29536 return this.regions[target].add(panel);
29540 * Remove a ContentPanel (or subclass) to this layout.
29541 * @param {String} target The target region key (north, south, east, west or center).
29542 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29543 * @return {Roo.ContentPanel} The removed panel
29545 remove : function(target, panel){
29546 target = target.toLowerCase();
29547 return this.regions[target].remove(panel);
29551 * Searches all regions for a panel with the specified id
29552 * @param {String} panelId
29553 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29555 findPanel : function(panelId){
29556 var rs = this.regions;
29557 for(var target in rs){
29558 if(typeof rs[target] != "function"){
29559 var p = rs[target].getPanel(panelId);
29569 * Searches all regions for a panel with the specified id and activates (shows) it.
29570 * @param {String/ContentPanel} panelId The panels id or the panel itself
29571 * @return {Roo.ContentPanel} The shown panel or null
29573 showPanel : function(panelId) {
29574 var rs = this.regions;
29575 for(var target in rs){
29576 var r = rs[target];
29577 if(typeof r != "function"){
29578 if(r.hasPanel(panelId)){
29579 return r.showPanel(panelId);
29587 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29588 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29590 restoreState : function(provider){
29592 provider = Roo.state.Manager;
29594 var sm = new Roo.LayoutStateManager();
29595 sm.init(this, provider);
29599 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29600 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29601 * a valid ContentPanel config object. Example:
29603 // Create the main layout
29604 var layout = new Roo.BorderLayout('main-ct', {
29615 // Create and add multiple ContentPanels at once via configs
29618 id: 'source-files',
29620 title:'Ext Source Files',
29633 * @param {Object} regions An object containing ContentPanel configs by region name
29635 batchAdd : function(regions){
29636 this.beginUpdate();
29637 for(var rname in regions){
29638 var lr = this.regions[rname];
29640 this.addTypedPanels(lr, regions[rname]);
29647 addTypedPanels : function(lr, ps){
29648 if(typeof ps == 'string'){
29649 lr.add(new Roo.ContentPanel(ps));
29651 else if(ps instanceof Array){
29652 for(var i =0, len = ps.length; i < len; i++){
29653 this.addTypedPanels(lr, ps[i]);
29656 else if(!ps.events){ // raw config?
29658 delete ps.el; // prevent conflict
29659 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29661 else { // panel object assumed!
29666 * Adds a xtype elements to the layout.
29670 xtype : 'ContentPanel',
29677 xtype : 'NestedLayoutPanel',
29683 items : [ ... list of content panels or nested layout panels.. ]
29687 * @param {Object} cfg Xtype definition of item to add.
29689 addxtype : function(cfg)
29691 // basically accepts a pannel...
29692 // can accept a layout region..!?!?
29693 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29695 if (!cfg.xtype.match(/Panel$/)) {
29700 if (typeof(cfg.region) == 'undefined') {
29701 Roo.log("Failed to add Panel, region was not set");
29705 var region = cfg.region;
29711 xitems = cfg.items;
29718 case 'ContentPanel': // ContentPanel (el, cfg)
29719 case 'ScrollPanel': // ContentPanel (el, cfg)
29721 if(cfg.autoCreate) {
29722 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29724 var el = this.el.createChild();
29725 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29728 this.add(region, ret);
29732 case 'TreePanel': // our new panel!
29733 cfg.el = this.el.createChild();
29734 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29735 this.add(region, ret);
29738 case 'NestedLayoutPanel':
29739 // create a new Layout (which is a Border Layout...
29740 var el = this.el.createChild();
29741 var clayout = cfg.layout;
29743 clayout.items = clayout.items || [];
29744 // replace this exitems with the clayout ones..
29745 xitems = clayout.items;
29748 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29749 cfg.background = false;
29751 var layout = new Roo.BorderLayout(el, clayout);
29753 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29754 //console.log('adding nested layout panel ' + cfg.toSource());
29755 this.add(region, ret);
29756 nb = {}; /// find first...
29761 // needs grid and region
29763 //var el = this.getRegion(region).el.createChild();
29764 var el = this.el.createChild();
29765 // create the grid first...
29767 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29769 if (region == 'center' && this.active ) {
29770 cfg.background = false;
29772 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29774 this.add(region, ret);
29775 if (cfg.background) {
29776 ret.on('activate', function(gp) {
29777 if (!gp.grid.rendered) {
29792 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29794 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29795 this.add(region, ret);
29798 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29802 // GridPanel (grid, cfg)
29805 this.beginUpdate();
29809 Roo.each(xitems, function(i) {
29810 region = nb && i.region ? i.region : false;
29812 var add = ret.addxtype(i);
29815 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29816 if (!i.background) {
29817 abn[region] = nb[region] ;
29824 // make the last non-background panel active..
29825 //if (nb) { Roo.log(abn); }
29828 for(var r in abn) {
29829 region = this.getRegion(r);
29831 // tried using nb[r], but it does not work..
29833 region.showPanel(abn[r]);
29844 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29845 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29846 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29847 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29850 var CP = Roo.ContentPanel;
29852 var layout = Roo.BorderLayout.create({
29856 panels: [new CP("north", "North")]
29865 panels: [new CP("west", {title: "West"})]
29874 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29883 panels: [new CP("south", {title: "South", closable: true})]
29890 preferredTabWidth: 150,
29892 new CP("center1", {title: "Close Me", closable: true}),
29893 new CP("center2", {title: "Center Panel", closable: false})
29898 layout.getRegion("center").showPanel("center1");
29903 Roo.BorderLayout.create = function(config, targetEl){
29904 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29905 layout.beginUpdate();
29906 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29907 for(var j = 0, jlen = regions.length; j < jlen; j++){
29908 var lr = regions[j];
29909 if(layout.regions[lr] && config[lr].panels){
29910 var r = layout.regions[lr];
29911 var ps = config[lr].panels;
29912 layout.addTypedPanels(r, ps);
29915 layout.endUpdate();
29920 Roo.BorderLayout.RegionFactory = {
29922 validRegions : ["north","south","east","west","center"],
29925 create : function(target, mgr, config){
29926 target = target.toLowerCase();
29927 if(config.lightweight || config.basic){
29928 return new Roo.BasicLayoutRegion(mgr, config, target);
29932 return new Roo.NorthLayoutRegion(mgr, config);
29934 return new Roo.SouthLayoutRegion(mgr, config);
29936 return new Roo.EastLayoutRegion(mgr, config);
29938 return new Roo.WestLayoutRegion(mgr, config);
29940 return new Roo.CenterLayoutRegion(mgr, config);
29942 throw 'Layout region "'+target+'" not supported.';
29946 * Ext JS Library 1.1.1
29947 * Copyright(c) 2006-2007, Ext JS, LLC.
29949 * Originally Released Under LGPL - original licence link has changed is not relivant.
29952 * <script type="text/javascript">
29956 * @class Roo.BasicLayoutRegion
29957 * @extends Roo.util.Observable
29958 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29959 * and does not have a titlebar, tabs or any other features. All it does is size and position
29960 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29962 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29964 this.position = pos;
29967 * @scope Roo.BasicLayoutRegion
29971 * @event beforeremove
29972 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29973 * @param {Roo.LayoutRegion} this
29974 * @param {Roo.ContentPanel} panel The panel
29975 * @param {Object} e The cancel event object
29977 "beforeremove" : true,
29979 * @event invalidated
29980 * Fires when the layout for this region is changed.
29981 * @param {Roo.LayoutRegion} this
29983 "invalidated" : true,
29985 * @event visibilitychange
29986 * Fires when this region is shown or hidden
29987 * @param {Roo.LayoutRegion} this
29988 * @param {Boolean} visibility true or false
29990 "visibilitychange" : true,
29992 * @event paneladded
29993 * Fires when a panel is added.
29994 * @param {Roo.LayoutRegion} this
29995 * @param {Roo.ContentPanel} panel The panel
29997 "paneladded" : true,
29999 * @event panelremoved
30000 * Fires when a panel is removed.
30001 * @param {Roo.LayoutRegion} this
30002 * @param {Roo.ContentPanel} panel The panel
30004 "panelremoved" : true,
30006 * @event beforecollapse
30007 * Fires when this region before collapse.
30008 * @param {Roo.LayoutRegion} this
30010 "beforecollapse" : true,
30013 * Fires when this region is collapsed.
30014 * @param {Roo.LayoutRegion} this
30016 "collapsed" : true,
30019 * Fires when this region is expanded.
30020 * @param {Roo.LayoutRegion} this
30025 * Fires when this region is slid into view.
30026 * @param {Roo.LayoutRegion} this
30028 "slideshow" : true,
30031 * Fires when this region slides out of view.
30032 * @param {Roo.LayoutRegion} this
30034 "slidehide" : true,
30036 * @event panelactivated
30037 * Fires when a panel is activated.
30038 * @param {Roo.LayoutRegion} this
30039 * @param {Roo.ContentPanel} panel The activated panel
30041 "panelactivated" : true,
30044 * Fires when the user resizes this region.
30045 * @param {Roo.LayoutRegion} this
30046 * @param {Number} newSize The new size (width for east/west, height for north/south)
30050 /** A collection of panels in this region. @type Roo.util.MixedCollection */
30051 this.panels = new Roo.util.MixedCollection();
30052 this.panels.getKey = this.getPanelId.createDelegate(this);
30054 this.activePanel = null;
30055 // ensure listeners are added...
30057 if (config.listeners || config.events) {
30058 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30059 listeners : config.listeners || {},
30060 events : config.events || {}
30064 if(skipConfig !== true){
30065 this.applyConfig(config);
30069 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30070 getPanelId : function(p){
30074 applyConfig : function(config){
30075 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30076 this.config = config;
30081 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
30082 * the width, for horizontal (north, south) the height.
30083 * @param {Number} newSize The new width or height
30085 resizeTo : function(newSize){
30086 var el = this.el ? this.el :
30087 (this.activePanel ? this.activePanel.getEl() : null);
30089 switch(this.position){
30092 el.setWidth(newSize);
30093 this.fireEvent("resized", this, newSize);
30097 el.setHeight(newSize);
30098 this.fireEvent("resized", this, newSize);
30104 getBox : function(){
30105 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30108 getMargins : function(){
30109 return this.margins;
30112 updateBox : function(box){
30114 var el = this.activePanel.getEl();
30115 el.dom.style.left = box.x + "px";
30116 el.dom.style.top = box.y + "px";
30117 this.activePanel.setSize(box.width, box.height);
30121 * Returns the container element for this region.
30122 * @return {Roo.Element}
30124 getEl : function(){
30125 return this.activePanel;
30129 * Returns true if this region is currently visible.
30130 * @return {Boolean}
30132 isVisible : function(){
30133 return this.activePanel ? true : false;
30136 setActivePanel : function(panel){
30137 panel = this.getPanel(panel);
30138 if(this.activePanel && this.activePanel != panel){
30139 this.activePanel.setActiveState(false);
30140 this.activePanel.getEl().setLeftTop(-10000,-10000);
30142 this.activePanel = panel;
30143 panel.setActiveState(true);
30145 panel.setSize(this.box.width, this.box.height);
30147 this.fireEvent("panelactivated", this, panel);
30148 this.fireEvent("invalidated");
30152 * Show the specified panel.
30153 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30154 * @return {Roo.ContentPanel} The shown panel or null
30156 showPanel : function(panel){
30157 if(panel = this.getPanel(panel)){
30158 this.setActivePanel(panel);
30164 * Get the active panel for this region.
30165 * @return {Roo.ContentPanel} The active panel or null
30167 getActivePanel : function(){
30168 return this.activePanel;
30172 * Add the passed ContentPanel(s)
30173 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30174 * @return {Roo.ContentPanel} The panel added (if only one was added)
30176 add : function(panel){
30177 if(arguments.length > 1){
30178 for(var i = 0, len = arguments.length; i < len; i++) {
30179 this.add(arguments[i]);
30183 if(this.hasPanel(panel)){
30184 this.showPanel(panel);
30187 var el = panel.getEl();
30188 if(el.dom.parentNode != this.mgr.el.dom){
30189 this.mgr.el.dom.appendChild(el.dom);
30191 if(panel.setRegion){
30192 panel.setRegion(this);
30194 this.panels.add(panel);
30195 el.setStyle("position", "absolute");
30196 if(!panel.background){
30197 this.setActivePanel(panel);
30198 if(this.config.initialSize && this.panels.getCount()==1){
30199 this.resizeTo(this.config.initialSize);
30202 this.fireEvent("paneladded", this, panel);
30207 * Returns true if the panel is in this region.
30208 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30209 * @return {Boolean}
30211 hasPanel : function(panel){
30212 if(typeof panel == "object"){ // must be panel obj
30213 panel = panel.getId();
30215 return this.getPanel(panel) ? true : false;
30219 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30220 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30221 * @param {Boolean} preservePanel Overrides the config preservePanel option
30222 * @return {Roo.ContentPanel} The panel that was removed
30224 remove : function(panel, preservePanel){
30225 panel = this.getPanel(panel);
30230 this.fireEvent("beforeremove", this, panel, e);
30231 if(e.cancel === true){
30234 var panelId = panel.getId();
30235 this.panels.removeKey(panelId);
30240 * Returns the panel specified or null if it's not in this region.
30241 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30242 * @return {Roo.ContentPanel}
30244 getPanel : function(id){
30245 if(typeof id == "object"){ // must be panel obj
30248 return this.panels.get(id);
30252 * Returns this regions position (north/south/east/west/center).
30255 getPosition: function(){
30256 return this.position;
30260 * Ext JS Library 1.1.1
30261 * Copyright(c) 2006-2007, Ext JS, LLC.
30263 * Originally Released Under LGPL - original licence link has changed is not relivant.
30266 * <script type="text/javascript">
30270 * @class Roo.LayoutRegion
30271 * @extends Roo.BasicLayoutRegion
30272 * This class represents a region in a layout manager.
30273 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30274 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30275 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30276 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30277 * @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})
30278 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30279 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30280 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30281 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30282 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30283 * @cfg {String} title The title for the region (overrides panel titles)
30284 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30285 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30286 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30287 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30288 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30289 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30290 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30291 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30292 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30293 * @cfg {Boolean} showPin True to show a pin button
30294 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30295 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30296 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30297 * @cfg {Number} width For East/West panels
30298 * @cfg {Number} height For North/South panels
30299 * @cfg {Boolean} split To show the splitter
30300 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30302 Roo.LayoutRegion = function(mgr, config, pos){
30303 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30304 var dh = Roo.DomHelper;
30305 /** This region's container element
30306 * @type Roo.Element */
30307 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30308 /** This region's title element
30309 * @type Roo.Element */
30311 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30312 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30313 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30315 this.titleEl.enableDisplayMode();
30316 /** This region's title text element
30317 * @type HTMLElement */
30318 this.titleTextEl = this.titleEl.dom.firstChild;
30319 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30320 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30321 this.closeBtn.enableDisplayMode();
30322 this.closeBtn.on("click", this.closeClicked, this);
30323 this.closeBtn.hide();
30325 this.createBody(config);
30326 this.visible = true;
30327 this.collapsed = false;
30329 if(config.hideWhenEmpty){
30331 this.on("paneladded", this.validateVisibility, this);
30332 this.on("panelremoved", this.validateVisibility, this);
30334 this.applyConfig(config);
30337 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30339 createBody : function(){
30340 /** This region's body element
30341 * @type Roo.Element */
30342 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30345 applyConfig : function(c){
30346 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30347 var dh = Roo.DomHelper;
30348 if(c.titlebar !== false){
30349 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30350 this.collapseBtn.on("click", this.collapse, this);
30351 this.collapseBtn.enableDisplayMode();
30353 if(c.showPin === true || this.showPin){
30354 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30355 this.stickBtn.enableDisplayMode();
30356 this.stickBtn.on("click", this.expand, this);
30357 this.stickBtn.hide();
30360 /** This region's collapsed element
30361 * @type Roo.Element */
30362 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30363 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30365 if(c.floatable !== false){
30366 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30367 this.collapsedEl.on("click", this.collapseClick, this);
30370 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30371 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30372 id: "message", unselectable: "on", style:{"float":"left"}});
30373 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30375 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30376 this.expandBtn.on("click", this.expand, this);
30378 if(this.collapseBtn){
30379 this.collapseBtn.setVisible(c.collapsible == true);
30381 this.cmargins = c.cmargins || this.cmargins ||
30382 (this.position == "west" || this.position == "east" ?
30383 {top: 0, left: 2, right:2, bottom: 0} :
30384 {top: 2, left: 0, right:0, bottom: 2});
30385 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30386 this.bottomTabs = c.tabPosition != "top";
30387 this.autoScroll = c.autoScroll || false;
30388 if(this.autoScroll){
30389 this.bodyEl.setStyle("overflow", "auto");
30391 this.bodyEl.setStyle("overflow", "hidden");
30393 //if(c.titlebar !== false){
30394 if((!c.titlebar && !c.title) || c.titlebar === false){
30395 this.titleEl.hide();
30397 this.titleEl.show();
30399 this.titleTextEl.innerHTML = c.title;
30403 this.duration = c.duration || .30;
30404 this.slideDuration = c.slideDuration || .45;
30407 this.collapse(true);
30414 * Returns true if this region is currently visible.
30415 * @return {Boolean}
30417 isVisible : function(){
30418 return this.visible;
30422 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30423 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30425 setCollapsedTitle : function(title){
30426 title = title || " ";
30427 if(this.collapsedTitleTextEl){
30428 this.collapsedTitleTextEl.innerHTML = title;
30432 getBox : function(){
30434 if(!this.collapsed){
30435 b = this.el.getBox(false, true);
30437 b = this.collapsedEl.getBox(false, true);
30442 getMargins : function(){
30443 return this.collapsed ? this.cmargins : this.margins;
30446 highlight : function(){
30447 this.el.addClass("x-layout-panel-dragover");
30450 unhighlight : function(){
30451 this.el.removeClass("x-layout-panel-dragover");
30454 updateBox : function(box){
30456 if(!this.collapsed){
30457 this.el.dom.style.left = box.x + "px";
30458 this.el.dom.style.top = box.y + "px";
30459 this.updateBody(box.width, box.height);
30461 this.collapsedEl.dom.style.left = box.x + "px";
30462 this.collapsedEl.dom.style.top = box.y + "px";
30463 this.collapsedEl.setSize(box.width, box.height);
30466 this.tabs.autoSizeTabs();
30470 updateBody : function(w, h){
30472 this.el.setWidth(w);
30473 w -= this.el.getBorderWidth("rl");
30474 if(this.config.adjustments){
30475 w += this.config.adjustments[0];
30479 this.el.setHeight(h);
30480 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30481 h -= this.el.getBorderWidth("tb");
30482 if(this.config.adjustments){
30483 h += this.config.adjustments[1];
30485 this.bodyEl.setHeight(h);
30487 h = this.tabs.syncHeight(h);
30490 if(this.panelSize){
30491 w = w !== null ? w : this.panelSize.width;
30492 h = h !== null ? h : this.panelSize.height;
30494 if(this.activePanel){
30495 var el = this.activePanel.getEl();
30496 w = w !== null ? w : el.getWidth();
30497 h = h !== null ? h : el.getHeight();
30498 this.panelSize = {width: w, height: h};
30499 this.activePanel.setSize(w, h);
30501 if(Roo.isIE && this.tabs){
30502 this.tabs.el.repaint();
30507 * Returns the container element for this region.
30508 * @return {Roo.Element}
30510 getEl : function(){
30515 * Hides this region.
30518 if(!this.collapsed){
30519 this.el.dom.style.left = "-2000px";
30522 this.collapsedEl.dom.style.left = "-2000px";
30523 this.collapsedEl.hide();
30525 this.visible = false;
30526 this.fireEvent("visibilitychange", this, false);
30530 * Shows this region if it was previously hidden.
30533 if(!this.collapsed){
30536 this.collapsedEl.show();
30538 this.visible = true;
30539 this.fireEvent("visibilitychange", this, true);
30542 closeClicked : function(){
30543 if(this.activePanel){
30544 this.remove(this.activePanel);
30548 collapseClick : function(e){
30550 e.stopPropagation();
30553 e.stopPropagation();
30559 * Collapses this region.
30560 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30562 collapse : function(skipAnim, skipCheck){
30563 if(this.collapsed) {
30567 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30569 this.collapsed = true;
30571 this.split.el.hide();
30573 if(this.config.animate && skipAnim !== true){
30574 this.fireEvent("invalidated", this);
30575 this.animateCollapse();
30577 this.el.setLocation(-20000,-20000);
30579 this.collapsedEl.show();
30580 this.fireEvent("collapsed", this);
30581 this.fireEvent("invalidated", this);
30587 animateCollapse : function(){
30592 * Expands this region if it was previously collapsed.
30593 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30594 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30596 expand : function(e, skipAnim){
30598 e.stopPropagation();
30600 if(!this.collapsed || this.el.hasActiveFx()) {
30604 this.afterSlideIn();
30607 this.collapsed = false;
30608 if(this.config.animate && skipAnim !== true){
30609 this.animateExpand();
30613 this.split.el.show();
30615 this.collapsedEl.setLocation(-2000,-2000);
30616 this.collapsedEl.hide();
30617 this.fireEvent("invalidated", this);
30618 this.fireEvent("expanded", this);
30622 animateExpand : function(){
30626 initTabs : function()
30628 this.bodyEl.setStyle("overflow", "hidden");
30629 var ts = new Roo.TabPanel(
30632 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30633 disableTooltips: this.config.disableTabTips,
30634 toolbar : this.config.toolbar
30637 if(this.config.hideTabs){
30638 ts.stripWrap.setDisplayed(false);
30641 ts.resizeTabs = this.config.resizeTabs === true;
30642 ts.minTabWidth = this.config.minTabWidth || 40;
30643 ts.maxTabWidth = this.config.maxTabWidth || 250;
30644 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30645 ts.monitorResize = false;
30646 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30647 ts.bodyEl.addClass('x-layout-tabs-body');
30648 this.panels.each(this.initPanelAsTab, this);
30651 initPanelAsTab : function(panel){
30652 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30653 this.config.closeOnTab && panel.isClosable());
30654 if(panel.tabTip !== undefined){
30655 ti.setTooltip(panel.tabTip);
30657 ti.on("activate", function(){
30658 this.setActivePanel(panel);
30660 if(this.config.closeOnTab){
30661 ti.on("beforeclose", function(t, e){
30663 this.remove(panel);
30669 updatePanelTitle : function(panel, title){
30670 if(this.activePanel == panel){
30671 this.updateTitle(title);
30674 var ti = this.tabs.getTab(panel.getEl().id);
30676 if(panel.tabTip !== undefined){
30677 ti.setTooltip(panel.tabTip);
30682 updateTitle : function(title){
30683 if(this.titleTextEl && !this.config.title){
30684 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30688 setActivePanel : function(panel){
30689 panel = this.getPanel(panel);
30690 if(this.activePanel && this.activePanel != panel){
30691 this.activePanel.setActiveState(false);
30693 this.activePanel = panel;
30694 panel.setActiveState(true);
30695 if(this.panelSize){
30696 panel.setSize(this.panelSize.width, this.panelSize.height);
30699 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30701 this.updateTitle(panel.getTitle());
30703 this.fireEvent("invalidated", this);
30705 this.fireEvent("panelactivated", this, panel);
30709 * Shows the specified panel.
30710 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30711 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30713 showPanel : function(panel)
30715 panel = this.getPanel(panel);
30718 var tab = this.tabs.getTab(panel.getEl().id);
30719 if(tab.isHidden()){
30720 this.tabs.unhideTab(tab.id);
30724 this.setActivePanel(panel);
30731 * Get the active panel for this region.
30732 * @return {Roo.ContentPanel} The active panel or null
30734 getActivePanel : function(){
30735 return this.activePanel;
30738 validateVisibility : function(){
30739 if(this.panels.getCount() < 1){
30740 this.updateTitle(" ");
30741 this.closeBtn.hide();
30744 if(!this.isVisible()){
30751 * Adds the passed ContentPanel(s) to this region.
30752 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30753 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30755 add : function(panel){
30756 if(arguments.length > 1){
30757 for(var i = 0, len = arguments.length; i < len; i++) {
30758 this.add(arguments[i]);
30762 if(this.hasPanel(panel)){
30763 this.showPanel(panel);
30766 panel.setRegion(this);
30767 this.panels.add(panel);
30768 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30769 this.bodyEl.dom.appendChild(panel.getEl().dom);
30770 if(panel.background !== true){
30771 this.setActivePanel(panel);
30773 this.fireEvent("paneladded", this, panel);
30779 this.initPanelAsTab(panel);
30781 if(panel.background !== true){
30782 this.tabs.activate(panel.getEl().id);
30784 this.fireEvent("paneladded", this, panel);
30789 * Hides the tab for the specified panel.
30790 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30792 hidePanel : function(panel){
30793 if(this.tabs && (panel = this.getPanel(panel))){
30794 this.tabs.hideTab(panel.getEl().id);
30799 * Unhides the tab for a previously hidden panel.
30800 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30802 unhidePanel : function(panel){
30803 if(this.tabs && (panel = this.getPanel(panel))){
30804 this.tabs.unhideTab(panel.getEl().id);
30808 clearPanels : function(){
30809 while(this.panels.getCount() > 0){
30810 this.remove(this.panels.first());
30815 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30816 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30817 * @param {Boolean} preservePanel Overrides the config preservePanel option
30818 * @return {Roo.ContentPanel} The panel that was removed
30820 remove : function(panel, preservePanel){
30821 panel = this.getPanel(panel);
30826 this.fireEvent("beforeremove", this, panel, e);
30827 if(e.cancel === true){
30830 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30831 var panelId = panel.getId();
30832 this.panels.removeKey(panelId);
30834 document.body.appendChild(panel.getEl().dom);
30837 this.tabs.removeTab(panel.getEl().id);
30838 }else if (!preservePanel){
30839 this.bodyEl.dom.removeChild(panel.getEl().dom);
30841 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30842 var p = this.panels.first();
30843 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30844 tempEl.appendChild(p.getEl().dom);
30845 this.bodyEl.update("");
30846 this.bodyEl.dom.appendChild(p.getEl().dom);
30848 this.updateTitle(p.getTitle());
30850 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30851 this.setActivePanel(p);
30853 panel.setRegion(null);
30854 if(this.activePanel == panel){
30855 this.activePanel = null;
30857 if(this.config.autoDestroy !== false && preservePanel !== true){
30858 try{panel.destroy();}catch(e){}
30860 this.fireEvent("panelremoved", this, panel);
30865 * Returns the TabPanel component used by this region
30866 * @return {Roo.TabPanel}
30868 getTabs : function(){
30872 createTool : function(parentEl, className){
30873 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30874 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30875 btn.addClassOnOver("x-layout-tools-button-over");
30880 * Ext JS Library 1.1.1
30881 * Copyright(c) 2006-2007, Ext JS, LLC.
30883 * Originally Released Under LGPL - original licence link has changed is not relivant.
30886 * <script type="text/javascript">
30892 * @class Roo.SplitLayoutRegion
30893 * @extends Roo.LayoutRegion
30894 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30896 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30897 this.cursor = cursor;
30898 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30901 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30902 splitTip : "Drag to resize.",
30903 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30904 useSplitTips : false,
30906 applyConfig : function(config){
30907 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30910 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30911 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30912 /** The SplitBar for this region
30913 * @type Roo.SplitBar */
30914 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30915 this.split.on("moved", this.onSplitMove, this);
30916 this.split.useShim = config.useShim === true;
30917 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30918 if(this.useSplitTips){
30919 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30921 if(config.collapsible){
30922 this.split.el.on("dblclick", this.collapse, this);
30925 if(typeof config.minSize != "undefined"){
30926 this.split.minSize = config.minSize;
30928 if(typeof config.maxSize != "undefined"){
30929 this.split.maxSize = config.maxSize;
30931 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30932 this.hideSplitter();
30937 getHMaxSize : function(){
30938 var cmax = this.config.maxSize || 10000;
30939 var center = this.mgr.getRegion("center");
30940 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30943 getVMaxSize : function(){
30944 var cmax = this.config.maxSize || 10000;
30945 var center = this.mgr.getRegion("center");
30946 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30949 onSplitMove : function(split, newSize){
30950 this.fireEvent("resized", this, newSize);
30954 * Returns the {@link Roo.SplitBar} for this region.
30955 * @return {Roo.SplitBar}
30957 getSplitBar : function(){
30962 this.hideSplitter();
30963 Roo.SplitLayoutRegion.superclass.hide.call(this);
30966 hideSplitter : function(){
30968 this.split.el.setLocation(-2000,-2000);
30969 this.split.el.hide();
30975 this.split.el.show();
30977 Roo.SplitLayoutRegion.superclass.show.call(this);
30980 beforeSlide: function(){
30981 if(Roo.isGecko){// firefox overflow auto bug workaround
30982 this.bodyEl.clip();
30984 this.tabs.bodyEl.clip();
30986 if(this.activePanel){
30987 this.activePanel.getEl().clip();
30989 if(this.activePanel.beforeSlide){
30990 this.activePanel.beforeSlide();
30996 afterSlide : function(){
30997 if(Roo.isGecko){// firefox overflow auto bug workaround
30998 this.bodyEl.unclip();
31000 this.tabs.bodyEl.unclip();
31002 if(this.activePanel){
31003 this.activePanel.getEl().unclip();
31004 if(this.activePanel.afterSlide){
31005 this.activePanel.afterSlide();
31011 initAutoHide : function(){
31012 if(this.autoHide !== false){
31013 if(!this.autoHideHd){
31014 var st = new Roo.util.DelayedTask(this.slideIn, this);
31015 this.autoHideHd = {
31016 "mouseout": function(e){
31017 if(!e.within(this.el, true)){
31021 "mouseover" : function(e){
31027 this.el.on(this.autoHideHd);
31031 clearAutoHide : function(){
31032 if(this.autoHide !== false){
31033 this.el.un("mouseout", this.autoHideHd.mouseout);
31034 this.el.un("mouseover", this.autoHideHd.mouseover);
31038 clearMonitor : function(){
31039 Roo.get(document).un("click", this.slideInIf, this);
31042 // these names are backwards but not changed for compat
31043 slideOut : function(){
31044 if(this.isSlid || this.el.hasActiveFx()){
31047 this.isSlid = true;
31048 if(this.collapseBtn){
31049 this.collapseBtn.hide();
31051 this.closeBtnState = this.closeBtn.getStyle('display');
31052 this.closeBtn.hide();
31054 this.stickBtn.show();
31057 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31058 this.beforeSlide();
31059 this.el.setStyle("z-index", 10001);
31060 this.el.slideIn(this.getSlideAnchor(), {
31061 callback: function(){
31063 this.initAutoHide();
31064 Roo.get(document).on("click", this.slideInIf, this);
31065 this.fireEvent("slideshow", this);
31072 afterSlideIn : function(){
31073 this.clearAutoHide();
31074 this.isSlid = false;
31075 this.clearMonitor();
31076 this.el.setStyle("z-index", "");
31077 if(this.collapseBtn){
31078 this.collapseBtn.show();
31080 this.closeBtn.setStyle('display', this.closeBtnState);
31082 this.stickBtn.hide();
31084 this.fireEvent("slidehide", this);
31087 slideIn : function(cb){
31088 if(!this.isSlid || this.el.hasActiveFx()){
31092 this.isSlid = false;
31093 this.beforeSlide();
31094 this.el.slideOut(this.getSlideAnchor(), {
31095 callback: function(){
31096 this.el.setLeftTop(-10000, -10000);
31098 this.afterSlideIn();
31106 slideInIf : function(e){
31107 if(!e.within(this.el)){
31112 animateCollapse : function(){
31113 this.beforeSlide();
31114 this.el.setStyle("z-index", 20000);
31115 var anchor = this.getSlideAnchor();
31116 this.el.slideOut(anchor, {
31117 callback : function(){
31118 this.el.setStyle("z-index", "");
31119 this.collapsedEl.slideIn(anchor, {duration:.3});
31121 this.el.setLocation(-10000,-10000);
31123 this.fireEvent("collapsed", this);
31130 animateExpand : function(){
31131 this.beforeSlide();
31132 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31133 this.el.setStyle("z-index", 20000);
31134 this.collapsedEl.hide({
31137 this.el.slideIn(this.getSlideAnchor(), {
31138 callback : function(){
31139 this.el.setStyle("z-index", "");
31142 this.split.el.show();
31144 this.fireEvent("invalidated", this);
31145 this.fireEvent("expanded", this);
31173 getAnchor : function(){
31174 return this.anchors[this.position];
31177 getCollapseAnchor : function(){
31178 return this.canchors[this.position];
31181 getSlideAnchor : function(){
31182 return this.sanchors[this.position];
31185 getAlignAdj : function(){
31186 var cm = this.cmargins;
31187 switch(this.position){
31203 getExpandAdj : function(){
31204 var c = this.collapsedEl, cm = this.cmargins;
31205 switch(this.position){
31207 return [-(cm.right+c.getWidth()+cm.left), 0];
31210 return [cm.right+c.getWidth()+cm.left, 0];
31213 return [0, -(cm.top+cm.bottom+c.getHeight())];
31216 return [0, cm.top+cm.bottom+c.getHeight()];
31222 * Ext JS Library 1.1.1
31223 * Copyright(c) 2006-2007, Ext JS, LLC.
31225 * Originally Released Under LGPL - original licence link has changed is not relivant.
31228 * <script type="text/javascript">
31231 * These classes are private internal classes
31233 Roo.CenterLayoutRegion = function(mgr, config){
31234 Roo.LayoutRegion.call(this, mgr, config, "center");
31235 this.visible = true;
31236 this.minWidth = config.minWidth || 20;
31237 this.minHeight = config.minHeight || 20;
31240 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31242 // center panel can't be hidden
31246 // center panel can't be hidden
31249 getMinWidth: function(){
31250 return this.minWidth;
31253 getMinHeight: function(){
31254 return this.minHeight;
31259 Roo.NorthLayoutRegion = function(mgr, config){
31260 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31262 this.split.placement = Roo.SplitBar.TOP;
31263 this.split.orientation = Roo.SplitBar.VERTICAL;
31264 this.split.el.addClass("x-layout-split-v");
31266 var size = config.initialSize || config.height;
31267 if(typeof size != "undefined"){
31268 this.el.setHeight(size);
31271 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31272 orientation: Roo.SplitBar.VERTICAL,
31273 getBox : function(){
31274 if(this.collapsed){
31275 return this.collapsedEl.getBox();
31277 var box = this.el.getBox();
31279 box.height += this.split.el.getHeight();
31284 updateBox : function(box){
31285 if(this.split && !this.collapsed){
31286 box.height -= this.split.el.getHeight();
31287 this.split.el.setLeft(box.x);
31288 this.split.el.setTop(box.y+box.height);
31289 this.split.el.setWidth(box.width);
31291 if(this.collapsed){
31292 this.updateBody(box.width, null);
31294 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31298 Roo.SouthLayoutRegion = function(mgr, config){
31299 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31301 this.split.placement = Roo.SplitBar.BOTTOM;
31302 this.split.orientation = Roo.SplitBar.VERTICAL;
31303 this.split.el.addClass("x-layout-split-v");
31305 var size = config.initialSize || config.height;
31306 if(typeof size != "undefined"){
31307 this.el.setHeight(size);
31310 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31311 orientation: Roo.SplitBar.VERTICAL,
31312 getBox : function(){
31313 if(this.collapsed){
31314 return this.collapsedEl.getBox();
31316 var box = this.el.getBox();
31318 var sh = this.split.el.getHeight();
31325 updateBox : function(box){
31326 if(this.split && !this.collapsed){
31327 var sh = this.split.el.getHeight();
31330 this.split.el.setLeft(box.x);
31331 this.split.el.setTop(box.y-sh);
31332 this.split.el.setWidth(box.width);
31334 if(this.collapsed){
31335 this.updateBody(box.width, null);
31337 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31341 Roo.EastLayoutRegion = function(mgr, config){
31342 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31344 this.split.placement = Roo.SplitBar.RIGHT;
31345 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31346 this.split.el.addClass("x-layout-split-h");
31348 var size = config.initialSize || config.width;
31349 if(typeof size != "undefined"){
31350 this.el.setWidth(size);
31353 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31354 orientation: Roo.SplitBar.HORIZONTAL,
31355 getBox : function(){
31356 if(this.collapsed){
31357 return this.collapsedEl.getBox();
31359 var box = this.el.getBox();
31361 var sw = this.split.el.getWidth();
31368 updateBox : function(box){
31369 if(this.split && !this.collapsed){
31370 var sw = this.split.el.getWidth();
31372 this.split.el.setLeft(box.x);
31373 this.split.el.setTop(box.y);
31374 this.split.el.setHeight(box.height);
31377 if(this.collapsed){
31378 this.updateBody(null, box.height);
31380 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31384 Roo.WestLayoutRegion = function(mgr, config){
31385 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31387 this.split.placement = Roo.SplitBar.LEFT;
31388 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31389 this.split.el.addClass("x-layout-split-h");
31391 var size = config.initialSize || config.width;
31392 if(typeof size != "undefined"){
31393 this.el.setWidth(size);
31396 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31397 orientation: Roo.SplitBar.HORIZONTAL,
31398 getBox : function(){
31399 if(this.collapsed){
31400 return this.collapsedEl.getBox();
31402 var box = this.el.getBox();
31404 box.width += this.split.el.getWidth();
31409 updateBox : function(box){
31410 if(this.split && !this.collapsed){
31411 var sw = this.split.el.getWidth();
31413 this.split.el.setLeft(box.x+box.width);
31414 this.split.el.setTop(box.y);
31415 this.split.el.setHeight(box.height);
31417 if(this.collapsed){
31418 this.updateBody(null, box.height);
31420 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31425 * Ext JS Library 1.1.1
31426 * Copyright(c) 2006-2007, Ext JS, LLC.
31428 * Originally Released Under LGPL - original licence link has changed is not relivant.
31431 * <script type="text/javascript">
31436 * Private internal class for reading and applying state
31438 Roo.LayoutStateManager = function(layout){
31439 // default empty state
31448 Roo.LayoutStateManager.prototype = {
31449 init : function(layout, provider){
31450 this.provider = provider;
31451 var state = provider.get(layout.id+"-layout-state");
31453 var wasUpdating = layout.isUpdating();
31455 layout.beginUpdate();
31457 for(var key in state){
31458 if(typeof state[key] != "function"){
31459 var rstate = state[key];
31460 var r = layout.getRegion(key);
31463 r.resizeTo(rstate.size);
31465 if(rstate.collapsed == true){
31468 r.expand(null, true);
31474 layout.endUpdate();
31476 this.state = state;
31478 this.layout = layout;
31479 layout.on("regionresized", this.onRegionResized, this);
31480 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31481 layout.on("regionexpanded", this.onRegionExpanded, this);
31484 storeState : function(){
31485 this.provider.set(this.layout.id+"-layout-state", this.state);
31488 onRegionResized : function(region, newSize){
31489 this.state[region.getPosition()].size = newSize;
31493 onRegionCollapsed : function(region){
31494 this.state[region.getPosition()].collapsed = true;
31498 onRegionExpanded : function(region){
31499 this.state[region.getPosition()].collapsed = false;
31504 * Ext JS Library 1.1.1
31505 * Copyright(c) 2006-2007, Ext JS, LLC.
31507 * Originally Released Under LGPL - original licence link has changed is not relivant.
31510 * <script type="text/javascript">
31513 * @class Roo.ContentPanel
31514 * @extends Roo.util.Observable
31515 * A basic ContentPanel element.
31516 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31517 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31518 * @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
31519 * @cfg {Boolean} closable True if the panel can be closed/removed
31520 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31521 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31522 * @cfg {Toolbar} toolbar A toolbar for this panel
31523 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31524 * @cfg {String} title The title for this panel
31525 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31526 * @cfg {String} url Calls {@link #setUrl} with this value
31527 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31528 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31529 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31530 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31533 * Create a new ContentPanel.
31534 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31535 * @param {String/Object} config A string to set only the title or a config object
31536 * @param {String} content (optional) Set the HTML content for this panel
31537 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31539 Roo.ContentPanel = function(el, config, content){
31543 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31547 if (config && config.parentLayout) {
31548 el = config.parentLayout.el.createChild();
31551 if(el.autoCreate){ // xtype is available if this is called from factory
31555 this.el = Roo.get(el);
31556 if(!this.el && config && config.autoCreate){
31557 if(typeof config.autoCreate == "object"){
31558 if(!config.autoCreate.id){
31559 config.autoCreate.id = config.id||el;
31561 this.el = Roo.DomHelper.append(document.body,
31562 config.autoCreate, true);
31564 this.el = Roo.DomHelper.append(document.body,
31565 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31568 this.closable = false;
31569 this.loaded = false;
31570 this.active = false;
31571 if(typeof config == "string"){
31572 this.title = config;
31574 Roo.apply(this, config);
31577 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31578 this.wrapEl = this.el.wrap();
31579 this.toolbar.container = this.el.insertSibling(false, 'before');
31580 this.toolbar = new Roo.Toolbar(this.toolbar);
31583 // xtype created footer. - not sure if will work as we normally have to render first..
31584 if (this.footer && !this.footer.el && this.footer.xtype) {
31585 if (!this.wrapEl) {
31586 this.wrapEl = this.el.wrap();
31589 this.footer.container = this.wrapEl.createChild();
31591 this.footer = Roo.factory(this.footer, Roo);
31596 this.resizeEl = Roo.get(this.resizeEl, true);
31598 this.resizeEl = this.el;
31600 // handle view.xtype
31608 * Fires when this panel is activated.
31609 * @param {Roo.ContentPanel} this
31613 * @event deactivate
31614 * Fires when this panel is activated.
31615 * @param {Roo.ContentPanel} this
31617 "deactivate" : true,
31621 * Fires when this panel is resized if fitToFrame is true.
31622 * @param {Roo.ContentPanel} this
31623 * @param {Number} width The width after any component adjustments
31624 * @param {Number} height The height after any component adjustments
31630 * Fires when this tab is created
31631 * @param {Roo.ContentPanel} this
31641 if(this.autoScroll){
31642 this.resizeEl.setStyle("overflow", "auto");
31644 // fix randome scrolling
31645 this.el.on('scroll', function() {
31646 Roo.log('fix random scolling');
31647 this.scrollTo('top',0);
31650 content = content || this.content;
31652 this.setContent(content);
31654 if(config && config.url){
31655 this.setUrl(this.url, this.params, this.loadOnce);
31660 Roo.ContentPanel.superclass.constructor.call(this);
31662 if (this.view && typeof(this.view.xtype) != 'undefined') {
31663 this.view.el = this.el.appendChild(document.createElement("div"));
31664 this.view = Roo.factory(this.view);
31665 this.view.render && this.view.render(false, '');
31669 this.fireEvent('render', this);
31672 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31674 setRegion : function(region){
31675 this.region = region;
31677 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31679 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31684 * Returns the toolbar for this Panel if one was configured.
31685 * @return {Roo.Toolbar}
31687 getToolbar : function(){
31688 return this.toolbar;
31691 setActiveState : function(active){
31692 this.active = active;
31694 this.fireEvent("deactivate", this);
31696 this.fireEvent("activate", this);
31700 * Updates this panel's element
31701 * @param {String} content The new content
31702 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31704 setContent : function(content, loadScripts){
31705 this.el.update(content, loadScripts);
31708 ignoreResize : function(w, h){
31709 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31712 this.lastSize = {width: w, height: h};
31717 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31718 * @return {Roo.UpdateManager} The UpdateManager
31720 getUpdateManager : function(){
31721 return this.el.getUpdateManager();
31724 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31725 * @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:
31728 url: "your-url.php",
31729 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31730 callback: yourFunction,
31731 scope: yourObject, //(optional scope)
31734 text: "Loading...",
31739 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31740 * 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.
31741 * @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}
31742 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31743 * @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.
31744 * @return {Roo.ContentPanel} this
31747 var um = this.el.getUpdateManager();
31748 um.update.apply(um, arguments);
31754 * 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.
31755 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31756 * @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)
31757 * @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)
31758 * @return {Roo.UpdateManager} The UpdateManager
31760 setUrl : function(url, params, loadOnce){
31761 if(this.refreshDelegate){
31762 this.removeListener("activate", this.refreshDelegate);
31764 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31765 this.on("activate", this.refreshDelegate);
31766 return this.el.getUpdateManager();
31769 _handleRefresh : function(url, params, loadOnce){
31770 if(!loadOnce || !this.loaded){
31771 var updater = this.el.getUpdateManager();
31772 updater.update(url, params, this._setLoaded.createDelegate(this));
31776 _setLoaded : function(){
31777 this.loaded = true;
31781 * Returns this panel's id
31784 getId : function(){
31789 * Returns this panel's element - used by regiosn to add.
31790 * @return {Roo.Element}
31792 getEl : function(){
31793 return this.wrapEl || this.el;
31796 adjustForComponents : function(width, height)
31798 //Roo.log('adjustForComponents ');
31799 if(this.resizeEl != this.el){
31800 width -= this.el.getFrameWidth('lr');
31801 height -= this.el.getFrameWidth('tb');
31804 var te = this.toolbar.getEl();
31805 height -= te.getHeight();
31806 te.setWidth(width);
31809 var te = this.footer.getEl();
31810 //Roo.log("footer:" + te.getHeight());
31812 height -= te.getHeight();
31813 te.setWidth(width);
31817 if(this.adjustments){
31818 width += this.adjustments[0];
31819 height += this.adjustments[1];
31821 return {"width": width, "height": height};
31824 setSize : function(width, height){
31825 if(this.fitToFrame && !this.ignoreResize(width, height)){
31826 if(this.fitContainer && this.resizeEl != this.el){
31827 this.el.setSize(width, height);
31829 var size = this.adjustForComponents(width, height);
31830 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31831 this.fireEvent('resize', this, size.width, size.height);
31836 * Returns this panel's title
31839 getTitle : function(){
31844 * Set this panel's title
31845 * @param {String} title
31847 setTitle : function(title){
31848 this.title = title;
31850 this.region.updatePanelTitle(this, title);
31855 * Returns true is this panel was configured to be closable
31856 * @return {Boolean}
31858 isClosable : function(){
31859 return this.closable;
31862 beforeSlide : function(){
31864 this.resizeEl.clip();
31867 afterSlide : function(){
31869 this.resizeEl.unclip();
31873 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31874 * Will fail silently if the {@link #setUrl} method has not been called.
31875 * This does not activate the panel, just updates its content.
31877 refresh : function(){
31878 if(this.refreshDelegate){
31879 this.loaded = false;
31880 this.refreshDelegate();
31885 * Destroys this panel
31887 destroy : function(){
31888 this.el.removeAllListeners();
31889 var tempEl = document.createElement("span");
31890 tempEl.appendChild(this.el.dom);
31891 tempEl.innerHTML = "";
31897 * form - if the content panel contains a form - this is a reference to it.
31898 * @type {Roo.form.Form}
31902 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31903 * This contains a reference to it.
31909 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31919 * @param {Object} cfg Xtype definition of item to add.
31922 addxtype : function(cfg) {
31924 if (cfg.xtype.match(/^Form$/)) {
31927 //if (this.footer) {
31928 // el = this.footer.container.insertSibling(false, 'before');
31930 el = this.el.createChild();
31933 this.form = new Roo.form.Form(cfg);
31936 if ( this.form.allItems.length) {
31937 this.form.render(el.dom);
31941 // should only have one of theses..
31942 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31943 // views.. should not be just added - used named prop 'view''
31945 cfg.el = this.el.appendChild(document.createElement("div"));
31948 var ret = new Roo.factory(cfg);
31950 ret.render && ret.render(false, ''); // render blank..
31959 * @class Roo.GridPanel
31960 * @extends Roo.ContentPanel
31962 * Create a new GridPanel.
31963 * @param {Roo.grid.Grid} grid The grid for this panel
31964 * @param {String/Object} config A string to set only the panel's title, or a config object
31966 Roo.GridPanel = function(grid, config){
31969 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31970 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31972 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31974 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31977 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31979 // xtype created footer. - not sure if will work as we normally have to render first..
31980 if (this.footer && !this.footer.el && this.footer.xtype) {
31982 this.footer.container = this.grid.getView().getFooterPanel(true);
31983 this.footer.dataSource = this.grid.dataSource;
31984 this.footer = Roo.factory(this.footer, Roo);
31988 grid.monitorWindowResize = false; // turn off autosizing
31989 grid.autoHeight = false;
31990 grid.autoWidth = false;
31992 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31995 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31996 getId : function(){
31997 return this.grid.id;
32001 * Returns the grid for this panel
32002 * @return {Roo.grid.Grid}
32004 getGrid : function(){
32008 setSize : function(width, height){
32009 if(!this.ignoreResize(width, height)){
32010 var grid = this.grid;
32011 var size = this.adjustForComponents(width, height);
32012 grid.getGridEl().setSize(size.width, size.height);
32017 beforeSlide : function(){
32018 this.grid.getView().scroller.clip();
32021 afterSlide : function(){
32022 this.grid.getView().scroller.unclip();
32025 destroy : function(){
32026 this.grid.destroy();
32028 Roo.GridPanel.superclass.destroy.call(this);
32034 * @class Roo.NestedLayoutPanel
32035 * @extends Roo.ContentPanel
32037 * Create a new NestedLayoutPanel.
32040 * @param {Roo.BorderLayout} layout The layout for this panel
32041 * @param {String/Object} config A string to set only the title or a config object
32043 Roo.NestedLayoutPanel = function(layout, config)
32045 // construct with only one argument..
32046 /* FIXME - implement nicer consturctors
32047 if (layout.layout) {
32049 layout = config.layout;
32050 delete config.layout;
32052 if (layout.xtype && !layout.getEl) {
32053 // then layout needs constructing..
32054 layout = Roo.factory(layout, Roo);
32059 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32061 layout.monitorWindowResize = false; // turn off autosizing
32062 this.layout = layout;
32063 this.layout.getEl().addClass("x-layout-nested-layout");
32070 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32072 setSize : function(width, height){
32073 if(!this.ignoreResize(width, height)){
32074 var size = this.adjustForComponents(width, height);
32075 var el = this.layout.getEl();
32076 el.setSize(size.width, size.height);
32077 var touch = el.dom.offsetWidth;
32078 this.layout.layout();
32079 // ie requires a double layout on the first pass
32080 if(Roo.isIE && !this.initialized){
32081 this.initialized = true;
32082 this.layout.layout();
32087 // activate all subpanels if not currently active..
32089 setActiveState : function(active){
32090 this.active = active;
32092 this.fireEvent("deactivate", this);
32096 this.fireEvent("activate", this);
32097 // not sure if this should happen before or after..
32098 if (!this.layout) {
32099 return; // should not happen..
32102 for (var r in this.layout.regions) {
32103 reg = this.layout.getRegion(r);
32104 if (reg.getActivePanel()) {
32105 //reg.showPanel(reg.getActivePanel()); // force it to activate..
32106 reg.setActivePanel(reg.getActivePanel());
32109 if (!reg.panels.length) {
32112 reg.showPanel(reg.getPanel(0));
32121 * Returns the nested BorderLayout for this panel
32122 * @return {Roo.BorderLayout}
32124 getLayout : function(){
32125 return this.layout;
32129 * Adds a xtype elements to the layout of the nested panel
32133 xtype : 'ContentPanel',
32140 xtype : 'NestedLayoutPanel',
32146 items : [ ... list of content panels or nested layout panels.. ]
32150 * @param {Object} cfg Xtype definition of item to add.
32152 addxtype : function(cfg) {
32153 return this.layout.addxtype(cfg);
32158 Roo.ScrollPanel = function(el, config, content){
32159 config = config || {};
32160 config.fitToFrame = true;
32161 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32163 this.el.dom.style.overflow = "hidden";
32164 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32165 this.el.removeClass("x-layout-inactive-content");
32166 this.el.on("mousewheel", this.onWheel, this);
32168 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
32169 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
32170 up.unselectable(); down.unselectable();
32171 up.on("click", this.scrollUp, this);
32172 down.on("click", this.scrollDown, this);
32173 up.addClassOnOver("x-scroller-btn-over");
32174 down.addClassOnOver("x-scroller-btn-over");
32175 up.addClassOnClick("x-scroller-btn-click");
32176 down.addClassOnClick("x-scroller-btn-click");
32177 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32179 this.resizeEl = this.el;
32180 this.el = wrap; this.up = up; this.down = down;
32183 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32185 wheelIncrement : 5,
32186 scrollUp : function(){
32187 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32190 scrollDown : function(){
32191 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32194 afterScroll : function(){
32195 var el = this.resizeEl;
32196 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32197 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32198 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32201 setSize : function(){
32202 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32203 this.afterScroll();
32206 onWheel : function(e){
32207 var d = e.getWheelDelta();
32208 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32209 this.afterScroll();
32213 setContent : function(content, loadScripts){
32214 this.resizeEl.update(content, loadScripts);
32228 * @class Roo.TreePanel
32229 * @extends Roo.ContentPanel
32231 * Create a new TreePanel. - defaults to fit/scoll contents.
32232 * @param {String/Object} config A string to set only the panel's title, or a config object
32233 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32235 Roo.TreePanel = function(config){
32236 var el = config.el;
32237 var tree = config.tree;
32238 delete config.tree;
32239 delete config.el; // hopefull!
32241 // wrapper for IE7 strict & safari scroll issue
32243 var treeEl = el.createChild();
32244 config.resizeEl = treeEl;
32248 Roo.TreePanel.superclass.constructor.call(this, el, config);
32251 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32252 //console.log(tree);
32253 this.on('activate', function()
32255 if (this.tree.rendered) {
32258 //console.log('render tree');
32259 this.tree.render();
32261 // this should not be needed.. - it's actually the 'el' that resizes?
32262 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32264 //this.on('resize', function (cp, w, h) {
32265 // this.tree.innerCt.setWidth(w);
32266 // this.tree.innerCt.setHeight(h);
32267 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32274 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32291 * Ext JS Library 1.1.1
32292 * Copyright(c) 2006-2007, Ext JS, LLC.
32294 * Originally Released Under LGPL - original licence link has changed is not relivant.
32297 * <script type="text/javascript">
32302 * @class Roo.ReaderLayout
32303 * @extends Roo.BorderLayout
32304 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32305 * center region containing two nested regions (a top one for a list view and one for item preview below),
32306 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32307 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32308 * expedites the setup of the overall layout and regions for this common application style.
32311 var reader = new Roo.ReaderLayout();
32312 var CP = Roo.ContentPanel; // shortcut for adding
32314 reader.beginUpdate();
32315 reader.add("north", new CP("north", "North"));
32316 reader.add("west", new CP("west", {title: "West"}));
32317 reader.add("east", new CP("east", {title: "East"}));
32319 reader.regions.listView.add(new CP("listView", "List"));
32320 reader.regions.preview.add(new CP("preview", "Preview"));
32321 reader.endUpdate();
32324 * Create a new ReaderLayout
32325 * @param {Object} config Configuration options
32326 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32327 * document.body if omitted)
32329 Roo.ReaderLayout = function(config, renderTo){
32330 var c = config || {size:{}};
32331 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32332 north: c.north !== false ? Roo.apply({
32336 }, c.north) : false,
32337 west: c.west !== false ? Roo.apply({
32345 margins:{left:5,right:0,bottom:5,top:5},
32346 cmargins:{left:5,right:5,bottom:5,top:5}
32347 }, c.west) : false,
32348 east: c.east !== false ? Roo.apply({
32356 margins:{left:0,right:5,bottom:5,top:5},
32357 cmargins:{left:5,right:5,bottom:5,top:5}
32358 }, c.east) : false,
32359 center: Roo.apply({
32360 tabPosition: 'top',
32364 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32368 this.el.addClass('x-reader');
32370 this.beginUpdate();
32372 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32373 south: c.preview !== false ? Roo.apply({
32380 cmargins:{top:5,left:0, right:0, bottom:0}
32381 }, c.preview) : false,
32382 center: Roo.apply({
32388 this.add('center', new Roo.NestedLayoutPanel(inner,
32389 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32393 this.regions.preview = inner.getRegion('south');
32394 this.regions.listView = inner.getRegion('center');
32397 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32399 * Ext JS Library 1.1.1
32400 * Copyright(c) 2006-2007, Ext JS, LLC.
32402 * Originally Released Under LGPL - original licence link has changed is not relivant.
32405 * <script type="text/javascript">
32409 * @class Roo.grid.Grid
32410 * @extends Roo.util.Observable
32411 * This class represents the primary interface of a component based grid control.
32412 * <br><br>Usage:<pre><code>
32413 var grid = new Roo.grid.Grid("my-container-id", {
32416 selModel: mySelectionModel,
32417 autoSizeColumns: true,
32418 monitorWindowResize: false,
32419 trackMouseOver: true
32424 * <b>Common Problems:</b><br/>
32425 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32426 * element will correct this<br/>
32427 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32428 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32429 * are unpredictable.<br/>
32430 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32431 * grid to calculate dimensions/offsets.<br/>
32433 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32434 * The container MUST have some type of size defined for the grid to fill. The container will be
32435 * automatically set to position relative if it isn't already.
32436 * @param {Object} config A config object that sets properties on this grid.
32438 Roo.grid.Grid = function(container, config){
32439 // initialize the container
32440 this.container = Roo.get(container);
32441 this.container.update("");
32442 this.container.setStyle("overflow", "hidden");
32443 this.container.addClass('x-grid-container');
32445 this.id = this.container.id;
32447 Roo.apply(this, config);
32448 // check and correct shorthanded configs
32450 this.dataSource = this.ds;
32454 this.colModel = this.cm;
32458 this.selModel = this.sm;
32462 if (this.selModel) {
32463 this.selModel = Roo.factory(this.selModel, Roo.grid);
32464 this.sm = this.selModel;
32465 this.sm.xmodule = this.xmodule || false;
32467 if (typeof(this.colModel.config) == 'undefined') {
32468 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32469 this.cm = this.colModel;
32470 this.cm.xmodule = this.xmodule || false;
32472 if (this.dataSource) {
32473 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32474 this.ds = this.dataSource;
32475 this.ds.xmodule = this.xmodule || false;
32482 this.container.setWidth(this.width);
32486 this.container.setHeight(this.height);
32493 * The raw click event for the entire grid.
32494 * @param {Roo.EventObject} e
32499 * The raw dblclick event for the entire grid.
32500 * @param {Roo.EventObject} e
32504 * @event contextmenu
32505 * The raw contextmenu event for the entire grid.
32506 * @param {Roo.EventObject} e
32508 "contextmenu" : true,
32511 * The raw mousedown event for the entire grid.
32512 * @param {Roo.EventObject} e
32514 "mousedown" : true,
32517 * The raw mouseup event for the entire grid.
32518 * @param {Roo.EventObject} e
32523 * The raw mouseover event for the entire grid.
32524 * @param {Roo.EventObject} e
32526 "mouseover" : true,
32529 * The raw mouseout event for the entire grid.
32530 * @param {Roo.EventObject} e
32535 * The raw keypress event for the entire grid.
32536 * @param {Roo.EventObject} e
32541 * The raw keydown event for the entire grid.
32542 * @param {Roo.EventObject} e
32550 * Fires when a cell is clicked
32551 * @param {Grid} this
32552 * @param {Number} rowIndex
32553 * @param {Number} columnIndex
32554 * @param {Roo.EventObject} e
32556 "cellclick" : true,
32558 * @event celldblclick
32559 * Fires when a cell is double clicked
32560 * @param {Grid} this
32561 * @param {Number} rowIndex
32562 * @param {Number} columnIndex
32563 * @param {Roo.EventObject} e
32565 "celldblclick" : true,
32568 * Fires when a row is clicked
32569 * @param {Grid} this
32570 * @param {Number} rowIndex
32571 * @param {Roo.EventObject} e
32575 * @event rowdblclick
32576 * Fires when a row is double clicked
32577 * @param {Grid} this
32578 * @param {Number} rowIndex
32579 * @param {Roo.EventObject} e
32581 "rowdblclick" : true,
32583 * @event headerclick
32584 * Fires when a header is clicked
32585 * @param {Grid} this
32586 * @param {Number} columnIndex
32587 * @param {Roo.EventObject} e
32589 "headerclick" : true,
32591 * @event headerdblclick
32592 * Fires when a header cell is double clicked
32593 * @param {Grid} this
32594 * @param {Number} columnIndex
32595 * @param {Roo.EventObject} e
32597 "headerdblclick" : true,
32599 * @event rowcontextmenu
32600 * Fires when a row is right clicked
32601 * @param {Grid} this
32602 * @param {Number} rowIndex
32603 * @param {Roo.EventObject} e
32605 "rowcontextmenu" : true,
32607 * @event cellcontextmenu
32608 * Fires when a cell is right clicked
32609 * @param {Grid} this
32610 * @param {Number} rowIndex
32611 * @param {Number} cellIndex
32612 * @param {Roo.EventObject} e
32614 "cellcontextmenu" : true,
32616 * @event headercontextmenu
32617 * Fires when a header is right clicked
32618 * @param {Grid} this
32619 * @param {Number} columnIndex
32620 * @param {Roo.EventObject} e
32622 "headercontextmenu" : true,
32624 * @event bodyscroll
32625 * Fires when the body element is scrolled
32626 * @param {Number} scrollLeft
32627 * @param {Number} scrollTop
32629 "bodyscroll" : true,
32631 * @event columnresize
32632 * Fires when the user resizes a column
32633 * @param {Number} columnIndex
32634 * @param {Number} newSize
32636 "columnresize" : true,
32638 * @event columnmove
32639 * Fires when the user moves a column
32640 * @param {Number} oldIndex
32641 * @param {Number} newIndex
32643 "columnmove" : true,
32646 * Fires when row(s) start being dragged
32647 * @param {Grid} this
32648 * @param {Roo.GridDD} dd The drag drop object
32649 * @param {event} e The raw browser event
32651 "startdrag" : true,
32654 * Fires when a drag operation is complete
32655 * @param {Grid} this
32656 * @param {Roo.GridDD} dd The drag drop object
32657 * @param {event} e The raw browser event
32662 * Fires when dragged row(s) are dropped on a valid DD target
32663 * @param {Grid} this
32664 * @param {Roo.GridDD} dd The drag drop object
32665 * @param {String} targetId The target drag drop object
32666 * @param {event} e The raw browser event
32671 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32672 * @param {Grid} this
32673 * @param {Roo.GridDD} dd The drag drop object
32674 * @param {String} targetId The target drag drop object
32675 * @param {event} e The raw browser event
32680 * Fires when the dragged row(s) first cross another DD target while being dragged
32681 * @param {Grid} this
32682 * @param {Roo.GridDD} dd The drag drop object
32683 * @param {String} targetId The target drag drop object
32684 * @param {event} e The raw browser event
32686 "dragenter" : true,
32689 * Fires when the dragged row(s) leave another DD target while being dragged
32690 * @param {Grid} this
32691 * @param {Roo.GridDD} dd The drag drop object
32692 * @param {String} targetId The target drag drop object
32693 * @param {event} e The raw browser event
32698 * Fires when a row is rendered, so you can change add a style to it.
32699 * @param {GridView} gridview The grid view
32700 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32706 * Fires when the grid is rendered
32707 * @param {Grid} grid
32712 Roo.grid.Grid.superclass.constructor.call(this);
32714 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32717 * @cfg {String} ddGroup - drag drop group.
32721 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32723 minColumnWidth : 25,
32726 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32727 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32728 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32730 autoSizeColumns : false,
32733 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32735 autoSizeHeaders : true,
32738 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32740 monitorWindowResize : true,
32743 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32744 * rows measured to get a columns size. Default is 0 (all rows).
32746 maxRowsToMeasure : 0,
32749 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32751 trackMouseOver : true,
32754 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32758 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32760 enableDragDrop : false,
32763 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32765 enableColumnMove : true,
32768 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32770 enableColumnHide : true,
32773 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32775 enableRowHeightSync : false,
32778 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32783 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32785 autoHeight : false,
32788 * @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.
32790 autoExpandColumn : false,
32793 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32796 autoExpandMin : 50,
32799 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32801 autoExpandMax : 1000,
32804 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32809 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32813 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32823 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32824 * of a fixed width. Default is false.
32827 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32830 * Called once after all setup has been completed and the grid is ready to be rendered.
32831 * @return {Roo.grid.Grid} this
32833 render : function()
32835 var c = this.container;
32836 // try to detect autoHeight/width mode
32837 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32838 this.autoHeight = true;
32840 var view = this.getView();
32843 c.on("click", this.onClick, this);
32844 c.on("dblclick", this.onDblClick, this);
32845 c.on("contextmenu", this.onContextMenu, this);
32846 c.on("keydown", this.onKeyDown, this);
32848 c.on("touchstart", this.onTouchStart, this);
32851 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32853 this.getSelectionModel().init(this);
32858 this.loadMask = new Roo.LoadMask(this.container,
32859 Roo.apply({store:this.dataSource}, this.loadMask));
32863 if (this.toolbar && this.toolbar.xtype) {
32864 this.toolbar.container = this.getView().getHeaderPanel(true);
32865 this.toolbar = new Roo.Toolbar(this.toolbar);
32867 if (this.footer && this.footer.xtype) {
32868 this.footer.dataSource = this.getDataSource();
32869 this.footer.container = this.getView().getFooterPanel(true);
32870 this.footer = Roo.factory(this.footer, Roo);
32872 if (this.dropTarget && this.dropTarget.xtype) {
32873 delete this.dropTarget.xtype;
32874 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32878 this.rendered = true;
32879 this.fireEvent('render', this);
32884 * Reconfigures the grid to use a different Store and Column Model.
32885 * The View will be bound to the new objects and refreshed.
32886 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32887 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32889 reconfigure : function(dataSource, colModel){
32891 this.loadMask.destroy();
32892 this.loadMask = new Roo.LoadMask(this.container,
32893 Roo.apply({store:dataSource}, this.loadMask));
32895 this.view.bind(dataSource, colModel);
32896 this.dataSource = dataSource;
32897 this.colModel = colModel;
32898 this.view.refresh(true);
32902 onKeyDown : function(e){
32903 this.fireEvent("keydown", e);
32907 * Destroy this grid.
32908 * @param {Boolean} removeEl True to remove the element
32910 destroy : function(removeEl, keepListeners){
32912 this.loadMask.destroy();
32914 var c = this.container;
32915 c.removeAllListeners();
32916 this.view.destroy();
32917 this.colModel.purgeListeners();
32918 if(!keepListeners){
32919 this.purgeListeners();
32922 if(removeEl === true){
32928 processEvent : function(name, e){
32929 // does this fire select???
32930 //Roo.log('grid:processEvent ' + name);
32932 if (name != 'touchstart' ) {
32933 this.fireEvent(name, e);
32936 var t = e.getTarget();
32938 var header = v.findHeaderIndex(t);
32939 if(header !== false){
32940 var ename = name == 'touchstart' ? 'click' : name;
32942 this.fireEvent("header" + ename, this, header, e);
32944 var row = v.findRowIndex(t);
32945 var cell = v.findCellIndex(t);
32946 if (name == 'touchstart') {
32947 // first touch is always a click.
32948 // hopefull this happens after selection is updated.?
32951 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32952 var cs = this.selModel.getSelectedCell();
32953 if (row == cs[0] && cell == cs[1]){
32957 if (typeof(this.selModel.getSelections) != 'undefined') {
32958 var cs = this.selModel.getSelections();
32959 var ds = this.dataSource;
32960 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32971 this.fireEvent("row" + name, this, row, e);
32972 if(cell !== false){
32973 this.fireEvent("cell" + name, this, row, cell, e);
32980 onClick : function(e){
32981 this.processEvent("click", e);
32984 onTouchStart : function(e){
32985 this.processEvent("touchstart", e);
32989 onContextMenu : function(e, t){
32990 this.processEvent("contextmenu", e);
32994 onDblClick : function(e){
32995 this.processEvent("dblclick", e);
32999 walkCells : function(row, col, step, fn, scope){
33000 var cm = this.colModel, clen = cm.getColumnCount();
33001 var ds = this.dataSource, rlen = ds.getCount(), first = true;
33013 if(fn.call(scope || this, row, col, cm) === true){
33031 if(fn.call(scope || this, row, col, cm) === true){
33043 getSelections : function(){
33044 return this.selModel.getSelections();
33048 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33049 * but if manual update is required this method will initiate it.
33051 autoSize : function(){
33053 this.view.layout();
33054 if(this.view.adjustForScroll){
33055 this.view.adjustForScroll();
33061 * Returns the grid's underlying element.
33062 * @return {Element} The element
33064 getGridEl : function(){
33065 return this.container;
33068 // private for compatibility, overridden by editor grid
33069 stopEditing : function(){},
33072 * Returns the grid's SelectionModel.
33073 * @return {SelectionModel}
33075 getSelectionModel : function(){
33076 if(!this.selModel){
33077 this.selModel = new Roo.grid.RowSelectionModel();
33079 return this.selModel;
33083 * Returns the grid's DataSource.
33084 * @return {DataSource}
33086 getDataSource : function(){
33087 return this.dataSource;
33091 * Returns the grid's ColumnModel.
33092 * @return {ColumnModel}
33094 getColumnModel : function(){
33095 return this.colModel;
33099 * Returns the grid's GridView object.
33100 * @return {GridView}
33102 getView : function(){
33104 this.view = new Roo.grid.GridView(this.viewConfig);
33109 * Called to get grid's drag proxy text, by default returns this.ddText.
33112 getDragDropText : function(){
33113 var count = this.selModel.getCount();
33114 return String.format(this.ddText, count, count == 1 ? '' : 's');
33118 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33119 * %0 is replaced with the number of selected rows.
33122 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33124 * Ext JS Library 1.1.1
33125 * Copyright(c) 2006-2007, Ext JS, LLC.
33127 * Originally Released Under LGPL - original licence link has changed is not relivant.
33130 * <script type="text/javascript">
33133 Roo.grid.AbstractGridView = function(){
33137 "beforerowremoved" : true,
33138 "beforerowsinserted" : true,
33139 "beforerefresh" : true,
33140 "rowremoved" : true,
33141 "rowsinserted" : true,
33142 "rowupdated" : true,
33145 Roo.grid.AbstractGridView.superclass.constructor.call(this);
33148 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33149 rowClass : "x-grid-row",
33150 cellClass : "x-grid-cell",
33151 tdClass : "x-grid-td",
33152 hdClass : "x-grid-hd",
33153 splitClass : "x-grid-hd-split",
33155 init: function(grid){
33157 var cid = this.grid.getGridEl().id;
33158 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33159 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33160 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33161 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33164 getColumnRenderers : function(){
33165 var renderers = [];
33166 var cm = this.grid.colModel;
33167 var colCount = cm.getColumnCount();
33168 for(var i = 0; i < colCount; i++){
33169 renderers[i] = cm.getRenderer(i);
33174 getColumnIds : function(){
33176 var cm = this.grid.colModel;
33177 var colCount = cm.getColumnCount();
33178 for(var i = 0; i < colCount; i++){
33179 ids[i] = cm.getColumnId(i);
33184 getDataIndexes : function(){
33185 if(!this.indexMap){
33186 this.indexMap = this.buildIndexMap();
33188 return this.indexMap.colToData;
33191 getColumnIndexByDataIndex : function(dataIndex){
33192 if(!this.indexMap){
33193 this.indexMap = this.buildIndexMap();
33195 return this.indexMap.dataToCol[dataIndex];
33199 * Set a css style for a column dynamically.
33200 * @param {Number} colIndex The index of the column
33201 * @param {String} name The css property name
33202 * @param {String} value The css value
33204 setCSSStyle : function(colIndex, name, value){
33205 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33206 Roo.util.CSS.updateRule(selector, name, value);
33209 generateRules : function(cm){
33210 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33211 Roo.util.CSS.removeStyleSheet(rulesId);
33212 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33213 var cid = cm.getColumnId(i);
33214 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33215 this.tdSelector, cid, " {\n}\n",
33216 this.hdSelector, cid, " {\n}\n",
33217 this.splitSelector, cid, " {\n}\n");
33219 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33223 * Ext JS Library 1.1.1
33224 * Copyright(c) 2006-2007, Ext JS, LLC.
33226 * Originally Released Under LGPL - original licence link has changed is not relivant.
33229 * <script type="text/javascript">
33233 // This is a support class used internally by the Grid components
33234 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33236 this.view = grid.getView();
33237 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33238 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33240 this.setHandleElId(Roo.id(hd));
33241 this.setOuterHandleElId(Roo.id(hd2));
33243 this.scroll = false;
33245 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33247 getDragData : function(e){
33248 var t = Roo.lib.Event.getTarget(e);
33249 var h = this.view.findHeaderCell(t);
33251 return {ddel: h.firstChild, header:h};
33256 onInitDrag : function(e){
33257 this.view.headersDisabled = true;
33258 var clone = this.dragData.ddel.cloneNode(true);
33259 clone.id = Roo.id();
33260 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33261 this.proxy.update(clone);
33265 afterValidDrop : function(){
33267 setTimeout(function(){
33268 v.headersDisabled = false;
33272 afterInvalidDrop : function(){
33274 setTimeout(function(){
33275 v.headersDisabled = false;
33281 * Ext JS Library 1.1.1
33282 * Copyright(c) 2006-2007, Ext JS, LLC.
33284 * Originally Released Under LGPL - original licence link has changed is not relivant.
33287 * <script type="text/javascript">
33290 // This is a support class used internally by the Grid components
33291 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33293 this.view = grid.getView();
33294 // split the proxies so they don't interfere with mouse events
33295 this.proxyTop = Roo.DomHelper.append(document.body, {
33296 cls:"col-move-top", html:" "
33298 this.proxyBottom = Roo.DomHelper.append(document.body, {
33299 cls:"col-move-bottom", html:" "
33301 this.proxyTop.hide = this.proxyBottom.hide = function(){
33302 this.setLeftTop(-100,-100);
33303 this.setStyle("visibility", "hidden");
33305 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33306 // temporarily disabled
33307 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33308 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33310 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33311 proxyOffsets : [-4, -9],
33312 fly: Roo.Element.fly,
33314 getTargetFromEvent : function(e){
33315 var t = Roo.lib.Event.getTarget(e);
33316 var cindex = this.view.findCellIndex(t);
33317 if(cindex !== false){
33318 return this.view.getHeaderCell(cindex);
33323 nextVisible : function(h){
33324 var v = this.view, cm = this.grid.colModel;
33327 if(!cm.isHidden(v.getCellIndex(h))){
33335 prevVisible : function(h){
33336 var v = this.view, cm = this.grid.colModel;
33339 if(!cm.isHidden(v.getCellIndex(h))){
33347 positionIndicator : function(h, n, e){
33348 var x = Roo.lib.Event.getPageX(e);
33349 var r = Roo.lib.Dom.getRegion(n.firstChild);
33350 var px, pt, py = r.top + this.proxyOffsets[1];
33351 if((r.right - x) <= (r.right-r.left)/2){
33352 px = r.right+this.view.borderWidth;
33358 var oldIndex = this.view.getCellIndex(h);
33359 var newIndex = this.view.getCellIndex(n);
33361 if(this.grid.colModel.isFixed(newIndex)){
33365 var locked = this.grid.colModel.isLocked(newIndex);
33370 if(oldIndex < newIndex){
33373 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33376 px += this.proxyOffsets[0];
33377 this.proxyTop.setLeftTop(px, py);
33378 this.proxyTop.show();
33379 if(!this.bottomOffset){
33380 this.bottomOffset = this.view.mainHd.getHeight();
33382 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33383 this.proxyBottom.show();
33387 onNodeEnter : function(n, dd, e, data){
33388 if(data.header != n){
33389 this.positionIndicator(data.header, n, e);
33393 onNodeOver : function(n, dd, e, data){
33394 var result = false;
33395 if(data.header != n){
33396 result = this.positionIndicator(data.header, n, e);
33399 this.proxyTop.hide();
33400 this.proxyBottom.hide();
33402 return result ? this.dropAllowed : this.dropNotAllowed;
33405 onNodeOut : function(n, dd, e, data){
33406 this.proxyTop.hide();
33407 this.proxyBottom.hide();
33410 onNodeDrop : function(n, dd, e, data){
33411 var h = data.header;
33413 var cm = this.grid.colModel;
33414 var x = Roo.lib.Event.getPageX(e);
33415 var r = Roo.lib.Dom.getRegion(n.firstChild);
33416 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33417 var oldIndex = this.view.getCellIndex(h);
33418 var newIndex = this.view.getCellIndex(n);
33419 var locked = cm.isLocked(newIndex);
33423 if(oldIndex < newIndex){
33426 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33429 cm.setLocked(oldIndex, locked, true);
33430 cm.moveColumn(oldIndex, newIndex);
33431 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33439 * Ext JS Library 1.1.1
33440 * Copyright(c) 2006-2007, Ext JS, LLC.
33442 * Originally Released Under LGPL - original licence link has changed is not relivant.
33445 * <script type="text/javascript">
33449 * @class Roo.grid.GridView
33450 * @extends Roo.util.Observable
33453 * @param {Object} config
33455 Roo.grid.GridView = function(config){
33456 Roo.grid.GridView.superclass.constructor.call(this);
33459 Roo.apply(this, config);
33462 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33464 unselectable : 'unselectable="on"',
33465 unselectableCls : 'x-unselectable',
33468 rowClass : "x-grid-row",
33470 cellClass : "x-grid-col",
33472 tdClass : "x-grid-td",
33474 hdClass : "x-grid-hd",
33476 splitClass : "x-grid-split",
33478 sortClasses : ["sort-asc", "sort-desc"],
33480 enableMoveAnim : false,
33484 dh : Roo.DomHelper,
33486 fly : Roo.Element.fly,
33488 css : Roo.util.CSS,
33494 scrollIncrement : 22,
33496 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33498 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33500 bind : function(ds, cm){
33502 this.ds.un("load", this.onLoad, this);
33503 this.ds.un("datachanged", this.onDataChange, this);
33504 this.ds.un("add", this.onAdd, this);
33505 this.ds.un("remove", this.onRemove, this);
33506 this.ds.un("update", this.onUpdate, this);
33507 this.ds.un("clear", this.onClear, this);
33510 ds.on("load", this.onLoad, this);
33511 ds.on("datachanged", this.onDataChange, this);
33512 ds.on("add", this.onAdd, this);
33513 ds.on("remove", this.onRemove, this);
33514 ds.on("update", this.onUpdate, this);
33515 ds.on("clear", this.onClear, this);
33520 this.cm.un("widthchange", this.onColWidthChange, this);
33521 this.cm.un("headerchange", this.onHeaderChange, this);
33522 this.cm.un("hiddenchange", this.onHiddenChange, this);
33523 this.cm.un("columnmoved", this.onColumnMove, this);
33524 this.cm.un("columnlockchange", this.onColumnLock, this);
33527 this.generateRules(cm);
33528 cm.on("widthchange", this.onColWidthChange, this);
33529 cm.on("headerchange", this.onHeaderChange, this);
33530 cm.on("hiddenchange", this.onHiddenChange, this);
33531 cm.on("columnmoved", this.onColumnMove, this);
33532 cm.on("columnlockchange", this.onColumnLock, this);
33537 init: function(grid){
33538 Roo.grid.GridView.superclass.init.call(this, grid);
33540 this.bind(grid.dataSource, grid.colModel);
33542 grid.on("headerclick", this.handleHeaderClick, this);
33544 if(grid.trackMouseOver){
33545 grid.on("mouseover", this.onRowOver, this);
33546 grid.on("mouseout", this.onRowOut, this);
33548 grid.cancelTextSelection = function(){};
33549 this.gridId = grid.id;
33551 var tpls = this.templates || {};
33554 tpls.master = new Roo.Template(
33555 '<div class="x-grid" hidefocus="true">',
33556 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33557 '<div class="x-grid-topbar"></div>',
33558 '<div class="x-grid-scroller"><div></div></div>',
33559 '<div class="x-grid-locked">',
33560 '<div class="x-grid-header">{lockedHeader}</div>',
33561 '<div class="x-grid-body">{lockedBody}</div>',
33563 '<div class="x-grid-viewport">',
33564 '<div class="x-grid-header">{header}</div>',
33565 '<div class="x-grid-body">{body}</div>',
33567 '<div class="x-grid-bottombar"></div>',
33569 '<div class="x-grid-resize-proxy"> </div>',
33572 tpls.master.disableformats = true;
33576 tpls.header = new Roo.Template(
33577 '<table border="0" cellspacing="0" cellpadding="0">',
33578 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33581 tpls.header.disableformats = true;
33583 tpls.header.compile();
33586 tpls.hcell = new Roo.Template(
33587 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33588 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33591 tpls.hcell.disableFormats = true;
33593 tpls.hcell.compile();
33596 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33597 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33598 tpls.hsplit.disableFormats = true;
33600 tpls.hsplit.compile();
33603 tpls.body = new Roo.Template(
33604 '<table border="0" cellspacing="0" cellpadding="0">',
33605 "<tbody>{rows}</tbody>",
33608 tpls.body.disableFormats = true;
33610 tpls.body.compile();
33613 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33614 tpls.row.disableFormats = true;
33616 tpls.row.compile();
33619 tpls.cell = new Roo.Template(
33620 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33621 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33622 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33625 tpls.cell.disableFormats = true;
33627 tpls.cell.compile();
33629 this.templates = tpls;
33632 // remap these for backwards compat
33633 onColWidthChange : function(){
33634 this.updateColumns.apply(this, arguments);
33636 onHeaderChange : function(){
33637 this.updateHeaders.apply(this, arguments);
33639 onHiddenChange : function(){
33640 this.handleHiddenChange.apply(this, arguments);
33642 onColumnMove : function(){
33643 this.handleColumnMove.apply(this, arguments);
33645 onColumnLock : function(){
33646 this.handleLockChange.apply(this, arguments);
33649 onDataChange : function(){
33651 this.updateHeaderSortState();
33654 onClear : function(){
33658 onUpdate : function(ds, record){
33659 this.refreshRow(record);
33662 refreshRow : function(record){
33663 var ds = this.ds, index;
33664 if(typeof record == 'number'){
33666 record = ds.getAt(index);
33668 index = ds.indexOf(record);
33670 this.insertRows(ds, index, index, true);
33671 this.onRemove(ds, record, index+1, true);
33672 this.syncRowHeights(index, index);
33674 this.fireEvent("rowupdated", this, index, record);
33677 onAdd : function(ds, records, index){
33678 this.insertRows(ds, index, index + (records.length-1));
33681 onRemove : function(ds, record, index, isUpdate){
33682 if(isUpdate !== true){
33683 this.fireEvent("beforerowremoved", this, index, record);
33685 var bt = this.getBodyTable(), lt = this.getLockedTable();
33686 if(bt.rows[index]){
33687 bt.firstChild.removeChild(bt.rows[index]);
33689 if(lt.rows[index]){
33690 lt.firstChild.removeChild(lt.rows[index]);
33692 if(isUpdate !== true){
33693 this.stripeRows(index);
33694 this.syncRowHeights(index, index);
33696 this.fireEvent("rowremoved", this, index, record);
33700 onLoad : function(){
33701 this.scrollToTop();
33705 * Scrolls the grid to the top
33707 scrollToTop : function(){
33709 this.scroller.dom.scrollTop = 0;
33715 * Gets a panel in the header of the grid that can be used for toolbars etc.
33716 * After modifying the contents of this panel a call to grid.autoSize() may be
33717 * required to register any changes in size.
33718 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33719 * @return Roo.Element
33721 getHeaderPanel : function(doShow){
33723 this.headerPanel.show();
33725 return this.headerPanel;
33729 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33730 * After modifying the contents of this panel a call to grid.autoSize() may be
33731 * required to register any changes in size.
33732 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33733 * @return Roo.Element
33735 getFooterPanel : function(doShow){
33737 this.footerPanel.show();
33739 return this.footerPanel;
33742 initElements : function(){
33743 var E = Roo.Element;
33744 var el = this.grid.getGridEl().dom.firstChild;
33745 var cs = el.childNodes;
33747 this.el = new E(el);
33749 this.focusEl = new E(el.firstChild);
33750 this.focusEl.swallowEvent("click", true);
33752 this.headerPanel = new E(cs[1]);
33753 this.headerPanel.enableDisplayMode("block");
33755 this.scroller = new E(cs[2]);
33756 this.scrollSizer = new E(this.scroller.dom.firstChild);
33758 this.lockedWrap = new E(cs[3]);
33759 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33760 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33762 this.mainWrap = new E(cs[4]);
33763 this.mainHd = new E(this.mainWrap.dom.firstChild);
33764 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33766 this.footerPanel = new E(cs[5]);
33767 this.footerPanel.enableDisplayMode("block");
33769 this.resizeProxy = new E(cs[6]);
33771 this.headerSelector = String.format(
33772 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33773 this.lockedHd.id, this.mainHd.id
33776 this.splitterSelector = String.format(
33777 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33778 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33781 idToCssName : function(s)
33783 return s.replace(/[^a-z0-9]+/ig, '-');
33786 getHeaderCell : function(index){
33787 return Roo.DomQuery.select(this.headerSelector)[index];
33790 getHeaderCellMeasure : function(index){
33791 return this.getHeaderCell(index).firstChild;
33794 getHeaderCellText : function(index){
33795 return this.getHeaderCell(index).firstChild.firstChild;
33798 getLockedTable : function(){
33799 return this.lockedBody.dom.firstChild;
33802 getBodyTable : function(){
33803 return this.mainBody.dom.firstChild;
33806 getLockedRow : function(index){
33807 return this.getLockedTable().rows[index];
33810 getRow : function(index){
33811 return this.getBodyTable().rows[index];
33814 getRowComposite : function(index){
33816 this.rowEl = new Roo.CompositeElementLite();
33818 var els = [], lrow, mrow;
33819 if(lrow = this.getLockedRow(index)){
33822 if(mrow = this.getRow(index)){
33825 this.rowEl.elements = els;
33829 * Gets the 'td' of the cell
33831 * @param {Integer} rowIndex row to select
33832 * @param {Integer} colIndex column to select
33836 getCell : function(rowIndex, colIndex){
33837 var locked = this.cm.getLockedCount();
33839 if(colIndex < locked){
33840 source = this.lockedBody.dom.firstChild;
33842 source = this.mainBody.dom.firstChild;
33843 colIndex -= locked;
33845 return source.rows[rowIndex].childNodes[colIndex];
33848 getCellText : function(rowIndex, colIndex){
33849 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33852 getCellBox : function(cell){
33853 var b = this.fly(cell).getBox();
33854 if(Roo.isOpera){ // opera fails to report the Y
33855 b.y = cell.offsetTop + this.mainBody.getY();
33860 getCellIndex : function(cell){
33861 var id = String(cell.className).match(this.cellRE);
33863 return parseInt(id[1], 10);
33868 findHeaderIndex : function(n){
33869 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33870 return r ? this.getCellIndex(r) : false;
33873 findHeaderCell : function(n){
33874 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33875 return r ? r : false;
33878 findRowIndex : function(n){
33882 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33883 return r ? r.rowIndex : false;
33886 findCellIndex : function(node){
33887 var stop = this.el.dom;
33888 while(node && node != stop){
33889 if(this.findRE.test(node.className)){
33890 return this.getCellIndex(node);
33892 node = node.parentNode;
33897 getColumnId : function(index){
33898 return this.cm.getColumnId(index);
33901 getSplitters : function()
33903 if(this.splitterSelector){
33904 return Roo.DomQuery.select(this.splitterSelector);
33910 getSplitter : function(index){
33911 return this.getSplitters()[index];
33914 onRowOver : function(e, t){
33916 if((row = this.findRowIndex(t)) !== false){
33917 this.getRowComposite(row).addClass("x-grid-row-over");
33921 onRowOut : function(e, t){
33923 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33924 this.getRowComposite(row).removeClass("x-grid-row-over");
33928 renderHeaders : function(){
33930 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33931 var cb = [], lb = [], sb = [], lsb = [], p = {};
33932 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33933 p.cellId = "x-grid-hd-0-" + i;
33934 p.splitId = "x-grid-csplit-0-" + i;
33935 p.id = cm.getColumnId(i);
33936 p.value = cm.getColumnHeader(i) || "";
33937 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33938 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33939 if(!cm.isLocked(i)){
33940 cb[cb.length] = ct.apply(p);
33941 sb[sb.length] = st.apply(p);
33943 lb[lb.length] = ct.apply(p);
33944 lsb[lsb.length] = st.apply(p);
33947 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33948 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33951 updateHeaders : function(){
33952 var html = this.renderHeaders();
33953 this.lockedHd.update(html[0]);
33954 this.mainHd.update(html[1]);
33958 * Focuses the specified row.
33959 * @param {Number} row The row index
33961 focusRow : function(row)
33963 //Roo.log('GridView.focusRow');
33964 var x = this.scroller.dom.scrollLeft;
33965 this.focusCell(row, 0, false);
33966 this.scroller.dom.scrollLeft = x;
33970 * Focuses the specified cell.
33971 * @param {Number} row The row index
33972 * @param {Number} col The column index
33973 * @param {Boolean} hscroll false to disable horizontal scrolling
33975 focusCell : function(row, col, hscroll)
33977 //Roo.log('GridView.focusCell');
33978 var el = this.ensureVisible(row, col, hscroll);
33979 this.focusEl.alignTo(el, "tl-tl");
33981 this.focusEl.focus();
33983 this.focusEl.focus.defer(1, this.focusEl);
33988 * Scrolls the specified cell into view
33989 * @param {Number} row The row index
33990 * @param {Number} col The column index
33991 * @param {Boolean} hscroll false to disable horizontal scrolling
33993 ensureVisible : function(row, col, hscroll)
33995 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33996 //return null; //disable for testing.
33997 if(typeof row != "number"){
33998 row = row.rowIndex;
34000 if(row < 0 && row >= this.ds.getCount()){
34003 col = (col !== undefined ? col : 0);
34004 var cm = this.grid.colModel;
34005 while(cm.isHidden(col)){
34009 var el = this.getCell(row, col);
34013 var c = this.scroller.dom;
34015 var ctop = parseInt(el.offsetTop, 10);
34016 var cleft = parseInt(el.offsetLeft, 10);
34017 var cbot = ctop + el.offsetHeight;
34018 var cright = cleft + el.offsetWidth;
34020 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34021 var stop = parseInt(c.scrollTop, 10);
34022 var sleft = parseInt(c.scrollLeft, 10);
34023 var sbot = stop + ch;
34024 var sright = sleft + c.clientWidth;
34026 Roo.log('GridView.ensureVisible:' +
34028 ' c.clientHeight:' + c.clientHeight +
34029 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34037 c.scrollTop = ctop;
34038 //Roo.log("set scrolltop to ctop DISABLE?");
34039 }else if(cbot > sbot){
34040 //Roo.log("set scrolltop to cbot-ch");
34041 c.scrollTop = cbot-ch;
34044 if(hscroll !== false){
34046 c.scrollLeft = cleft;
34047 }else if(cright > sright){
34048 c.scrollLeft = cright-c.clientWidth;
34055 updateColumns : function(){
34056 this.grid.stopEditing();
34057 var cm = this.grid.colModel, colIds = this.getColumnIds();
34058 //var totalWidth = cm.getTotalWidth();
34060 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34061 //if(cm.isHidden(i)) continue;
34062 var w = cm.getColumnWidth(i);
34063 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34064 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34066 this.updateSplitters();
34069 generateRules : function(cm){
34070 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34071 Roo.util.CSS.removeStyleSheet(rulesId);
34072 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34073 var cid = cm.getColumnId(i);
34075 if(cm.config[i].align){
34076 align = 'text-align:'+cm.config[i].align+';';
34079 if(cm.isHidden(i)){
34080 hidden = 'display:none;';
34082 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34084 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34085 this.hdSelector, cid, " {\n", align, width, "}\n",
34086 this.tdSelector, cid, " {\n",hidden,"\n}\n",
34087 this.splitSelector, cid, " {\n", hidden , "\n}\n");
34089 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34092 updateSplitters : function(){
34093 var cm = this.cm, s = this.getSplitters();
34094 if(s){ // splitters not created yet
34095 var pos = 0, locked = true;
34096 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34097 if(cm.isHidden(i)) {
34100 var w = cm.getColumnWidth(i); // make sure it's a number
34101 if(!cm.isLocked(i) && locked){
34106 s[i].style.left = (pos-this.splitOffset) + "px";
34111 handleHiddenChange : function(colModel, colIndex, hidden){
34113 this.hideColumn(colIndex);
34115 this.unhideColumn(colIndex);
34119 hideColumn : function(colIndex){
34120 var cid = this.getColumnId(colIndex);
34121 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34122 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34124 this.updateHeaders();
34126 this.updateSplitters();
34130 unhideColumn : function(colIndex){
34131 var cid = this.getColumnId(colIndex);
34132 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34133 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34136 this.updateHeaders();
34138 this.updateSplitters();
34142 insertRows : function(dm, firstRow, lastRow, isUpdate){
34143 if(firstRow == 0 && lastRow == dm.getCount()-1){
34147 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34149 var s = this.getScrollState();
34150 var markup = this.renderRows(firstRow, lastRow);
34151 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34152 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34153 this.restoreScroll(s);
34155 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34156 this.syncRowHeights(firstRow, lastRow);
34157 this.stripeRows(firstRow);
34163 bufferRows : function(markup, target, index){
34164 var before = null, trows = target.rows, tbody = target.tBodies[0];
34165 if(index < trows.length){
34166 before = trows[index];
34168 var b = document.createElement("div");
34169 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34170 var rows = b.firstChild.rows;
34171 for(var i = 0, len = rows.length; i < len; i++){
34173 tbody.insertBefore(rows[0], before);
34175 tbody.appendChild(rows[0]);
34182 deleteRows : function(dm, firstRow, lastRow){
34183 if(dm.getRowCount()<1){
34184 this.fireEvent("beforerefresh", this);
34185 this.mainBody.update("");
34186 this.lockedBody.update("");
34187 this.fireEvent("refresh", this);
34189 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34190 var bt = this.getBodyTable();
34191 var tbody = bt.firstChild;
34192 var rows = bt.rows;
34193 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34194 tbody.removeChild(rows[firstRow]);
34196 this.stripeRows(firstRow);
34197 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34201 updateRows : function(dataSource, firstRow, lastRow){
34202 var s = this.getScrollState();
34204 this.restoreScroll(s);
34207 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34211 this.updateHeaderSortState();
34214 getScrollState : function(){
34216 var sb = this.scroller.dom;
34217 return {left: sb.scrollLeft, top: sb.scrollTop};
34220 stripeRows : function(startRow){
34221 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34224 startRow = startRow || 0;
34225 var rows = this.getBodyTable().rows;
34226 var lrows = this.getLockedTable().rows;
34227 var cls = ' x-grid-row-alt ';
34228 for(var i = startRow, len = rows.length; i < len; i++){
34229 var row = rows[i], lrow = lrows[i];
34230 var isAlt = ((i+1) % 2 == 0);
34231 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34232 if(isAlt == hasAlt){
34236 row.className += " x-grid-row-alt";
34238 row.className = row.className.replace("x-grid-row-alt", "");
34241 lrow.className = row.className;
34246 restoreScroll : function(state){
34247 //Roo.log('GridView.restoreScroll');
34248 var sb = this.scroller.dom;
34249 sb.scrollLeft = state.left;
34250 sb.scrollTop = state.top;
34254 syncScroll : function(){
34255 //Roo.log('GridView.syncScroll');
34256 var sb = this.scroller.dom;
34257 var sh = this.mainHd.dom;
34258 var bs = this.mainBody.dom;
34259 var lv = this.lockedBody.dom;
34260 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34261 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34264 handleScroll : function(e){
34266 var sb = this.scroller.dom;
34267 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34271 handleWheel : function(e){
34272 var d = e.getWheelDelta();
34273 this.scroller.dom.scrollTop -= d*22;
34274 // set this here to prevent jumpy scrolling on large tables
34275 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34279 renderRows : function(startRow, endRow){
34280 // pull in all the crap needed to render rows
34281 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34282 var colCount = cm.getColumnCount();
34284 if(ds.getCount() < 1){
34288 // build a map for all the columns
34290 for(var i = 0; i < colCount; i++){
34291 var name = cm.getDataIndex(i);
34293 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34294 renderer : cm.getRenderer(i),
34295 id : cm.getColumnId(i),
34296 locked : cm.isLocked(i),
34297 has_editor : cm.isCellEditable(i)
34301 startRow = startRow || 0;
34302 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34304 // records to render
34305 var rs = ds.getRange(startRow, endRow);
34307 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34310 // As much as I hate to duplicate code, this was branched because FireFox really hates
34311 // [].join("") on strings. The performance difference was substantial enough to
34312 // branch this function
34313 doRender : Roo.isGecko ?
34314 function(cs, rs, ds, startRow, colCount, stripe){
34315 var ts = this.templates, ct = ts.cell, rt = ts.row;
34317 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34319 var hasListener = this.grid.hasListener('rowclass');
34321 for(var j = 0, len = rs.length; j < len; j++){
34322 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34323 for(var i = 0; i < colCount; i++){
34325 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34327 p.css = p.attr = "";
34328 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34329 if(p.value == undefined || p.value === "") {
34330 p.value = " ";
34333 p.css += ' x-grid-editable-cell';
34335 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34336 p.css += ' x-grid-dirty-cell';
34338 var markup = ct.apply(p);
34346 if(stripe && ((rowIndex+1) % 2 == 0)){
34347 alt.push("x-grid-row-alt")
34350 alt.push( " x-grid-dirty-row");
34353 if(this.getRowClass){
34354 alt.push(this.getRowClass(r, rowIndex));
34360 rowIndex : rowIndex,
34363 this.grid.fireEvent('rowclass', this, rowcfg);
34364 alt.push(rowcfg.rowClass);
34366 rp.alt = alt.join(" ");
34367 lbuf+= rt.apply(rp);
34369 buf+= rt.apply(rp);
34371 return [lbuf, buf];
34373 function(cs, rs, ds, startRow, colCount, stripe){
34374 var ts = this.templates, ct = ts.cell, rt = ts.row;
34376 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34377 var hasListener = this.grid.hasListener('rowclass');
34380 for(var j = 0, len = rs.length; j < len; j++){
34381 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34382 for(var i = 0; i < colCount; i++){
34384 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34386 p.css = p.attr = "";
34387 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34388 if(p.value == undefined || p.value === "") {
34389 p.value = " ";
34393 p.css += ' x-grid-editable-cell';
34395 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34396 p.css += ' x-grid-dirty-cell'
34399 var markup = ct.apply(p);
34401 cb[cb.length] = markup;
34403 lcb[lcb.length] = markup;
34407 if(stripe && ((rowIndex+1) % 2 == 0)){
34408 alt.push( "x-grid-row-alt");
34411 alt.push(" x-grid-dirty-row");
34414 if(this.getRowClass){
34415 alt.push( this.getRowClass(r, rowIndex));
34421 rowIndex : rowIndex,
34424 this.grid.fireEvent('rowclass', this, rowcfg);
34425 alt.push(rowcfg.rowClass);
34428 rp.alt = alt.join(" ");
34429 rp.cells = lcb.join("");
34430 lbuf[lbuf.length] = rt.apply(rp);
34431 rp.cells = cb.join("");
34432 buf[buf.length] = rt.apply(rp);
34434 return [lbuf.join(""), buf.join("")];
34437 renderBody : function(){
34438 var markup = this.renderRows();
34439 var bt = this.templates.body;
34440 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34444 * Refreshes the grid
34445 * @param {Boolean} headersToo
34447 refresh : function(headersToo){
34448 this.fireEvent("beforerefresh", this);
34449 this.grid.stopEditing();
34450 var result = this.renderBody();
34451 this.lockedBody.update(result[0]);
34452 this.mainBody.update(result[1]);
34453 if(headersToo === true){
34454 this.updateHeaders();
34455 this.updateColumns();
34456 this.updateSplitters();
34457 this.updateHeaderSortState();
34459 this.syncRowHeights();
34461 this.fireEvent("refresh", this);
34464 handleColumnMove : function(cm, oldIndex, newIndex){
34465 this.indexMap = null;
34466 var s = this.getScrollState();
34467 this.refresh(true);
34468 this.restoreScroll(s);
34469 this.afterMove(newIndex);
34472 afterMove : function(colIndex){
34473 if(this.enableMoveAnim && Roo.enableFx){
34474 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34476 // if multisort - fix sortOrder, and reload..
34477 if (this.grid.dataSource.multiSort) {
34478 // the we can call sort again..
34479 var dm = this.grid.dataSource;
34480 var cm = this.grid.colModel;
34482 for(var i = 0; i < cm.config.length; i++ ) {
34484 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34485 continue; // dont' bother, it's not in sort list or being set.
34488 so.push(cm.config[i].dataIndex);
34491 dm.load(dm.lastOptions);
34498 updateCell : function(dm, rowIndex, dataIndex){
34499 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34500 if(typeof colIndex == "undefined"){ // not present in grid
34503 var cm = this.grid.colModel;
34504 var cell = this.getCell(rowIndex, colIndex);
34505 var cellText = this.getCellText(rowIndex, colIndex);
34508 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34509 id : cm.getColumnId(colIndex),
34510 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34512 var renderer = cm.getRenderer(colIndex);
34513 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34514 if(typeof val == "undefined" || val === "") {
34517 cellText.innerHTML = val;
34518 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34519 this.syncRowHeights(rowIndex, rowIndex);
34522 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34524 if(this.grid.autoSizeHeaders){
34525 var h = this.getHeaderCellMeasure(colIndex);
34526 maxWidth = Math.max(maxWidth, h.scrollWidth);
34529 if(this.cm.isLocked(colIndex)){
34530 tb = this.getLockedTable();
34533 tb = this.getBodyTable();
34534 index = colIndex - this.cm.getLockedCount();
34537 var rows = tb.rows;
34538 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34539 for(var i = 0; i < stopIndex; i++){
34540 var cell = rows[i].childNodes[index].firstChild;
34541 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34544 return maxWidth + /*margin for error in IE*/ 5;
34547 * Autofit a column to its content.
34548 * @param {Number} colIndex
34549 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34551 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34552 if(this.cm.isHidden(colIndex)){
34553 return; // can't calc a hidden column
34556 var cid = this.cm.getColumnId(colIndex);
34557 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34558 if(this.grid.autoSizeHeaders){
34559 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34562 var newWidth = this.calcColumnWidth(colIndex);
34563 this.cm.setColumnWidth(colIndex,
34564 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34565 if(!suppressEvent){
34566 this.grid.fireEvent("columnresize", colIndex, newWidth);
34571 * Autofits all columns to their content and then expands to fit any extra space in the grid
34573 autoSizeColumns : function(){
34574 var cm = this.grid.colModel;
34575 var colCount = cm.getColumnCount();
34576 for(var i = 0; i < colCount; i++){
34577 this.autoSizeColumn(i, true, true);
34579 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34582 this.updateColumns();
34588 * Autofits all columns to the grid's width proportionate with their current size
34589 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34591 fitColumns : function(reserveScrollSpace){
34592 var cm = this.grid.colModel;
34593 var colCount = cm.getColumnCount();
34597 for (i = 0; i < colCount; i++){
34598 if(!cm.isHidden(i) && !cm.isFixed(i)){
34599 w = cm.getColumnWidth(i);
34605 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34606 if(reserveScrollSpace){
34609 var frac = (avail - cm.getTotalWidth())/width;
34610 while (cols.length){
34613 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34615 this.updateColumns();
34619 onRowSelect : function(rowIndex){
34620 var row = this.getRowComposite(rowIndex);
34621 row.addClass("x-grid-row-selected");
34624 onRowDeselect : function(rowIndex){
34625 var row = this.getRowComposite(rowIndex);
34626 row.removeClass("x-grid-row-selected");
34629 onCellSelect : function(row, col){
34630 var cell = this.getCell(row, col);
34632 Roo.fly(cell).addClass("x-grid-cell-selected");
34636 onCellDeselect : function(row, col){
34637 var cell = this.getCell(row, col);
34639 Roo.fly(cell).removeClass("x-grid-cell-selected");
34643 updateHeaderSortState : function(){
34645 // sort state can be single { field: xxx, direction : yyy}
34646 // or { xxx=>ASC , yyy : DESC ..... }
34649 if (!this.ds.multiSort) {
34650 var state = this.ds.getSortState();
34654 mstate[state.field] = state.direction;
34655 // FIXME... - this is not used here.. but might be elsewhere..
34656 this.sortState = state;
34659 mstate = this.ds.sortToggle;
34661 //remove existing sort classes..
34663 var sc = this.sortClasses;
34664 var hds = this.el.select(this.headerSelector).removeClass(sc);
34666 for(var f in mstate) {
34668 var sortColumn = this.cm.findColumnIndex(f);
34670 if(sortColumn != -1){
34671 var sortDir = mstate[f];
34672 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34681 handleHeaderClick : function(g, index,e){
34683 Roo.log("header click");
34686 // touch events on header are handled by context
34687 this.handleHdCtx(g,index,e);
34692 if(this.headersDisabled){
34695 var dm = g.dataSource, cm = g.colModel;
34696 if(!cm.isSortable(index)){
34701 if (dm.multiSort) {
34702 // update the sortOrder
34704 for(var i = 0; i < cm.config.length; i++ ) {
34706 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34707 continue; // dont' bother, it's not in sort list or being set.
34710 so.push(cm.config[i].dataIndex);
34716 dm.sort(cm.getDataIndex(index));
34720 destroy : function(){
34722 this.colMenu.removeAll();
34723 Roo.menu.MenuMgr.unregister(this.colMenu);
34724 this.colMenu.getEl().remove();
34725 delete this.colMenu;
34728 this.hmenu.removeAll();
34729 Roo.menu.MenuMgr.unregister(this.hmenu);
34730 this.hmenu.getEl().remove();
34733 if(this.grid.enableColumnMove){
34734 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34736 for(var dd in dds){
34737 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34738 var elid = dds[dd].dragElId;
34740 Roo.get(elid).remove();
34741 } else if(dds[dd].config.isTarget){
34742 dds[dd].proxyTop.remove();
34743 dds[dd].proxyBottom.remove();
34746 if(Roo.dd.DDM.locationCache[dd]){
34747 delete Roo.dd.DDM.locationCache[dd];
34750 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34753 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34754 this.bind(null, null);
34755 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34758 handleLockChange : function(){
34759 this.refresh(true);
34762 onDenyColumnLock : function(){
34766 onDenyColumnHide : function(){
34770 handleHdMenuClick : function(item){
34771 var index = this.hdCtxIndex;
34772 var cm = this.cm, ds = this.ds;
34775 ds.sort(cm.getDataIndex(index), "ASC");
34778 ds.sort(cm.getDataIndex(index), "DESC");
34781 var lc = cm.getLockedCount();
34782 if(cm.getColumnCount(true) <= lc+1){
34783 this.onDenyColumnLock();
34787 cm.setLocked(index, true, true);
34788 cm.moveColumn(index, lc);
34789 this.grid.fireEvent("columnmove", index, lc);
34791 cm.setLocked(index, true);
34795 var lc = cm.getLockedCount();
34796 if((lc-1) != index){
34797 cm.setLocked(index, false, true);
34798 cm.moveColumn(index, lc-1);
34799 this.grid.fireEvent("columnmove", index, lc-1);
34801 cm.setLocked(index, false);
34804 case 'wider': // used to expand cols on touch..
34806 var cw = cm.getColumnWidth(index);
34807 cw += (item.id == 'wider' ? 1 : -1) * 50;
34808 cw = Math.max(0, cw);
34809 cw = Math.min(cw,4000);
34810 cm.setColumnWidth(index, cw);
34814 index = cm.getIndexById(item.id.substr(4));
34816 if(item.checked && cm.getColumnCount(true) <= 1){
34817 this.onDenyColumnHide();
34820 cm.setHidden(index, item.checked);
34826 beforeColMenuShow : function(){
34827 var cm = this.cm, colCount = cm.getColumnCount();
34828 this.colMenu.removeAll();
34829 for(var i = 0; i < colCount; i++){
34830 this.colMenu.add(new Roo.menu.CheckItem({
34831 id: "col-"+cm.getColumnId(i),
34832 text: cm.getColumnHeader(i),
34833 checked: !cm.isHidden(i),
34839 handleHdCtx : function(g, index, e){
34841 var hd = this.getHeaderCell(index);
34842 this.hdCtxIndex = index;
34843 var ms = this.hmenu.items, cm = this.cm;
34844 ms.get("asc").setDisabled(!cm.isSortable(index));
34845 ms.get("desc").setDisabled(!cm.isSortable(index));
34846 if(this.grid.enableColLock !== false){
34847 ms.get("lock").setDisabled(cm.isLocked(index));
34848 ms.get("unlock").setDisabled(!cm.isLocked(index));
34850 this.hmenu.show(hd, "tl-bl");
34853 handleHdOver : function(e){
34854 var hd = this.findHeaderCell(e.getTarget());
34855 if(hd && !this.headersDisabled){
34856 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34857 this.fly(hd).addClass("x-grid-hd-over");
34862 handleHdOut : function(e){
34863 var hd = this.findHeaderCell(e.getTarget());
34865 this.fly(hd).removeClass("x-grid-hd-over");
34869 handleSplitDblClick : function(e, t){
34870 var i = this.getCellIndex(t);
34871 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34872 this.autoSizeColumn(i, true);
34877 render : function(){
34880 var colCount = cm.getColumnCount();
34882 if(this.grid.monitorWindowResize === true){
34883 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34885 var header = this.renderHeaders();
34886 var body = this.templates.body.apply({rows:""});
34887 var html = this.templates.master.apply({
34890 lockedHeader: header[0],
34894 //this.updateColumns();
34896 this.grid.getGridEl().dom.innerHTML = html;
34898 this.initElements();
34900 // a kludge to fix the random scolling effect in webkit
34901 this.el.on("scroll", function() {
34902 this.el.dom.scrollTop=0; // hopefully not recursive..
34905 this.scroller.on("scroll", this.handleScroll, this);
34906 this.lockedBody.on("mousewheel", this.handleWheel, this);
34907 this.mainBody.on("mousewheel", this.handleWheel, this);
34909 this.mainHd.on("mouseover", this.handleHdOver, this);
34910 this.mainHd.on("mouseout", this.handleHdOut, this);
34911 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34912 {delegate: "."+this.splitClass});
34914 this.lockedHd.on("mouseover", this.handleHdOver, this);
34915 this.lockedHd.on("mouseout", this.handleHdOut, this);
34916 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34917 {delegate: "."+this.splitClass});
34919 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34920 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34923 this.updateSplitters();
34925 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34926 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34927 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34930 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34931 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34933 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34934 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34936 if(this.grid.enableColLock !== false){
34937 this.hmenu.add('-',
34938 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34939 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34943 this.hmenu.add('-',
34944 {id:"wider", text: this.columnsWiderText},
34945 {id:"narrow", text: this.columnsNarrowText }
34951 if(this.grid.enableColumnHide !== false){
34953 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34954 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34955 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34957 this.hmenu.add('-',
34958 {id:"columns", text: this.columnsText, menu: this.colMenu}
34961 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34963 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34966 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34967 this.dd = new Roo.grid.GridDragZone(this.grid, {
34968 ddGroup : this.grid.ddGroup || 'GridDD'
34974 for(var i = 0; i < colCount; i++){
34975 if(cm.isHidden(i)){
34976 this.hideColumn(i);
34978 if(cm.config[i].align){
34979 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34980 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34984 this.updateHeaderSortState();
34986 this.beforeInitialResize();
34989 // two part rendering gives faster view to the user
34990 this.renderPhase2.defer(1, this);
34993 renderPhase2 : function(){
34994 // render the rows now
34996 if(this.grid.autoSizeColumns){
34997 this.autoSizeColumns();
35001 beforeInitialResize : function(){
35005 onColumnSplitterMoved : function(i, w){
35006 this.userResized = true;
35007 var cm = this.grid.colModel;
35008 cm.setColumnWidth(i, w, true);
35009 var cid = cm.getColumnId(i);
35010 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35011 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35012 this.updateSplitters();
35014 this.grid.fireEvent("columnresize", i, w);
35017 syncRowHeights : function(startIndex, endIndex){
35018 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35019 startIndex = startIndex || 0;
35020 var mrows = this.getBodyTable().rows;
35021 var lrows = this.getLockedTable().rows;
35022 var len = mrows.length-1;
35023 endIndex = Math.min(endIndex || len, len);
35024 for(var i = startIndex; i <= endIndex; i++){
35025 var m = mrows[i], l = lrows[i];
35026 var h = Math.max(m.offsetHeight, l.offsetHeight);
35027 m.style.height = l.style.height = h + "px";
35032 layout : function(initialRender, is2ndPass){
35034 var auto = g.autoHeight;
35035 var scrollOffset = 16;
35036 var c = g.getGridEl(), cm = this.cm,
35037 expandCol = g.autoExpandColumn,
35039 //c.beginMeasure();
35041 if(!c.dom.offsetWidth){ // display:none?
35043 this.lockedWrap.show();
35044 this.mainWrap.show();
35049 var hasLock = this.cm.isLocked(0);
35051 var tbh = this.headerPanel.getHeight();
35052 var bbh = this.footerPanel.getHeight();
35055 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35056 var newHeight = ch + c.getBorderWidth("tb");
35058 newHeight = Math.min(g.maxHeight, newHeight);
35060 c.setHeight(newHeight);
35064 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35067 var s = this.scroller;
35069 var csize = c.getSize(true);
35071 this.el.setSize(csize.width, csize.height);
35073 this.headerPanel.setWidth(csize.width);
35074 this.footerPanel.setWidth(csize.width);
35076 var hdHeight = this.mainHd.getHeight();
35077 var vw = csize.width;
35078 var vh = csize.height - (tbh + bbh);
35082 var bt = this.getBodyTable();
35084 if(cm.getLockedCount() == cm.config.length){
35085 bt = this.getLockedTable();
35088 var ltWidth = hasLock ?
35089 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35091 var scrollHeight = bt.offsetHeight;
35092 var scrollWidth = ltWidth + bt.offsetWidth;
35093 var vscroll = false, hscroll = false;
35095 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35097 var lw = this.lockedWrap, mw = this.mainWrap;
35098 var lb = this.lockedBody, mb = this.mainBody;
35100 setTimeout(function(){
35101 var t = s.dom.offsetTop;
35102 var w = s.dom.clientWidth,
35103 h = s.dom.clientHeight;
35106 lw.setSize(ltWidth, h);
35108 mw.setLeftTop(ltWidth, t);
35109 mw.setSize(w-ltWidth, h);
35111 lb.setHeight(h-hdHeight);
35112 mb.setHeight(h-hdHeight);
35114 if(is2ndPass !== true && !gv.userResized && expandCol){
35115 // high speed resize without full column calculation
35117 var ci = cm.getIndexById(expandCol);
35119 ci = cm.findColumnIndex(expandCol);
35121 ci = Math.max(0, ci); // make sure it's got at least the first col.
35122 var expandId = cm.getColumnId(ci);
35123 var tw = cm.getTotalWidth(false);
35124 var currentWidth = cm.getColumnWidth(ci);
35125 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35126 if(currentWidth != cw){
35127 cm.setColumnWidth(ci, cw, true);
35128 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35129 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35130 gv.updateSplitters();
35131 gv.layout(false, true);
35143 onWindowResize : function(){
35144 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35150 appendFooter : function(parentEl){
35154 sortAscText : "Sort Ascending",
35155 sortDescText : "Sort Descending",
35156 lockText : "Lock Column",
35157 unlockText : "Unlock Column",
35158 columnsText : "Columns",
35160 columnsWiderText : "Wider",
35161 columnsNarrowText : "Thinner"
35165 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35166 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35167 this.proxy.el.addClass('x-grid3-col-dd');
35170 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35171 handleMouseDown : function(e){
35175 callHandleMouseDown : function(e){
35176 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35181 * Ext JS Library 1.1.1
35182 * Copyright(c) 2006-2007, Ext JS, LLC.
35184 * Originally Released Under LGPL - original licence link has changed is not relivant.
35187 * <script type="text/javascript">
35191 // This is a support class used internally by the Grid components
35192 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35194 this.view = grid.getView();
35195 this.proxy = this.view.resizeProxy;
35196 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35197 "gridSplitters" + this.grid.getGridEl().id, {
35198 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35200 this.setHandleElId(Roo.id(hd));
35201 this.setOuterHandleElId(Roo.id(hd2));
35202 this.scroll = false;
35204 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35205 fly: Roo.Element.fly,
35207 b4StartDrag : function(x, y){
35208 this.view.headersDisabled = true;
35209 this.proxy.setHeight(this.view.mainWrap.getHeight());
35210 var w = this.cm.getColumnWidth(this.cellIndex);
35211 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35212 this.resetConstraints();
35213 this.setXConstraint(minw, 1000);
35214 this.setYConstraint(0, 0);
35215 this.minX = x - minw;
35216 this.maxX = x + 1000;
35218 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35222 handleMouseDown : function(e){
35223 ev = Roo.EventObject.setEvent(e);
35224 var t = this.fly(ev.getTarget());
35225 if(t.hasClass("x-grid-split")){
35226 this.cellIndex = this.view.getCellIndex(t.dom);
35227 this.split = t.dom;
35228 this.cm = this.grid.colModel;
35229 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35230 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35235 endDrag : function(e){
35236 this.view.headersDisabled = false;
35237 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35238 var diff = endX - this.startPos;
35239 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35242 autoOffset : function(){
35243 this.setDelta(0,0);
35247 * Ext JS Library 1.1.1
35248 * Copyright(c) 2006-2007, Ext JS, LLC.
35250 * Originally Released Under LGPL - original licence link has changed is not relivant.
35253 * <script type="text/javascript">
35257 // This is a support class used internally by the Grid components
35258 Roo.grid.GridDragZone = function(grid, config){
35259 this.view = grid.getView();
35260 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35261 if(this.view.lockedBody){
35262 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35263 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35265 this.scroll = false;
35267 this.ddel = document.createElement('div');
35268 this.ddel.className = 'x-grid-dd-wrap';
35271 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35272 ddGroup : "GridDD",
35274 getDragData : function(e){
35275 var t = Roo.lib.Event.getTarget(e);
35276 var rowIndex = this.view.findRowIndex(t);
35277 var sm = this.grid.selModel;
35279 //Roo.log(rowIndex);
35281 if (sm.getSelectedCell) {
35282 // cell selection..
35283 if (!sm.getSelectedCell()) {
35286 if (rowIndex != sm.getSelectedCell()[0]) {
35292 if(rowIndex !== false){
35297 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35299 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35302 if (e.hasModifier()){
35303 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35306 Roo.log("getDragData");
35311 rowIndex: rowIndex,
35312 selections:sm.getSelections ? sm.getSelections() : (
35313 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35320 onInitDrag : function(e){
35321 var data = this.dragData;
35322 this.ddel.innerHTML = this.grid.getDragDropText();
35323 this.proxy.update(this.ddel);
35324 // fire start drag?
35327 afterRepair : function(){
35328 this.dragging = false;
35331 getRepairXY : function(e, data){
35335 onEndDrag : function(data, e){
35339 onValidDrop : function(dd, e, id){
35344 beforeInvalidDrop : function(e, id){
35349 * Ext JS Library 1.1.1
35350 * Copyright(c) 2006-2007, Ext JS, LLC.
35352 * Originally Released Under LGPL - original licence link has changed is not relivant.
35355 * <script type="text/javascript">
35360 * @class Roo.grid.ColumnModel
35361 * @extends Roo.util.Observable
35362 * This is the default implementation of a ColumnModel used by the Grid. It defines
35363 * the columns in the grid.
35366 var colModel = new Roo.grid.ColumnModel([
35367 {header: "Ticker", width: 60, sortable: true, locked: true},
35368 {header: "Company Name", width: 150, sortable: true},
35369 {header: "Market Cap.", width: 100, sortable: true},
35370 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35371 {header: "Employees", width: 100, sortable: true, resizable: false}
35376 * The config options listed for this class are options which may appear in each
35377 * individual column definition.
35378 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35380 * @param {Object} config An Array of column config objects. See this class's
35381 * config objects for details.
35383 Roo.grid.ColumnModel = function(config){
35385 * The config passed into the constructor
35387 this.config = config;
35390 // if no id, create one
35391 // if the column does not have a dataIndex mapping,
35392 // map it to the order it is in the config
35393 for(var i = 0, len = config.length; i < len; i++){
35395 if(typeof c.dataIndex == "undefined"){
35398 if(typeof c.renderer == "string"){
35399 c.renderer = Roo.util.Format[c.renderer];
35401 if(typeof c.id == "undefined"){
35404 if(c.editor && c.editor.xtype){
35405 c.editor = Roo.factory(c.editor, Roo.grid);
35407 if(c.editor && c.editor.isFormField){
35408 c.editor = new Roo.grid.GridEditor(c.editor);
35410 this.lookup[c.id] = c;
35414 * The width of columns which have no width specified (defaults to 100)
35417 this.defaultWidth = 100;
35420 * Default sortable of columns which have no sortable specified (defaults to false)
35423 this.defaultSortable = false;
35427 * @event widthchange
35428 * Fires when the width of a column changes.
35429 * @param {ColumnModel} this
35430 * @param {Number} columnIndex The column index
35431 * @param {Number} newWidth The new width
35433 "widthchange": true,
35435 * @event headerchange
35436 * Fires when the text of a header changes.
35437 * @param {ColumnModel} this
35438 * @param {Number} columnIndex The column index
35439 * @param {Number} newText The new header text
35441 "headerchange": true,
35443 * @event hiddenchange
35444 * Fires when a column is hidden or "unhidden".
35445 * @param {ColumnModel} this
35446 * @param {Number} columnIndex The column index
35447 * @param {Boolean} hidden true if hidden, false otherwise
35449 "hiddenchange": true,
35451 * @event columnmoved
35452 * Fires when a column is moved.
35453 * @param {ColumnModel} this
35454 * @param {Number} oldIndex
35455 * @param {Number} newIndex
35457 "columnmoved" : true,
35459 * @event columlockchange
35460 * Fires when a column's locked state is changed
35461 * @param {ColumnModel} this
35462 * @param {Number} colIndex
35463 * @param {Boolean} locked true if locked
35465 "columnlockchange" : true
35467 Roo.grid.ColumnModel.superclass.constructor.call(this);
35469 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35471 * @cfg {String} header The header text to display in the Grid view.
35474 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35475 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35476 * specified, the column's index is used as an index into the Record's data Array.
35479 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35480 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35483 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35484 * Defaults to the value of the {@link #defaultSortable} property.
35485 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35488 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35491 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35494 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35497 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35500 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35501 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35502 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35503 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35506 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35509 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35512 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35515 * @cfg {String} cursor (Optional)
35518 * @cfg {String} tooltip (Optional)
35521 * @cfg {Number} xs (Optional)
35524 * @cfg {Number} sm (Optional)
35527 * @cfg {Number} md (Optional)
35530 * @cfg {Number} lg (Optional)
35533 * Returns the id of the column at the specified index.
35534 * @param {Number} index The column index
35535 * @return {String} the id
35537 getColumnId : function(index){
35538 return this.config[index].id;
35542 * Returns the column for a specified id.
35543 * @param {String} id The column id
35544 * @return {Object} the column
35546 getColumnById : function(id){
35547 return this.lookup[id];
35552 * Returns the column for a specified dataIndex.
35553 * @param {String} dataIndex The column dataIndex
35554 * @return {Object|Boolean} the column or false if not found
35556 getColumnByDataIndex: function(dataIndex){
35557 var index = this.findColumnIndex(dataIndex);
35558 return index > -1 ? this.config[index] : false;
35562 * Returns the index for a specified column id.
35563 * @param {String} id The column id
35564 * @return {Number} the index, or -1 if not found
35566 getIndexById : function(id){
35567 for(var i = 0, len = this.config.length; i < len; i++){
35568 if(this.config[i].id == id){
35576 * Returns the index for a specified column dataIndex.
35577 * @param {String} dataIndex The column dataIndex
35578 * @return {Number} the index, or -1 if not found
35581 findColumnIndex : function(dataIndex){
35582 for(var i = 0, len = this.config.length; i < len; i++){
35583 if(this.config[i].dataIndex == dataIndex){
35591 moveColumn : function(oldIndex, newIndex){
35592 var c = this.config[oldIndex];
35593 this.config.splice(oldIndex, 1);
35594 this.config.splice(newIndex, 0, c);
35595 this.dataMap = null;
35596 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35599 isLocked : function(colIndex){
35600 return this.config[colIndex].locked === true;
35603 setLocked : function(colIndex, value, suppressEvent){
35604 if(this.isLocked(colIndex) == value){
35607 this.config[colIndex].locked = value;
35608 if(!suppressEvent){
35609 this.fireEvent("columnlockchange", this, colIndex, value);
35613 getTotalLockedWidth : function(){
35614 var totalWidth = 0;
35615 for(var i = 0; i < this.config.length; i++){
35616 if(this.isLocked(i) && !this.isHidden(i)){
35617 this.totalWidth += this.getColumnWidth(i);
35623 getLockedCount : function(){
35624 for(var i = 0, len = this.config.length; i < len; i++){
35625 if(!this.isLocked(i)){
35630 return this.config.length;
35634 * Returns the number of columns.
35637 getColumnCount : function(visibleOnly){
35638 if(visibleOnly === true){
35640 for(var i = 0, len = this.config.length; i < len; i++){
35641 if(!this.isHidden(i)){
35647 return this.config.length;
35651 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35652 * @param {Function} fn
35653 * @param {Object} scope (optional)
35654 * @return {Array} result
35656 getColumnsBy : function(fn, scope){
35658 for(var i = 0, len = this.config.length; i < len; i++){
35659 var c = this.config[i];
35660 if(fn.call(scope||this, c, i) === true){
35668 * Returns true if the specified column is sortable.
35669 * @param {Number} col The column index
35670 * @return {Boolean}
35672 isSortable : function(col){
35673 if(typeof this.config[col].sortable == "undefined"){
35674 return this.defaultSortable;
35676 return this.config[col].sortable;
35680 * Returns the rendering (formatting) function defined for the column.
35681 * @param {Number} col The column index.
35682 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35684 getRenderer : function(col){
35685 if(!this.config[col].renderer){
35686 return Roo.grid.ColumnModel.defaultRenderer;
35688 return this.config[col].renderer;
35692 * Sets the rendering (formatting) function for a column.
35693 * @param {Number} col The column index
35694 * @param {Function} fn The function to use to process the cell's raw data
35695 * to return HTML markup for the grid view. The render function is called with
35696 * the following parameters:<ul>
35697 * <li>Data value.</li>
35698 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35699 * <li>css A CSS style string to apply to the table cell.</li>
35700 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35701 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35702 * <li>Row index</li>
35703 * <li>Column index</li>
35704 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35706 setRenderer : function(col, fn){
35707 this.config[col].renderer = fn;
35711 * Returns the width for the specified column.
35712 * @param {Number} col The column index
35715 getColumnWidth : function(col){
35716 return this.config[col].width * 1 || this.defaultWidth;
35720 * Sets the width for a column.
35721 * @param {Number} col The column index
35722 * @param {Number} width The new width
35724 setColumnWidth : function(col, width, suppressEvent){
35725 this.config[col].width = width;
35726 this.totalWidth = null;
35727 if(!suppressEvent){
35728 this.fireEvent("widthchange", this, col, width);
35733 * Returns the total width of all columns.
35734 * @param {Boolean} includeHidden True to include hidden column widths
35737 getTotalWidth : function(includeHidden){
35738 if(!this.totalWidth){
35739 this.totalWidth = 0;
35740 for(var i = 0, len = this.config.length; i < len; i++){
35741 if(includeHidden || !this.isHidden(i)){
35742 this.totalWidth += this.getColumnWidth(i);
35746 return this.totalWidth;
35750 * Returns the header for the specified column.
35751 * @param {Number} col The column index
35754 getColumnHeader : function(col){
35755 return this.config[col].header;
35759 * Sets the header for a column.
35760 * @param {Number} col The column index
35761 * @param {String} header The new header
35763 setColumnHeader : function(col, header){
35764 this.config[col].header = header;
35765 this.fireEvent("headerchange", this, col, header);
35769 * Returns the tooltip for the specified column.
35770 * @param {Number} col The column index
35773 getColumnTooltip : function(col){
35774 return this.config[col].tooltip;
35777 * Sets the tooltip for a column.
35778 * @param {Number} col The column index
35779 * @param {String} tooltip The new tooltip
35781 setColumnTooltip : function(col, tooltip){
35782 this.config[col].tooltip = tooltip;
35786 * Returns the dataIndex for the specified column.
35787 * @param {Number} col The column index
35790 getDataIndex : function(col){
35791 return this.config[col].dataIndex;
35795 * Sets the dataIndex for a column.
35796 * @param {Number} col The column index
35797 * @param {Number} dataIndex The new dataIndex
35799 setDataIndex : function(col, dataIndex){
35800 this.config[col].dataIndex = dataIndex;
35806 * Returns true if the cell is editable.
35807 * @param {Number} colIndex The column index
35808 * @param {Number} rowIndex The row index - this is nto actually used..?
35809 * @return {Boolean}
35811 isCellEditable : function(colIndex, rowIndex){
35812 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35816 * Returns the editor defined for the cell/column.
35817 * return false or null to disable editing.
35818 * @param {Number} colIndex The column index
35819 * @param {Number} rowIndex The row index
35822 getCellEditor : function(colIndex, rowIndex){
35823 return this.config[colIndex].editor;
35827 * Sets if a column is editable.
35828 * @param {Number} col The column index
35829 * @param {Boolean} editable True if the column is editable
35831 setEditable : function(col, editable){
35832 this.config[col].editable = editable;
35837 * Returns true if the column is hidden.
35838 * @param {Number} colIndex The column index
35839 * @return {Boolean}
35841 isHidden : function(colIndex){
35842 return this.config[colIndex].hidden;
35847 * Returns true if the column width cannot be changed
35849 isFixed : function(colIndex){
35850 return this.config[colIndex].fixed;
35854 * Returns true if the column can be resized
35855 * @return {Boolean}
35857 isResizable : function(colIndex){
35858 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35861 * Sets if a column is hidden.
35862 * @param {Number} colIndex The column index
35863 * @param {Boolean} hidden True if the column is hidden
35865 setHidden : function(colIndex, hidden){
35866 this.config[colIndex].hidden = hidden;
35867 this.totalWidth = null;
35868 this.fireEvent("hiddenchange", this, colIndex, hidden);
35872 * Sets the editor for a column.
35873 * @param {Number} col The column index
35874 * @param {Object} editor The editor object
35876 setEditor : function(col, editor){
35877 this.config[col].editor = editor;
35881 Roo.grid.ColumnModel.defaultRenderer = function(value)
35883 if(typeof value == "object") {
35886 if(typeof value == "string" && value.length < 1){
35890 return String.format("{0}", value);
35893 // Alias for backwards compatibility
35894 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35897 * Ext JS Library 1.1.1
35898 * Copyright(c) 2006-2007, Ext JS, LLC.
35900 * Originally Released Under LGPL - original licence link has changed is not relivant.
35903 * <script type="text/javascript">
35907 * @class Roo.grid.AbstractSelectionModel
35908 * @extends Roo.util.Observable
35909 * Abstract base class for grid SelectionModels. It provides the interface that should be
35910 * implemented by descendant classes. This class should not be directly instantiated.
35913 Roo.grid.AbstractSelectionModel = function(){
35914 this.locked = false;
35915 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35918 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35919 /** @ignore Called by the grid automatically. Do not call directly. */
35920 init : function(grid){
35926 * Locks the selections.
35929 this.locked = true;
35933 * Unlocks the selections.
35935 unlock : function(){
35936 this.locked = false;
35940 * Returns true if the selections are locked.
35941 * @return {Boolean}
35943 isLocked : function(){
35944 return this.locked;
35948 * Ext JS Library 1.1.1
35949 * Copyright(c) 2006-2007, Ext JS, LLC.
35951 * Originally Released Under LGPL - original licence link has changed is not relivant.
35954 * <script type="text/javascript">
35957 * @extends Roo.grid.AbstractSelectionModel
35958 * @class Roo.grid.RowSelectionModel
35959 * The default SelectionModel used by {@link Roo.grid.Grid}.
35960 * It supports multiple selections and keyboard selection/navigation.
35962 * @param {Object} config
35964 Roo.grid.RowSelectionModel = function(config){
35965 Roo.apply(this, config);
35966 this.selections = new Roo.util.MixedCollection(false, function(o){
35971 this.lastActive = false;
35975 * @event selectionchange
35976 * Fires when the selection changes
35977 * @param {SelectionModel} this
35979 "selectionchange" : true,
35981 * @event afterselectionchange
35982 * Fires after the selection changes (eg. by key press or clicking)
35983 * @param {SelectionModel} this
35985 "afterselectionchange" : true,
35987 * @event beforerowselect
35988 * Fires when a row is selected being selected, return false to cancel.
35989 * @param {SelectionModel} this
35990 * @param {Number} rowIndex The selected index
35991 * @param {Boolean} keepExisting False if other selections will be cleared
35993 "beforerowselect" : true,
35996 * Fires when a row is selected.
35997 * @param {SelectionModel} this
35998 * @param {Number} rowIndex The selected index
35999 * @param {Roo.data.Record} r The record
36001 "rowselect" : true,
36003 * @event rowdeselect
36004 * Fires when a row is deselected.
36005 * @param {SelectionModel} this
36006 * @param {Number} rowIndex The selected index
36008 "rowdeselect" : true
36010 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36011 this.locked = false;
36014 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
36016 * @cfg {Boolean} singleSelect
36017 * True to allow selection of only one row at a time (defaults to false)
36019 singleSelect : false,
36022 initEvents : function(){
36024 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36025 this.grid.on("mousedown", this.handleMouseDown, this);
36026 }else{ // allow click to work like normal
36027 this.grid.on("rowclick", this.handleDragableRowClick, this);
36030 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36031 "up" : function(e){
36033 this.selectPrevious(e.shiftKey);
36034 }else if(this.last !== false && this.lastActive !== false){
36035 var last = this.last;
36036 this.selectRange(this.last, this.lastActive-1);
36037 this.grid.getView().focusRow(this.lastActive);
36038 if(last !== false){
36042 this.selectFirstRow();
36044 this.fireEvent("afterselectionchange", this);
36046 "down" : function(e){
36048 this.selectNext(e.shiftKey);
36049 }else if(this.last !== false && this.lastActive !== false){
36050 var last = this.last;
36051 this.selectRange(this.last, this.lastActive+1);
36052 this.grid.getView().focusRow(this.lastActive);
36053 if(last !== false){
36057 this.selectFirstRow();
36059 this.fireEvent("afterselectionchange", this);
36064 var view = this.grid.view;
36065 view.on("refresh", this.onRefresh, this);
36066 view.on("rowupdated", this.onRowUpdated, this);
36067 view.on("rowremoved", this.onRemove, this);
36071 onRefresh : function(){
36072 var ds = this.grid.dataSource, i, v = this.grid.view;
36073 var s = this.selections;
36074 s.each(function(r){
36075 if((i = ds.indexOfId(r.id)) != -1){
36077 s.add(ds.getAt(i)); // updating the selection relate data
36085 onRemove : function(v, index, r){
36086 this.selections.remove(r);
36090 onRowUpdated : function(v, index, r){
36091 if(this.isSelected(r)){
36092 v.onRowSelect(index);
36098 * @param {Array} records The records to select
36099 * @param {Boolean} keepExisting (optional) True to keep existing selections
36101 selectRecords : function(records, keepExisting){
36103 this.clearSelections();
36105 var ds = this.grid.dataSource;
36106 for(var i = 0, len = records.length; i < len; i++){
36107 this.selectRow(ds.indexOf(records[i]), true);
36112 * Gets the number of selected rows.
36115 getCount : function(){
36116 return this.selections.length;
36120 * Selects the first row in the grid.
36122 selectFirstRow : function(){
36127 * Select the last row.
36128 * @param {Boolean} keepExisting (optional) True to keep existing selections
36130 selectLastRow : function(keepExisting){
36131 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36135 * Selects the row immediately following the last selected row.
36136 * @param {Boolean} keepExisting (optional) True to keep existing selections
36138 selectNext : function(keepExisting){
36139 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36140 this.selectRow(this.last+1, keepExisting);
36141 this.grid.getView().focusRow(this.last);
36146 * Selects the row that precedes the last selected row.
36147 * @param {Boolean} keepExisting (optional) True to keep existing selections
36149 selectPrevious : function(keepExisting){
36151 this.selectRow(this.last-1, keepExisting);
36152 this.grid.getView().focusRow(this.last);
36157 * Returns the selected records
36158 * @return {Array} Array of selected records
36160 getSelections : function(){
36161 return [].concat(this.selections.items);
36165 * Returns the first selected record.
36168 getSelected : function(){
36169 return this.selections.itemAt(0);
36174 * Clears all selections.
36176 clearSelections : function(fast){
36181 var ds = this.grid.dataSource;
36182 var s = this.selections;
36183 s.each(function(r){
36184 this.deselectRow(ds.indexOfId(r.id));
36188 this.selections.clear();
36195 * Selects all rows.
36197 selectAll : function(){
36201 this.selections.clear();
36202 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36203 this.selectRow(i, true);
36208 * Returns True if there is a selection.
36209 * @return {Boolean}
36211 hasSelection : function(){
36212 return this.selections.length > 0;
36216 * Returns True if the specified row is selected.
36217 * @param {Number/Record} record The record or index of the record to check
36218 * @return {Boolean}
36220 isSelected : function(index){
36221 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36222 return (r && this.selections.key(r.id) ? true : false);
36226 * Returns True if the specified record id is selected.
36227 * @param {String} id The id of record to check
36228 * @return {Boolean}
36230 isIdSelected : function(id){
36231 return (this.selections.key(id) ? true : false);
36235 handleMouseDown : function(e, t){
36236 var view = this.grid.getView(), rowIndex;
36237 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36240 if(e.shiftKey && this.last !== false){
36241 var last = this.last;
36242 this.selectRange(last, rowIndex, e.ctrlKey);
36243 this.last = last; // reset the last
36244 view.focusRow(rowIndex);
36246 var isSelected = this.isSelected(rowIndex);
36247 if(e.button !== 0 && isSelected){
36248 view.focusRow(rowIndex);
36249 }else if(e.ctrlKey && isSelected){
36250 this.deselectRow(rowIndex);
36251 }else if(!isSelected){
36252 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36253 view.focusRow(rowIndex);
36256 this.fireEvent("afterselectionchange", this);
36259 handleDragableRowClick : function(grid, rowIndex, e)
36261 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36262 this.selectRow(rowIndex, false);
36263 grid.view.focusRow(rowIndex);
36264 this.fireEvent("afterselectionchange", this);
36269 * Selects multiple rows.
36270 * @param {Array} rows Array of the indexes of the row to select
36271 * @param {Boolean} keepExisting (optional) True to keep existing selections
36273 selectRows : function(rows, keepExisting){
36275 this.clearSelections();
36277 for(var i = 0, len = rows.length; i < len; i++){
36278 this.selectRow(rows[i], true);
36283 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36284 * @param {Number} startRow The index of the first row in the range
36285 * @param {Number} endRow The index of the last row in the range
36286 * @param {Boolean} keepExisting (optional) True to retain existing selections
36288 selectRange : function(startRow, endRow, keepExisting){
36293 this.clearSelections();
36295 if(startRow <= endRow){
36296 for(var i = startRow; i <= endRow; i++){
36297 this.selectRow(i, true);
36300 for(var i = startRow; i >= endRow; i--){
36301 this.selectRow(i, true);
36307 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36308 * @param {Number} startRow The index of the first row in the range
36309 * @param {Number} endRow The index of the last row in the range
36311 deselectRange : function(startRow, endRow, preventViewNotify){
36315 for(var i = startRow; i <= endRow; i++){
36316 this.deselectRow(i, preventViewNotify);
36322 * @param {Number} row The index of the row to select
36323 * @param {Boolean} keepExisting (optional) True to keep existing selections
36325 selectRow : function(index, keepExisting, preventViewNotify){
36326 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36329 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36330 if(!keepExisting || this.singleSelect){
36331 this.clearSelections();
36333 var r = this.grid.dataSource.getAt(index);
36334 this.selections.add(r);
36335 this.last = this.lastActive = index;
36336 if(!preventViewNotify){
36337 this.grid.getView().onRowSelect(index);
36339 this.fireEvent("rowselect", this, index, r);
36340 this.fireEvent("selectionchange", this);
36346 * @param {Number} row The index of the row to deselect
36348 deselectRow : function(index, preventViewNotify){
36352 if(this.last == index){
36355 if(this.lastActive == index){
36356 this.lastActive = false;
36358 var r = this.grid.dataSource.getAt(index);
36359 this.selections.remove(r);
36360 if(!preventViewNotify){
36361 this.grid.getView().onRowDeselect(index);
36363 this.fireEvent("rowdeselect", this, index);
36364 this.fireEvent("selectionchange", this);
36368 restoreLast : function(){
36370 this.last = this._last;
36375 acceptsNav : function(row, col, cm){
36376 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36380 onEditorKey : function(field, e){
36381 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36386 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36388 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36390 }else if(k == e.ENTER && !e.ctrlKey){
36394 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36396 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36398 }else if(k == e.ESC){
36402 g.startEditing(newCell[0], newCell[1]);
36407 * Ext JS Library 1.1.1
36408 * Copyright(c) 2006-2007, Ext JS, LLC.
36410 * Originally Released Under LGPL - original licence link has changed is not relivant.
36413 * <script type="text/javascript">
36416 * @class Roo.grid.CellSelectionModel
36417 * @extends Roo.grid.AbstractSelectionModel
36418 * This class provides the basic implementation for cell selection in a grid.
36420 * @param {Object} config The object containing the configuration of this model.
36421 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36423 Roo.grid.CellSelectionModel = function(config){
36424 Roo.apply(this, config);
36426 this.selection = null;
36430 * @event beforerowselect
36431 * Fires before a cell is selected.
36432 * @param {SelectionModel} this
36433 * @param {Number} rowIndex The selected row index
36434 * @param {Number} colIndex The selected cell index
36436 "beforecellselect" : true,
36438 * @event cellselect
36439 * Fires when a cell is selected.
36440 * @param {SelectionModel} this
36441 * @param {Number} rowIndex The selected row index
36442 * @param {Number} colIndex The selected cell index
36444 "cellselect" : true,
36446 * @event selectionchange
36447 * Fires when the active selection changes.
36448 * @param {SelectionModel} this
36449 * @param {Object} selection null for no selection or an object (o) with two properties
36451 <li>o.record: the record object for the row the selection is in</li>
36452 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36455 "selectionchange" : true,
36458 * Fires when the tab (or enter) was pressed on the last editable cell
36459 * You can use this to trigger add new row.
36460 * @param {SelectionModel} this
36464 * @event beforeeditnext
36465 * Fires before the next editable sell is made active
36466 * You can use this to skip to another cell or fire the tabend
36467 * if you set cell to false
36468 * @param {Object} eventdata object : { cell : [ row, col ] }
36470 "beforeeditnext" : true
36472 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36475 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36477 enter_is_tab: false,
36480 initEvents : function(){
36481 this.grid.on("mousedown", this.handleMouseDown, this);
36482 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36483 var view = this.grid.view;
36484 view.on("refresh", this.onViewChange, this);
36485 view.on("rowupdated", this.onRowUpdated, this);
36486 view.on("beforerowremoved", this.clearSelections, this);
36487 view.on("beforerowsinserted", this.clearSelections, this);
36488 if(this.grid.isEditor){
36489 this.grid.on("beforeedit", this.beforeEdit, this);
36494 beforeEdit : function(e){
36495 this.select(e.row, e.column, false, true, e.record);
36499 onRowUpdated : function(v, index, r){
36500 if(this.selection && this.selection.record == r){
36501 v.onCellSelect(index, this.selection.cell[1]);
36506 onViewChange : function(){
36507 this.clearSelections(true);
36511 * Returns the currently selected cell,.
36512 * @return {Array} The selected cell (row, column) or null if none selected.
36514 getSelectedCell : function(){
36515 return this.selection ? this.selection.cell : null;
36519 * Clears all selections.
36520 * @param {Boolean} true to prevent the gridview from being notified about the change.
36522 clearSelections : function(preventNotify){
36523 var s = this.selection;
36525 if(preventNotify !== true){
36526 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36528 this.selection = null;
36529 this.fireEvent("selectionchange", this, null);
36534 * Returns true if there is a selection.
36535 * @return {Boolean}
36537 hasSelection : function(){
36538 return this.selection ? true : false;
36542 handleMouseDown : function(e, t){
36543 var v = this.grid.getView();
36544 if(this.isLocked()){
36547 var row = v.findRowIndex(t);
36548 var cell = v.findCellIndex(t);
36549 if(row !== false && cell !== false){
36550 this.select(row, cell);
36556 * @param {Number} rowIndex
36557 * @param {Number} collIndex
36559 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36560 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36561 this.clearSelections();
36562 r = r || this.grid.dataSource.getAt(rowIndex);
36565 cell : [rowIndex, colIndex]
36567 if(!preventViewNotify){
36568 var v = this.grid.getView();
36569 v.onCellSelect(rowIndex, colIndex);
36570 if(preventFocus !== true){
36571 v.focusCell(rowIndex, colIndex);
36574 this.fireEvent("cellselect", this, rowIndex, colIndex);
36575 this.fireEvent("selectionchange", this, this.selection);
36580 isSelectable : function(rowIndex, colIndex, cm){
36581 return !cm.isHidden(colIndex);
36585 handleKeyDown : function(e){
36586 //Roo.log('Cell Sel Model handleKeyDown');
36587 if(!e.isNavKeyPress()){
36590 var g = this.grid, s = this.selection;
36593 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36595 this.select(cell[0], cell[1]);
36600 var walk = function(row, col, step){
36601 return g.walkCells(row, col, step, sm.isSelectable, sm);
36603 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36610 // handled by onEditorKey
36611 if (g.isEditor && g.editing) {
36615 newCell = walk(r, c-1, -1);
36617 newCell = walk(r, c+1, 1);
36622 newCell = walk(r+1, c, 1);
36626 newCell = walk(r-1, c, -1);
36630 newCell = walk(r, c+1, 1);
36634 newCell = walk(r, c-1, -1);
36639 if(g.isEditor && !g.editing){
36640 g.startEditing(r, c);
36649 this.select(newCell[0], newCell[1]);
36655 acceptsNav : function(row, col, cm){
36656 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36660 * @param {Number} field (not used) - as it's normally used as a listener
36661 * @param {Number} e - event - fake it by using
36663 * var e = Roo.EventObjectImpl.prototype;
36664 * e.keyCode = e.TAB
36668 onEditorKey : function(field, e){
36670 var k = e.getKey(),
36673 ed = g.activeEditor,
36675 ///Roo.log('onEditorKey' + k);
36678 if (this.enter_is_tab && k == e.ENTER) {
36684 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36686 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36692 } else if(k == e.ENTER && !e.ctrlKey){
36695 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36697 } else if(k == e.ESC){
36702 var ecall = { cell : newCell, forward : forward };
36703 this.fireEvent('beforeeditnext', ecall );
36704 newCell = ecall.cell;
36705 forward = ecall.forward;
36709 //Roo.log('next cell after edit');
36710 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36711 } else if (forward) {
36712 // tabbed past last
36713 this.fireEvent.defer(100, this, ['tabend',this]);
36718 * Ext JS Library 1.1.1
36719 * Copyright(c) 2006-2007, Ext JS, LLC.
36721 * Originally Released Under LGPL - original licence link has changed is not relivant.
36724 * <script type="text/javascript">
36728 * @class Roo.grid.EditorGrid
36729 * @extends Roo.grid.Grid
36730 * Class for creating and editable grid.
36731 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36732 * The container MUST have some type of size defined for the grid to fill. The container will be
36733 * automatically set to position relative if it isn't already.
36734 * @param {Object} dataSource The data model to bind to
36735 * @param {Object} colModel The column model with info about this grid's columns
36737 Roo.grid.EditorGrid = function(container, config){
36738 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36739 this.getGridEl().addClass("xedit-grid");
36741 if(!this.selModel){
36742 this.selModel = new Roo.grid.CellSelectionModel();
36745 this.activeEditor = null;
36749 * @event beforeedit
36750 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36751 * <ul style="padding:5px;padding-left:16px;">
36752 * <li>grid - This grid</li>
36753 * <li>record - The record being edited</li>
36754 * <li>field - The field name being edited</li>
36755 * <li>value - The value for the field being edited.</li>
36756 * <li>row - The grid row index</li>
36757 * <li>column - The grid column index</li>
36758 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36760 * @param {Object} e An edit event (see above for description)
36762 "beforeedit" : true,
36765 * Fires after a cell is edited. <br />
36766 * <ul style="padding:5px;padding-left:16px;">
36767 * <li>grid - This grid</li>
36768 * <li>record - The record being edited</li>
36769 * <li>field - The field name being edited</li>
36770 * <li>value - The value being set</li>
36771 * <li>originalValue - The original value for the field, before the edit.</li>
36772 * <li>row - The grid row index</li>
36773 * <li>column - The grid column index</li>
36775 * @param {Object} e An edit event (see above for description)
36777 "afteredit" : true,
36779 * @event validateedit
36780 * Fires after a cell is edited, but before the value is set in the record.
36781 * You can use this to modify the value being set in the field, Return false
36782 * to cancel the change. The edit event object has the following properties <br />
36783 * <ul style="padding:5px;padding-left:16px;">
36784 * <li>editor - This editor</li>
36785 * <li>grid - This grid</li>
36786 * <li>record - The record being edited</li>
36787 * <li>field - The field name being edited</li>
36788 * <li>value - The value being set</li>
36789 * <li>originalValue - The original value for the field, before the edit.</li>
36790 * <li>row - The grid row index</li>
36791 * <li>column - The grid column index</li>
36792 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36794 * @param {Object} e An edit event (see above for description)
36796 "validateedit" : true
36798 this.on("bodyscroll", this.stopEditing, this);
36799 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36802 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36804 * @cfg {Number} clicksToEdit
36805 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36812 trackMouseOver: false, // causes very odd FF errors
36814 onCellDblClick : function(g, row, col){
36815 this.startEditing(row, col);
36818 onEditComplete : function(ed, value, startValue){
36819 this.editing = false;
36820 this.activeEditor = null;
36821 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36823 var field = this.colModel.getDataIndex(ed.col);
36828 originalValue: startValue,
36835 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36838 if(String(value) !== String(startValue)){
36840 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36841 r.set(field, e.value);
36842 // if we are dealing with a combo box..
36843 // then we also set the 'name' colum to be the displayField
36844 if (ed.field.displayField && ed.field.name) {
36845 r.set(ed.field.name, ed.field.el.dom.value);
36848 delete e.cancel; //?? why!!!
36849 this.fireEvent("afteredit", e);
36852 this.fireEvent("afteredit", e); // always fire it!
36854 this.view.focusCell(ed.row, ed.col);
36858 * Starts editing the specified for the specified row/column
36859 * @param {Number} rowIndex
36860 * @param {Number} colIndex
36862 startEditing : function(row, col){
36863 this.stopEditing();
36864 if(this.colModel.isCellEditable(col, row)){
36865 this.view.ensureVisible(row, col, true);
36867 var r = this.dataSource.getAt(row);
36868 var field = this.colModel.getDataIndex(col);
36869 var cell = Roo.get(this.view.getCell(row,col));
36874 value: r.data[field],
36879 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36880 this.editing = true;
36881 var ed = this.colModel.getCellEditor(col, row);
36887 ed.render(ed.parentEl || document.body);
36893 (function(){ // complex but required for focus issues in safari, ie and opera
36897 ed.on("complete", this.onEditComplete, this, {single: true});
36898 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36899 this.activeEditor = ed;
36900 var v = r.data[field];
36901 ed.startEdit(this.view.getCell(row, col), v);
36902 // combo's with 'displayField and name set
36903 if (ed.field.displayField && ed.field.name) {
36904 ed.field.el.dom.value = r.data[ed.field.name];
36908 }).defer(50, this);
36914 * Stops any active editing
36916 stopEditing : function(){
36917 if(this.activeEditor){
36918 this.activeEditor.completeEdit();
36920 this.activeEditor = null;
36924 * Called to get grid's drag proxy text, by default returns this.ddText.
36927 getDragDropText : function(){
36928 var count = this.selModel.getSelectedCell() ? 1 : 0;
36929 return String.format(this.ddText, count, count == 1 ? '' : 's');
36934 * Ext JS Library 1.1.1
36935 * Copyright(c) 2006-2007, Ext JS, LLC.
36937 * Originally Released Under LGPL - original licence link has changed is not relivant.
36940 * <script type="text/javascript">
36943 // private - not really -- you end up using it !
36944 // This is a support class used internally by the Grid components
36947 * @class Roo.grid.GridEditor
36948 * @extends Roo.Editor
36949 * Class for creating and editable grid elements.
36950 * @param {Object} config any settings (must include field)
36952 Roo.grid.GridEditor = function(field, config){
36953 if (!config && field.field) {
36955 field = Roo.factory(config.field, Roo.form);
36957 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36958 field.monitorTab = false;
36961 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36964 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36967 alignment: "tl-tl",
36970 cls: "x-small-editor x-grid-editor",
36975 * Ext JS Library 1.1.1
36976 * Copyright(c) 2006-2007, Ext JS, LLC.
36978 * Originally Released Under LGPL - original licence link has changed is not relivant.
36981 * <script type="text/javascript">
36986 Roo.grid.PropertyRecord = Roo.data.Record.create([
36987 {name:'name',type:'string'}, 'value'
36991 Roo.grid.PropertyStore = function(grid, source){
36993 this.store = new Roo.data.Store({
36994 recordType : Roo.grid.PropertyRecord
36996 this.store.on('update', this.onUpdate, this);
36998 this.setSource(source);
37000 Roo.grid.PropertyStore.superclass.constructor.call(this);
37005 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37006 setSource : function(o){
37008 this.store.removeAll();
37011 if(this.isEditableValue(o[k])){
37012 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37015 this.store.loadRecords({records: data}, {}, true);
37018 onUpdate : function(ds, record, type){
37019 if(type == Roo.data.Record.EDIT){
37020 var v = record.data['value'];
37021 var oldValue = record.modified['value'];
37022 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37023 this.source[record.id] = v;
37025 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37032 getProperty : function(row){
37033 return this.store.getAt(row);
37036 isEditableValue: function(val){
37037 if(val && val instanceof Date){
37039 }else if(typeof val == 'object' || typeof val == 'function'){
37045 setValue : function(prop, value){
37046 this.source[prop] = value;
37047 this.store.getById(prop).set('value', value);
37050 getSource : function(){
37051 return this.source;
37055 Roo.grid.PropertyColumnModel = function(grid, store){
37058 g.PropertyColumnModel.superclass.constructor.call(this, [
37059 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37060 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37062 this.store = store;
37063 this.bselect = Roo.DomHelper.append(document.body, {
37064 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37065 {tag: 'option', value: 'true', html: 'true'},
37066 {tag: 'option', value: 'false', html: 'false'}
37069 Roo.id(this.bselect);
37072 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37073 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37074 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37075 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37076 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37078 this.renderCellDelegate = this.renderCell.createDelegate(this);
37079 this.renderPropDelegate = this.renderProp.createDelegate(this);
37082 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37086 valueText : 'Value',
37088 dateFormat : 'm/j/Y',
37091 renderDate : function(dateVal){
37092 return dateVal.dateFormat(this.dateFormat);
37095 renderBool : function(bVal){
37096 return bVal ? 'true' : 'false';
37099 isCellEditable : function(colIndex, rowIndex){
37100 return colIndex == 1;
37103 getRenderer : function(col){
37105 this.renderCellDelegate : this.renderPropDelegate;
37108 renderProp : function(v){
37109 return this.getPropertyName(v);
37112 renderCell : function(val){
37114 if(val instanceof Date){
37115 rv = this.renderDate(val);
37116 }else if(typeof val == 'boolean'){
37117 rv = this.renderBool(val);
37119 return Roo.util.Format.htmlEncode(rv);
37122 getPropertyName : function(name){
37123 var pn = this.grid.propertyNames;
37124 return pn && pn[name] ? pn[name] : name;
37127 getCellEditor : function(colIndex, rowIndex){
37128 var p = this.store.getProperty(rowIndex);
37129 var n = p.data['name'], val = p.data['value'];
37131 if(typeof(this.grid.customEditors[n]) == 'string'){
37132 return this.editors[this.grid.customEditors[n]];
37134 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37135 return this.grid.customEditors[n];
37137 if(val instanceof Date){
37138 return this.editors['date'];
37139 }else if(typeof val == 'number'){
37140 return this.editors['number'];
37141 }else if(typeof val == 'boolean'){
37142 return this.editors['boolean'];
37144 return this.editors['string'];
37150 * @class Roo.grid.PropertyGrid
37151 * @extends Roo.grid.EditorGrid
37152 * This class represents the interface of a component based property grid control.
37153 * <br><br>Usage:<pre><code>
37154 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37162 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37163 * The container MUST have some type of size defined for the grid to fill. The container will be
37164 * automatically set to position relative if it isn't already.
37165 * @param {Object} config A config object that sets properties on this grid.
37167 Roo.grid.PropertyGrid = function(container, config){
37168 config = config || {};
37169 var store = new Roo.grid.PropertyStore(this);
37170 this.store = store;
37171 var cm = new Roo.grid.PropertyColumnModel(this, store);
37172 store.store.sort('name', 'ASC');
37173 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37176 enableColLock:false,
37177 enableColumnMove:false,
37179 trackMouseOver: false,
37182 this.getGridEl().addClass('x-props-grid');
37183 this.lastEditRow = null;
37184 this.on('columnresize', this.onColumnResize, this);
37187 * @event beforepropertychange
37188 * Fires before a property changes (return false to stop?)
37189 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37190 * @param {String} id Record Id
37191 * @param {String} newval New Value
37192 * @param {String} oldval Old Value
37194 "beforepropertychange": true,
37196 * @event propertychange
37197 * Fires after a property changes
37198 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37199 * @param {String} id Record Id
37200 * @param {String} newval New Value
37201 * @param {String} oldval Old Value
37203 "propertychange": true
37205 this.customEditors = this.customEditors || {};
37207 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37210 * @cfg {Object} customEditors map of colnames=> custom editors.
37211 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37212 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37213 * false disables editing of the field.
37217 * @cfg {Object} propertyNames map of property Names to their displayed value
37220 render : function(){
37221 Roo.grid.PropertyGrid.superclass.render.call(this);
37222 this.autoSize.defer(100, this);
37225 autoSize : function(){
37226 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37228 this.view.fitColumns();
37232 onColumnResize : function(){
37233 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37237 * Sets the data for the Grid
37238 * accepts a Key => Value object of all the elements avaiable.
37239 * @param {Object} data to appear in grid.
37241 setSource : function(source){
37242 this.store.setSource(source);
37246 * Gets all the data from the grid.
37247 * @return {Object} data data stored in grid
37249 getSource : function(){
37250 return this.store.getSource();
37259 * @class Roo.grid.Calendar
37260 * @extends Roo.util.Grid
37261 * This class extends the Grid to provide a calendar widget
37262 * <br><br>Usage:<pre><code>
37263 var grid = new Roo.grid.Calendar("my-container-id", {
37266 selModel: mySelectionModel,
37267 autoSizeColumns: true,
37268 monitorWindowResize: false,
37269 trackMouseOver: true
37270 eventstore : real data store..
37276 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37277 * The container MUST have some type of size defined for the grid to fill. The container will be
37278 * automatically set to position relative if it isn't already.
37279 * @param {Object} config A config object that sets properties on this grid.
37281 Roo.grid.Calendar = function(container, config){
37282 // initialize the container
37283 this.container = Roo.get(container);
37284 this.container.update("");
37285 this.container.setStyle("overflow", "hidden");
37286 this.container.addClass('x-grid-container');
37288 this.id = this.container.id;
37290 Roo.apply(this, config);
37291 // check and correct shorthanded configs
37295 for (var r = 0;r < 6;r++) {
37298 for (var c =0;c < 7;c++) {
37302 if (this.eventStore) {
37303 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37304 this.eventStore.on('load',this.onLoad, this);
37305 this.eventStore.on('beforeload',this.clearEvents, this);
37309 this.dataSource = new Roo.data.Store({
37310 proxy: new Roo.data.MemoryProxy(rows),
37311 reader: new Roo.data.ArrayReader({}, [
37312 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37315 this.dataSource.load();
37316 this.ds = this.dataSource;
37317 this.ds.xmodule = this.xmodule || false;
37320 var cellRender = function(v,x,r)
37322 return String.format(
37323 '<div class="fc-day fc-widget-content"><div>' +
37324 '<div class="fc-event-container"></div>' +
37325 '<div class="fc-day-number">{0}</div>'+
37327 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37328 '</div></div>', v);
37333 this.colModel = new Roo.grid.ColumnModel( [
37335 xtype: 'ColumnModel',
37337 dataIndex : 'weekday0',
37339 renderer : cellRender
37342 xtype: 'ColumnModel',
37344 dataIndex : 'weekday1',
37346 renderer : cellRender
37349 xtype: 'ColumnModel',
37351 dataIndex : 'weekday2',
37352 header : 'Tuesday',
37353 renderer : cellRender
37356 xtype: 'ColumnModel',
37358 dataIndex : 'weekday3',
37359 header : 'Wednesday',
37360 renderer : cellRender
37363 xtype: 'ColumnModel',
37365 dataIndex : 'weekday4',
37366 header : 'Thursday',
37367 renderer : cellRender
37370 xtype: 'ColumnModel',
37372 dataIndex : 'weekday5',
37374 renderer : cellRender
37377 xtype: 'ColumnModel',
37379 dataIndex : 'weekday6',
37380 header : 'Saturday',
37381 renderer : cellRender
37384 this.cm = this.colModel;
37385 this.cm.xmodule = this.xmodule || false;
37389 //this.selModel = new Roo.grid.CellSelectionModel();
37390 //this.sm = this.selModel;
37391 //this.selModel.init(this);
37395 this.container.setWidth(this.width);
37399 this.container.setHeight(this.height);
37406 * The raw click event for the entire grid.
37407 * @param {Roo.EventObject} e
37412 * The raw dblclick event for the entire grid.
37413 * @param {Roo.EventObject} e
37417 * @event contextmenu
37418 * The raw contextmenu event for the entire grid.
37419 * @param {Roo.EventObject} e
37421 "contextmenu" : true,
37424 * The raw mousedown event for the entire grid.
37425 * @param {Roo.EventObject} e
37427 "mousedown" : true,
37430 * The raw mouseup event for the entire grid.
37431 * @param {Roo.EventObject} e
37436 * The raw mouseover event for the entire grid.
37437 * @param {Roo.EventObject} e
37439 "mouseover" : true,
37442 * The raw mouseout event for the entire grid.
37443 * @param {Roo.EventObject} e
37448 * The raw keypress event for the entire grid.
37449 * @param {Roo.EventObject} e
37454 * The raw keydown event for the entire grid.
37455 * @param {Roo.EventObject} e
37463 * Fires when a cell is clicked
37464 * @param {Grid} this
37465 * @param {Number} rowIndex
37466 * @param {Number} columnIndex
37467 * @param {Roo.EventObject} e
37469 "cellclick" : true,
37471 * @event celldblclick
37472 * Fires when a cell is double clicked
37473 * @param {Grid} this
37474 * @param {Number} rowIndex
37475 * @param {Number} columnIndex
37476 * @param {Roo.EventObject} e
37478 "celldblclick" : true,
37481 * Fires when a row is clicked
37482 * @param {Grid} this
37483 * @param {Number} rowIndex
37484 * @param {Roo.EventObject} e
37488 * @event rowdblclick
37489 * Fires when a row is double clicked
37490 * @param {Grid} this
37491 * @param {Number} rowIndex
37492 * @param {Roo.EventObject} e
37494 "rowdblclick" : true,
37496 * @event headerclick
37497 * Fires when a header is clicked
37498 * @param {Grid} this
37499 * @param {Number} columnIndex
37500 * @param {Roo.EventObject} e
37502 "headerclick" : true,
37504 * @event headerdblclick
37505 * Fires when a header cell is double clicked
37506 * @param {Grid} this
37507 * @param {Number} columnIndex
37508 * @param {Roo.EventObject} e
37510 "headerdblclick" : true,
37512 * @event rowcontextmenu
37513 * Fires when a row is right clicked
37514 * @param {Grid} this
37515 * @param {Number} rowIndex
37516 * @param {Roo.EventObject} e
37518 "rowcontextmenu" : true,
37520 * @event cellcontextmenu
37521 * Fires when a cell is right clicked
37522 * @param {Grid} this
37523 * @param {Number} rowIndex
37524 * @param {Number} cellIndex
37525 * @param {Roo.EventObject} e
37527 "cellcontextmenu" : true,
37529 * @event headercontextmenu
37530 * Fires when a header is right clicked
37531 * @param {Grid} this
37532 * @param {Number} columnIndex
37533 * @param {Roo.EventObject} e
37535 "headercontextmenu" : true,
37537 * @event bodyscroll
37538 * Fires when the body element is scrolled
37539 * @param {Number} scrollLeft
37540 * @param {Number} scrollTop
37542 "bodyscroll" : true,
37544 * @event columnresize
37545 * Fires when the user resizes a column
37546 * @param {Number} columnIndex
37547 * @param {Number} newSize
37549 "columnresize" : true,
37551 * @event columnmove
37552 * Fires when the user moves a column
37553 * @param {Number} oldIndex
37554 * @param {Number} newIndex
37556 "columnmove" : true,
37559 * Fires when row(s) start being dragged
37560 * @param {Grid} this
37561 * @param {Roo.GridDD} dd The drag drop object
37562 * @param {event} e The raw browser event
37564 "startdrag" : true,
37567 * Fires when a drag operation is complete
37568 * @param {Grid} this
37569 * @param {Roo.GridDD} dd The drag drop object
37570 * @param {event} e The raw browser event
37575 * Fires when dragged row(s) are dropped on a valid DD target
37576 * @param {Grid} this
37577 * @param {Roo.GridDD} dd The drag drop object
37578 * @param {String} targetId The target drag drop object
37579 * @param {event} e The raw browser event
37584 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37585 * @param {Grid} this
37586 * @param {Roo.GridDD} dd The drag drop object
37587 * @param {String} targetId The target drag drop object
37588 * @param {event} e The raw browser event
37593 * Fires when the dragged row(s) first cross another DD target while being dragged
37594 * @param {Grid} this
37595 * @param {Roo.GridDD} dd The drag drop object
37596 * @param {String} targetId The target drag drop object
37597 * @param {event} e The raw browser event
37599 "dragenter" : true,
37602 * Fires when the dragged row(s) leave another DD target while being dragged
37603 * @param {Grid} this
37604 * @param {Roo.GridDD} dd The drag drop object
37605 * @param {String} targetId The target drag drop object
37606 * @param {event} e The raw browser event
37611 * Fires when a row is rendered, so you can change add a style to it.
37612 * @param {GridView} gridview The grid view
37613 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37619 * Fires when the grid is rendered
37620 * @param {Grid} grid
37625 * Fires when a date is selected
37626 * @param {DatePicker} this
37627 * @param {Date} date The selected date
37631 * @event monthchange
37632 * Fires when the displayed month changes
37633 * @param {DatePicker} this
37634 * @param {Date} date The selected month
37636 'monthchange': true,
37638 * @event evententer
37639 * Fires when mouse over an event
37640 * @param {Calendar} this
37641 * @param {event} Event
37643 'evententer': true,
37645 * @event eventleave
37646 * Fires when the mouse leaves an
37647 * @param {Calendar} this
37650 'eventleave': true,
37652 * @event eventclick
37653 * Fires when the mouse click an
37654 * @param {Calendar} this
37657 'eventclick': true,
37659 * @event eventrender
37660 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37661 * @param {Calendar} this
37662 * @param {data} data to be modified
37664 'eventrender': true
37668 Roo.grid.Grid.superclass.constructor.call(this);
37669 this.on('render', function() {
37670 this.view.el.addClass('x-grid-cal');
37672 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37676 if (!Roo.grid.Calendar.style) {
37677 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37680 '.x-grid-cal .x-grid-col' : {
37681 height: 'auto !important',
37682 'vertical-align': 'top'
37684 '.x-grid-cal .fc-event-hori' : {
37695 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37697 * @cfg {Store} eventStore The store that loads events.
37702 activeDate : false,
37705 monitorWindowResize : false,
37708 resizeColumns : function() {
37709 var col = (this.view.el.getWidth() / 7) - 3;
37710 // loop through cols, and setWidth
37711 for(var i =0 ; i < 7 ; i++){
37712 this.cm.setColumnWidth(i, col);
37715 setDate :function(date) {
37717 Roo.log('setDate?');
37719 this.resizeColumns();
37720 var vd = this.activeDate;
37721 this.activeDate = date;
37722 // if(vd && this.el){
37723 // var t = date.getTime();
37724 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37725 // Roo.log('using add remove');
37727 // this.fireEvent('monthchange', this, date);
37729 // this.cells.removeClass("fc-state-highlight");
37730 // this.cells.each(function(c){
37731 // if(c.dateValue == t){
37732 // c.addClass("fc-state-highlight");
37733 // setTimeout(function(){
37734 // try{c.dom.firstChild.focus();}catch(e){}
37744 var days = date.getDaysInMonth();
37746 var firstOfMonth = date.getFirstDateOfMonth();
37747 var startingPos = firstOfMonth.getDay()-this.startDay;
37749 if(startingPos < this.startDay){
37753 var pm = date.add(Date.MONTH, -1);
37754 var prevStart = pm.getDaysInMonth()-startingPos;
37758 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37760 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37761 //this.cells.addClassOnOver('fc-state-hover');
37763 var cells = this.cells.elements;
37764 var textEls = this.textNodes;
37766 //Roo.each(cells, function(cell){
37767 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37770 days += startingPos;
37772 // convert everything to numbers so it's fast
37773 var day = 86400000;
37774 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37777 //Roo.log(prevStart);
37779 var today = new Date().clearTime().getTime();
37780 var sel = date.clearTime().getTime();
37781 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37782 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37783 var ddMatch = this.disabledDatesRE;
37784 var ddText = this.disabledDatesText;
37785 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37786 var ddaysText = this.disabledDaysText;
37787 var format = this.format;
37789 var setCellClass = function(cal, cell){
37791 //Roo.log('set Cell Class');
37793 var t = d.getTime();
37798 cell.dateValue = t;
37800 cell.className += " fc-today";
37801 cell.className += " fc-state-highlight";
37802 cell.title = cal.todayText;
37805 // disable highlight in other month..
37806 cell.className += " fc-state-highlight";
37811 //cell.className = " fc-state-disabled";
37812 cell.title = cal.minText;
37816 //cell.className = " fc-state-disabled";
37817 cell.title = cal.maxText;
37821 if(ddays.indexOf(d.getDay()) != -1){
37822 // cell.title = ddaysText;
37823 // cell.className = " fc-state-disabled";
37826 if(ddMatch && format){
37827 var fvalue = d.dateFormat(format);
37828 if(ddMatch.test(fvalue)){
37829 cell.title = ddText.replace("%0", fvalue);
37830 cell.className = " fc-state-disabled";
37834 if (!cell.initialClassName) {
37835 cell.initialClassName = cell.dom.className;
37838 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37843 for(; i < startingPos; i++) {
37844 cells[i].dayName = (++prevStart);
37845 Roo.log(textEls[i]);
37846 d.setDate(d.getDate()+1);
37848 //cells[i].className = "fc-past fc-other-month";
37849 setCellClass(this, cells[i]);
37854 for(; i < days; i++){
37855 intDay = i - startingPos + 1;
37856 cells[i].dayName = (intDay);
37857 d.setDate(d.getDate()+1);
37859 cells[i].className = ''; // "x-date-active";
37860 setCellClass(this, cells[i]);
37864 for(; i < 42; i++) {
37865 //textEls[i].innerHTML = (++extraDays);
37867 d.setDate(d.getDate()+1);
37868 cells[i].dayName = (++extraDays);
37869 cells[i].className = "fc-future fc-other-month";
37870 setCellClass(this, cells[i]);
37873 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37875 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37877 // this will cause all the cells to mis
37880 for (var r = 0;r < 6;r++) {
37881 for (var c =0;c < 7;c++) {
37882 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37886 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37887 for(i=0;i<cells.length;i++) {
37889 this.cells.elements[i].dayName = cells[i].dayName ;
37890 this.cells.elements[i].className = cells[i].className;
37891 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37892 this.cells.elements[i].title = cells[i].title ;
37893 this.cells.elements[i].dateValue = cells[i].dateValue ;
37899 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37900 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37902 ////if(totalRows != 6){
37903 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37904 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37907 this.fireEvent('monthchange', this, date);
37912 * Returns the grid's SelectionModel.
37913 * @return {SelectionModel}
37915 getSelectionModel : function(){
37916 if(!this.selModel){
37917 this.selModel = new Roo.grid.CellSelectionModel();
37919 return this.selModel;
37923 this.eventStore.load()
37929 findCell : function(dt) {
37930 dt = dt.clearTime().getTime();
37932 this.cells.each(function(c){
37933 //Roo.log("check " +c.dateValue + '?=' + dt);
37934 if(c.dateValue == dt){
37944 findCells : function(rec) {
37945 var s = rec.data.start_dt.clone().clearTime().getTime();
37947 var e= rec.data.end_dt.clone().clearTime().getTime();
37950 this.cells.each(function(c){
37951 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37953 if(c.dateValue > e){
37956 if(c.dateValue < s){
37965 findBestRow: function(cells)
37969 for (var i =0 ; i < cells.length;i++) {
37970 ret = Math.max(cells[i].rows || 0,ret);
37977 addItem : function(rec)
37979 // look for vertical location slot in
37980 var cells = this.findCells(rec);
37982 rec.row = this.findBestRow(cells);
37984 // work out the location.
37988 for(var i =0; i < cells.length; i++) {
37996 if (crow.start.getY() == cells[i].getY()) {
37998 crow.end = cells[i];
38014 for (var i = 0; i < cells.length;i++) {
38015 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38022 clearEvents: function() {
38024 if (!this.eventStore.getCount()) {
38027 // reset number of rows in cells.
38028 Roo.each(this.cells.elements, function(c){
38032 this.eventStore.each(function(e) {
38033 this.clearEvent(e);
38038 clearEvent : function(ev)
38041 Roo.each(ev.els, function(el) {
38042 el.un('mouseenter' ,this.onEventEnter, this);
38043 el.un('mouseleave' ,this.onEventLeave, this);
38051 renderEvent : function(ev,ctr) {
38053 ctr = this.view.el.select('.fc-event-container',true).first();
38057 this.clearEvent(ev);
38063 var cells = ev.cells;
38064 var rows = ev.rows;
38065 this.fireEvent('eventrender', this, ev);
38067 for(var i =0; i < rows.length; i++) {
38071 cls += ' fc-event-start';
38073 if ((i+1) == rows.length) {
38074 cls += ' fc-event-end';
38077 //Roo.log(ev.data);
38078 // how many rows should it span..
38079 var cg = this.eventTmpl.append(ctr,Roo.apply({
38082 }, ev.data) , true);
38085 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38086 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38087 cg.on('click', this.onEventClick, this, ev);
38091 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38092 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38095 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38096 cg.setWidth(ebox.right - sbox.x -2);
38100 renderEvents: function()
38102 // first make sure there is enough space..
38104 if (!this.eventTmpl) {
38105 this.eventTmpl = new Roo.Template(
38106 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38107 '<div class="fc-event-inner">' +
38108 '<span class="fc-event-time">{time}</span>' +
38109 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38111 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38119 this.cells.each(function(c) {
38120 //Roo.log(c.select('.fc-day-content div',true).first());
38121 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38124 var ctr = this.view.el.select('.fc-event-container',true).first();
38127 this.eventStore.each(function(ev){
38129 this.renderEvent(ev);
38133 this.view.layout();
38137 onEventEnter: function (e, el,event,d) {
38138 this.fireEvent('evententer', this, el, event);
38141 onEventLeave: function (e, el,event,d) {
38142 this.fireEvent('eventleave', this, el, event);
38145 onEventClick: function (e, el,event,d) {
38146 this.fireEvent('eventclick', this, el, event);
38149 onMonthChange: function () {
38153 onLoad: function () {
38155 //Roo.log('calendar onload');
38157 if(this.eventStore.getCount() > 0){
38161 this.eventStore.each(function(d){
38166 if (typeof(add.end_dt) == 'undefined') {
38167 Roo.log("Missing End time in calendar data: ");
38171 if (typeof(add.start_dt) == 'undefined') {
38172 Roo.log("Missing Start time in calendar data: ");
38176 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38177 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38178 add.id = add.id || d.id;
38179 add.title = add.title || '??';
38187 this.renderEvents();
38197 render : function ()
38201 if (!this.view.el.hasClass('course-timesheet')) {
38202 this.view.el.addClass('course-timesheet');
38204 if (this.tsStyle) {
38209 Roo.log(_this.grid.view.el.getWidth());
38212 this.tsStyle = Roo.util.CSS.createStyleSheet({
38213 '.course-timesheet .x-grid-row' : {
38216 '.x-grid-row td' : {
38217 'vertical-align' : 0
38219 '.course-edit-link' : {
38221 'text-overflow' : 'ellipsis',
38222 'overflow' : 'hidden',
38223 'white-space' : 'nowrap',
38224 'cursor' : 'pointer'
38229 '.de-act-sup-link' : {
38230 'color' : 'purple',
38231 'text-decoration' : 'line-through'
38235 'text-decoration' : 'line-through'
38237 '.course-timesheet .course-highlight' : {
38238 'border-top-style': 'dashed !important',
38239 'border-bottom-bottom': 'dashed !important'
38241 '.course-timesheet .course-item' : {
38242 'font-family' : 'tahoma, arial, helvetica',
38243 'font-size' : '11px',
38244 'overflow' : 'hidden',
38245 'padding-left' : '10px',
38246 'padding-right' : '10px',
38247 'padding-top' : '10px'
38255 monitorWindowResize : false,
38256 cellrenderer : function(v,x,r)
38261 xtype: 'CellSelectionModel',
38268 beforeload : function (_self, options)
38270 options.params = options.params || {};
38271 options.params._month = _this.monthField.getValue();
38272 options.params.limit = 9999;
38273 options.params['sort'] = 'when_dt';
38274 options.params['dir'] = 'ASC';
38275 this.proxy.loadResponse = this.loadResponse;
38277 //this.addColumns();
38279 load : function (_self, records, options)
38281 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38282 // if you click on the translation.. you can edit it...
38283 var el = Roo.get(this);
38284 var id = el.dom.getAttribute('data-id');
38285 var d = el.dom.getAttribute('data-date');
38286 var t = el.dom.getAttribute('data-time');
38287 //var id = this.child('span').dom.textContent;
38290 Pman.Dialog.CourseCalendar.show({
38294 productitem_active : id ? 1 : 0
38296 _this.grid.ds.load({});
38301 _this.panel.fireEvent('resize', [ '', '' ]);
38304 loadResponse : function(o, success, response){
38305 // this is overridden on before load..
38307 Roo.log("our code?");
38308 //Roo.log(success);
38309 //Roo.log(response)
38310 delete this.activeRequest;
38312 this.fireEvent("loadexception", this, o, response);
38313 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38318 result = o.reader.read(response);
38320 Roo.log("load exception?");
38321 this.fireEvent("loadexception", this, o, response, e);
38322 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38325 Roo.log("ready...");
38326 // loop through result.records;
38327 // and set this.tdate[date] = [] << array of records..
38329 Roo.each(result.records, function(r){
38331 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38332 _this.tdata[r.data.when_dt.format('j')] = [];
38334 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38337 //Roo.log(_this.tdata);
38339 result.records = [];
38340 result.totalRecords = 6;
38342 // let's generate some duumy records for the rows.
38343 //var st = _this.dateField.getValue();
38345 // work out monday..
38346 //st = st.add(Date.DAY, -1 * st.format('w'));
38348 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38350 var firstOfMonth = date.getFirstDayOfMonth();
38351 var days = date.getDaysInMonth();
38353 var firstAdded = false;
38354 for (var i = 0; i < result.totalRecords ; i++) {
38355 //var d= st.add(Date.DAY, i);
38358 for(var w = 0 ; w < 7 ; w++){
38359 if(!firstAdded && firstOfMonth != w){
38366 var dd = (d > 0 && d < 10) ? "0"+d : d;
38367 row['weekday'+w] = String.format(
38368 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38369 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38371 date.format('Y-m-')+dd
38374 if(typeof(_this.tdata[d]) != 'undefined'){
38375 Roo.each(_this.tdata[d], function(r){
38379 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38380 if(r.parent_id*1>0){
38381 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38384 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38385 deactive = 'de-act-link';
38388 row['weekday'+w] += String.format(
38389 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38391 r.product_id_name, //1
38392 r.when_dt.format('h:ia'), //2
38402 // only do this if something added..
38404 result.records.push(_this.grid.dataSource.reader.newRow(row));
38408 // push it twice. (second one with an hour..
38412 this.fireEvent("load", this, o, o.request.arg);
38413 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38415 sortInfo : {field: 'when_dt', direction : 'ASC' },
38417 xtype: 'HttpProxy',
38420 url : baseURL + '/Roo/Shop_course.php'
38423 xtype: 'JsonReader',
38440 'name': 'parent_id',
38444 'name': 'product_id',
38448 'name': 'productitem_id',
38466 click : function (_self, e)
38468 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38469 sd.setMonth(sd.getMonth()-1);
38470 _this.monthField.setValue(sd.format('Y-m-d'));
38471 _this.grid.ds.load({});
38477 xtype: 'Separator',
38481 xtype: 'MonthField',
38484 render : function (_self)
38486 _this.monthField = _self;
38487 // _this.monthField.set today
38489 select : function (combo, date)
38491 _this.grid.ds.load({});
38494 value : (function() { return new Date(); })()
38497 xtype: 'Separator',
38503 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38513 click : function (_self, e)
38515 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38516 sd.setMonth(sd.getMonth()+1);
38517 _this.monthField.setValue(sd.format('Y-m-d'));
38518 _this.grid.ds.load({});
38531 * Ext JS Library 1.1.1
38532 * Copyright(c) 2006-2007, Ext JS, LLC.
38534 * Originally Released Under LGPL - original licence link has changed is not relivant.
38537 * <script type="text/javascript">
38541 * @class Roo.LoadMask
38542 * A simple utility class for generically masking elements while loading data. If the element being masked has
38543 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38544 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38545 * element's UpdateManager load indicator and will be destroyed after the initial load.
38547 * Create a new LoadMask
38548 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38549 * @param {Object} config The config object
38551 Roo.LoadMask = function(el, config){
38552 this.el = Roo.get(el);
38553 Roo.apply(this, config);
38555 this.store.on('beforeload', this.onBeforeLoad, this);
38556 this.store.on('load', this.onLoad, this);
38557 this.store.on('loadexception', this.onLoadException, this);
38558 this.removeMask = false;
38560 var um = this.el.getUpdateManager();
38561 um.showLoadIndicator = false; // disable the default indicator
38562 um.on('beforeupdate', this.onBeforeLoad, this);
38563 um.on('update', this.onLoad, this);
38564 um.on('failure', this.onLoad, this);
38565 this.removeMask = true;
38569 Roo.LoadMask.prototype = {
38571 * @cfg {Boolean} removeMask
38572 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38573 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38576 * @cfg {String} msg
38577 * The text to display in a centered loading message box (defaults to 'Loading...')
38579 msg : 'Loading...',
38581 * @cfg {String} msgCls
38582 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38584 msgCls : 'x-mask-loading',
38587 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38593 * Disables the mask to prevent it from being displayed
38595 disable : function(){
38596 this.disabled = true;
38600 * Enables the mask so that it can be displayed
38602 enable : function(){
38603 this.disabled = false;
38606 onLoadException : function()
38608 Roo.log(arguments);
38610 if (typeof(arguments[3]) != 'undefined') {
38611 Roo.MessageBox.alert("Error loading",arguments[3]);
38615 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38616 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38623 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38626 onLoad : function()
38628 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38632 onBeforeLoad : function(){
38633 if(!this.disabled){
38634 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38639 destroy : function(){
38641 this.store.un('beforeload', this.onBeforeLoad, this);
38642 this.store.un('load', this.onLoad, this);
38643 this.store.un('loadexception', this.onLoadException, this);
38645 var um = this.el.getUpdateManager();
38646 um.un('beforeupdate', this.onBeforeLoad, this);
38647 um.un('update', this.onLoad, this);
38648 um.un('failure', this.onLoad, this);
38653 * Ext JS Library 1.1.1
38654 * Copyright(c) 2006-2007, Ext JS, LLC.
38656 * Originally Released Under LGPL - original licence link has changed is not relivant.
38659 * <script type="text/javascript">
38664 * @class Roo.XTemplate
38665 * @extends Roo.Template
38666 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38668 var t = new Roo.XTemplate(
38669 '<select name="{name}">',
38670 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38674 // then append, applying the master template values
38677 * Supported features:
38682 {a_variable} - output encoded.
38683 {a_variable.format:("Y-m-d")} - call a method on the variable
38684 {a_variable:raw} - unencoded output
38685 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38686 {a_variable:this.method_on_template(...)} - call a method on the template object.
38691 <tpl for="a_variable or condition.."></tpl>
38692 <tpl if="a_variable or condition"></tpl>
38693 <tpl exec="some javascript"></tpl>
38694 <tpl name="named_template"></tpl> (experimental)
38696 <tpl for="."></tpl> - just iterate the property..
38697 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38701 Roo.XTemplate = function()
38703 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38710 Roo.extend(Roo.XTemplate, Roo.Template, {
38713 * The various sub templates
38718 * basic tag replacing syntax
38721 * // you can fake an object call by doing this
38725 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38728 * compile the template
38730 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38733 compile: function()
38737 s = ['<tpl>', s, '</tpl>'].join('');
38739 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38740 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38741 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38742 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38743 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38748 while(true == !!(m = s.match(re))){
38749 var forMatch = m[0].match(nameRe),
38750 ifMatch = m[0].match(ifRe),
38751 execMatch = m[0].match(execRe),
38752 namedMatch = m[0].match(namedRe),
38757 name = forMatch && forMatch[1] ? forMatch[1] : '';
38760 // if - puts fn into test..
38761 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38763 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38768 // exec - calls a function... returns empty if true is returned.
38769 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38771 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38779 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38780 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38781 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38784 var uid = namedMatch ? namedMatch[1] : id;
38788 id: namedMatch ? namedMatch[1] : id,
38795 s = s.replace(m[0], '');
38797 s = s.replace(m[0], '{xtpl'+ id + '}');
38802 for(var i = tpls.length-1; i >= 0; --i){
38803 this.compileTpl(tpls[i]);
38804 this.tpls[tpls[i].id] = tpls[i];
38806 this.master = tpls[tpls.length-1];
38810 * same as applyTemplate, except it's done to one of the subTemplates
38811 * when using named templates, you can do:
38813 * var str = pl.applySubTemplate('your-name', values);
38816 * @param {Number} id of the template
38817 * @param {Object} values to apply to template
38818 * @param {Object} parent (normaly the instance of this object)
38820 applySubTemplate : function(id, values, parent)
38824 var t = this.tpls[id];
38828 if(t.test && !t.test.call(this, values, parent)){
38832 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38833 Roo.log(e.toString());
38839 if(t.exec && t.exec.call(this, values, parent)){
38843 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38844 Roo.log(e.toString());
38849 var vs = t.target ? t.target.call(this, values, parent) : values;
38850 parent = t.target ? values : parent;
38851 if(t.target && vs instanceof Array){
38853 for(var i = 0, len = vs.length; i < len; i++){
38854 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38856 return buf.join('');
38858 return t.compiled.call(this, vs, parent);
38860 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38861 Roo.log(e.toString());
38862 Roo.log(t.compiled);
38867 compileTpl : function(tpl)
38869 var fm = Roo.util.Format;
38870 var useF = this.disableFormats !== true;
38871 var sep = Roo.isGecko ? "+" : ",";
38872 var undef = function(str) {
38873 Roo.log("Property not found :" + str);
38877 var fn = function(m, name, format, args)
38879 //Roo.log(arguments);
38880 args = args ? args.replace(/\\'/g,"'") : args;
38881 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38882 if (typeof(format) == 'undefined') {
38883 format= 'htmlEncode';
38885 if (format == 'raw' ) {
38889 if(name.substr(0, 4) == 'xtpl'){
38890 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38893 // build an array of options to determine if value is undefined..
38895 // basically get 'xxxx.yyyy' then do
38896 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38897 // (function () { Roo.log("Property not found"); return ''; })() :
38902 Roo.each(name.split('.'), function(st) {
38903 lookfor += (lookfor.length ? '.': '') + st;
38904 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38907 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38910 if(format && useF){
38912 args = args ? ',' + args : "";
38914 if(format.substr(0, 5) != "this."){
38915 format = "fm." + format + '(';
38917 format = 'this.call("'+ format.substr(5) + '", ';
38921 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38925 // called with xxyx.yuu:(test,test)
38927 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38929 // raw.. - :raw modifier..
38930 return "'"+ sep + udef_st + name + ")"+sep+"'";
38934 // branched to use + in gecko and [].join() in others
38936 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38937 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38940 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38941 body.push(tpl.body.replace(/(\r\n|\n)/g,
38942 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38943 body.push("'].join('');};};");
38944 body = body.join('');
38947 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38949 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38955 applyTemplate : function(values){
38956 return this.master.compiled.call(this, values, {});
38957 //var s = this.subs;
38960 apply : function(){
38961 return this.applyTemplate.apply(this, arguments);
38966 Roo.XTemplate.from = function(el){
38967 el = Roo.getDom(el);
38968 return new Roo.XTemplate(el.value || el.innerHTML);