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 this.stores[opts.list+1].loadData( [] );
20492 var rec = view.store.getAt(ix[0]);
20493 this.setFromData(rec.data);
20495 var lw = Math.floor(
20496 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20498 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20499 var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20500 this.stores[opts.list+1].loadData( data );
20501 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20502 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20503 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20504 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20506 onDoubleClick : function()
20508 this.collapse(); //??
20513 findRecord : function (prop,value)
20515 return this.findRecordInStore(this.store, prop,value);
20519 findRecordInStore : function(store, prop, value)
20521 var cstore = new Roo.data.SimpleStore({
20522 //fields : this.store.reader.meta.fields, // we need array reader.. for
20523 reader : this.store.reader,
20527 var record = false;
20528 if(store.getCount() > 0){
20529 store.each(function(r){
20530 if(r.data[prop] == value){
20534 if (r.data.cn && r.data.cn.length) {
20535 cstore.loadData( r.data.cn);
20536 var cret = _this.findRecordInStore(cstore, prop, value);
20537 if (cret !== false) {
20554 * Ext JS Library 1.1.1
20555 * Copyright(c) 2006-2007, Ext JS, LLC.
20557 * Originally Released Under LGPL - original licence link has changed is not relivant.
20560 * <script type="text/javascript">
20563 * @class Roo.form.Checkbox
20564 * @extends Roo.form.Field
20565 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20567 * Creates a new Checkbox
20568 * @param {Object} config Configuration options
20570 Roo.form.Checkbox = function(config){
20571 Roo.form.Checkbox.superclass.constructor.call(this, config);
20575 * Fires when the checkbox is checked or unchecked.
20576 * @param {Roo.form.Checkbox} this This checkbox
20577 * @param {Boolean} checked The new checked value
20583 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20585 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20587 focusClass : undefined,
20589 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20591 fieldClass: "x-form-field",
20593 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20597 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20598 * {tag: "input", type: "checkbox", autocomplete: "off"})
20600 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20602 * @cfg {String} boxLabel The text that appears beside the checkbox
20606 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20610 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20612 valueOff: '0', // value when not checked..
20614 actionMode : 'viewEl',
20617 itemCls : 'x-menu-check-item x-form-item',
20618 groupClass : 'x-menu-group-item',
20619 inputType : 'hidden',
20622 inSetChecked: false, // check that we are not calling self...
20624 inputElement: false, // real input element?
20625 basedOn: false, // ????
20627 isFormField: true, // not sure where this is needed!!!!
20629 onResize : function(){
20630 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20631 if(!this.boxLabel){
20632 this.el.alignTo(this.wrap, 'c-c');
20636 initEvents : function(){
20637 Roo.form.Checkbox.superclass.initEvents.call(this);
20638 this.el.on("click", this.onClick, this);
20639 this.el.on("change", this.onClick, this);
20643 getResizeEl : function(){
20647 getPositionEl : function(){
20652 onRender : function(ct, position){
20653 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20655 if(this.inputValue !== undefined){
20656 this.el.dom.value = this.inputValue;
20659 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20660 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20661 var viewEl = this.wrap.createChild({
20662 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20663 this.viewEl = viewEl;
20664 this.wrap.on('click', this.onClick, this);
20666 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20667 this.el.on('propertychange', this.setFromHidden, this); //ie
20672 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20673 // viewEl.on('click', this.onClick, this);
20675 //if(this.checked){
20676 this.setChecked(this.checked);
20678 //this.checked = this.el.dom;
20684 initValue : Roo.emptyFn,
20687 * Returns the checked state of the checkbox.
20688 * @return {Boolean} True if checked, else false
20690 getValue : function(){
20692 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20694 return this.valueOff;
20699 onClick : function(){
20700 if (this.disabled) {
20703 this.setChecked(!this.checked);
20705 //if(this.el.dom.checked != this.checked){
20706 // this.setValue(this.el.dom.checked);
20711 * Sets the checked state of the checkbox.
20712 * On is always based on a string comparison between inputValue and the param.
20713 * @param {Boolean/String} value - the value to set
20714 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20716 setValue : function(v,suppressEvent){
20719 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20720 //if(this.el && this.el.dom){
20721 // this.el.dom.checked = this.checked;
20722 // this.el.dom.defaultChecked = this.checked;
20724 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20725 //this.fireEvent("check", this, this.checked);
20728 setChecked : function(state,suppressEvent)
20730 if (this.inSetChecked) {
20731 this.checked = state;
20737 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20739 this.checked = state;
20740 if(suppressEvent !== true){
20741 this.fireEvent('check', this, state);
20743 this.inSetChecked = true;
20744 this.el.dom.value = state ? this.inputValue : this.valueOff;
20745 this.inSetChecked = false;
20748 // handle setting of hidden value by some other method!!?!?
20749 setFromHidden: function()
20754 //console.log("SET FROM HIDDEN");
20755 //alert('setFrom hidden');
20756 this.setValue(this.el.dom.value);
20759 onDestroy : function()
20762 Roo.get(this.viewEl).remove();
20765 Roo.form.Checkbox.superclass.onDestroy.call(this);
20768 setBoxLabel : function(str)
20770 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20775 * Ext JS Library 1.1.1
20776 * Copyright(c) 2006-2007, Ext JS, LLC.
20778 * Originally Released Under LGPL - original licence link has changed is not relivant.
20781 * <script type="text/javascript">
20785 * @class Roo.form.Radio
20786 * @extends Roo.form.Checkbox
20787 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20788 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20790 * Creates a new Radio
20791 * @param {Object} config Configuration options
20793 Roo.form.Radio = function(){
20794 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20796 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20797 inputType: 'radio',
20800 * If this radio is part of a group, it will return the selected value
20803 getGroupValue : function(){
20804 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20808 onRender : function(ct, position){
20809 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20811 if(this.inputValue !== undefined){
20812 this.el.dom.value = this.inputValue;
20815 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20816 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20817 //var viewEl = this.wrap.createChild({
20818 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20819 //this.viewEl = viewEl;
20820 //this.wrap.on('click', this.onClick, this);
20822 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20823 //this.el.on('propertychange', this.setFromHidden, this); //ie
20828 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20829 // viewEl.on('click', this.onClick, this);
20832 this.el.dom.checked = 'checked' ;
20838 });//<script type="text/javascript">
20841 * Based Ext JS Library 1.1.1
20842 * Copyright(c) 2006-2007, Ext JS, LLC.
20848 * @class Roo.HtmlEditorCore
20849 * @extends Roo.Component
20850 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20852 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20855 Roo.HtmlEditorCore = function(config){
20858 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20863 * @event initialize
20864 * Fires when the editor is fully initialized (including the iframe)
20865 * @param {Roo.HtmlEditorCore} this
20870 * Fires when the editor is first receives the focus. Any insertion must wait
20871 * until after this event.
20872 * @param {Roo.HtmlEditorCore} this
20876 * @event beforesync
20877 * Fires before the textarea is updated with content from the editor iframe. Return false
20878 * to cancel the sync.
20879 * @param {Roo.HtmlEditorCore} this
20880 * @param {String} html
20884 * @event beforepush
20885 * Fires before the iframe editor is updated with content from the textarea. Return false
20886 * to cancel the push.
20887 * @param {Roo.HtmlEditorCore} this
20888 * @param {String} html
20893 * Fires when the textarea is updated with content from the editor iframe.
20894 * @param {Roo.HtmlEditorCore} this
20895 * @param {String} html
20900 * Fires when the iframe editor is updated with content from the textarea.
20901 * @param {Roo.HtmlEditorCore} this
20902 * @param {String} html
20907 * @event editorevent
20908 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20909 * @param {Roo.HtmlEditorCore} this
20915 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20917 // defaults : white / black...
20918 this.applyBlacklists();
20925 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20929 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20935 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20940 * @cfg {Number} height (in pixels)
20944 * @cfg {Number} width (in pixels)
20949 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20952 stylesheets: false,
20957 // private properties
20958 validationEvent : false,
20960 initialized : false,
20962 sourceEditMode : false,
20963 onFocus : Roo.emptyFn,
20965 hideMode:'offsets',
20969 // blacklist + whitelisted elements..
20976 * Protected method that will not generally be called directly. It
20977 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20978 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20980 getDocMarkup : function(){
20984 // inherit styels from page...??
20985 if (this.stylesheets === false) {
20987 Roo.get(document.head).select('style').each(function(node) {
20988 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20991 Roo.get(document.head).select('link').each(function(node) {
20992 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20995 } else if (!this.stylesheets.length) {
20997 st = '<style type="text/css">' +
20998 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21001 st = '<style type="text/css">' +
21006 st += '<style type="text/css">' +
21007 'IMG { cursor: pointer } ' +
21010 var cls = 'roo-htmleditor-body';
21012 if(this.bodyCls.length){
21013 cls += ' ' + this.bodyCls;
21016 return '<html><head>' + st +
21017 //<style type="text/css">' +
21018 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21020 ' </head><body class="' + cls + '"></body></html>';
21024 onRender : function(ct, position)
21027 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21028 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21031 this.el.dom.style.border = '0 none';
21032 this.el.dom.setAttribute('tabIndex', -1);
21033 this.el.addClass('x-hidden hide');
21037 if(Roo.isIE){ // fix IE 1px bogus margin
21038 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21042 this.frameId = Roo.id();
21046 var iframe = this.owner.wrap.createChild({
21048 cls: 'form-control', // bootstrap..
21050 name: this.frameId,
21051 frameBorder : 'no',
21052 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21057 this.iframe = iframe.dom;
21059 this.assignDocWin();
21061 this.doc.designMode = 'on';
21064 this.doc.write(this.getDocMarkup());
21068 var task = { // must defer to wait for browser to be ready
21070 //console.log("run task?" + this.doc.readyState);
21071 this.assignDocWin();
21072 if(this.doc.body || this.doc.readyState == 'complete'){
21074 this.doc.designMode="on";
21078 Roo.TaskMgr.stop(task);
21079 this.initEditor.defer(10, this);
21086 Roo.TaskMgr.start(task);
21091 onResize : function(w, h)
21093 Roo.log('resize: ' +w + ',' + h );
21094 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21098 if(typeof w == 'number'){
21100 this.iframe.style.width = w + 'px';
21102 if(typeof h == 'number'){
21104 this.iframe.style.height = h + 'px';
21106 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21113 * Toggles the editor between standard and source edit mode.
21114 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21116 toggleSourceEdit : function(sourceEditMode){
21118 this.sourceEditMode = sourceEditMode === true;
21120 if(this.sourceEditMode){
21122 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21125 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21126 //this.iframe.className = '';
21129 //this.setSize(this.owner.wrap.getSize());
21130 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21137 * Protected method that will not generally be called directly. If you need/want
21138 * custom HTML cleanup, this is the method you should override.
21139 * @param {String} html The HTML to be cleaned
21140 * return {String} The cleaned HTML
21142 cleanHtml : function(html){
21143 html = String(html);
21144 if(html.length > 5){
21145 if(Roo.isSafari){ // strip safari nonsense
21146 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21149 if(html == ' '){
21156 * HTML Editor -> Textarea
21157 * Protected method that will not generally be called directly. Syncs the contents
21158 * of the editor iframe with the textarea.
21160 syncValue : function(){
21161 if(this.initialized){
21162 var bd = (this.doc.body || this.doc.documentElement);
21163 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21164 var html = bd.innerHTML;
21166 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21167 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21169 html = '<div style="'+m[0]+'">' + html + '</div>';
21172 html = this.cleanHtml(html);
21173 // fix up the special chars.. normaly like back quotes in word...
21174 // however we do not want to do this with chinese..
21175 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21177 var cc = match.charCodeAt();
21179 // Get the character value, handling surrogate pairs
21180 if (match.length == 2) {
21181 // It's a surrogate pair, calculate the Unicode code point
21182 var high = match.charCodeAt(0) - 0xD800;
21183 var low = match.charCodeAt(1) - 0xDC00;
21184 cc = (high * 0x400) + low + 0x10000;
21186 (cc >= 0x4E00 && cc < 0xA000 ) ||
21187 (cc >= 0x3400 && cc < 0x4E00 ) ||
21188 (cc >= 0xf900 && cc < 0xfb00 )
21193 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21194 return "&#" + cc + ";";
21201 if(this.owner.fireEvent('beforesync', this, html) !== false){
21202 this.el.dom.value = html;
21203 this.owner.fireEvent('sync', this, html);
21209 * Protected method that will not generally be called directly. Pushes the value of the textarea
21210 * into the iframe editor.
21212 pushValue : function(){
21213 if(this.initialized){
21214 var v = this.el.dom.value.trim();
21216 // if(v.length < 1){
21220 if(this.owner.fireEvent('beforepush', this, v) !== false){
21221 var d = (this.doc.body || this.doc.documentElement);
21223 this.cleanUpPaste();
21224 this.el.dom.value = d.innerHTML;
21225 this.owner.fireEvent('push', this, v);
21231 deferFocus : function(){
21232 this.focus.defer(10, this);
21236 focus : function(){
21237 if(this.win && !this.sourceEditMode){
21244 assignDocWin: function()
21246 var iframe = this.iframe;
21249 this.doc = iframe.contentWindow.document;
21250 this.win = iframe.contentWindow;
21252 // if (!Roo.get(this.frameId)) {
21255 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21256 // this.win = Roo.get(this.frameId).dom.contentWindow;
21258 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21262 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21263 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21268 initEditor : function(){
21269 //console.log("INIT EDITOR");
21270 this.assignDocWin();
21274 this.doc.designMode="on";
21276 this.doc.write(this.getDocMarkup());
21279 var dbody = (this.doc.body || this.doc.documentElement);
21280 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21281 // this copies styles from the containing element into thsi one..
21282 // not sure why we need all of this..
21283 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21285 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21286 //ss['background-attachment'] = 'fixed'; // w3c
21287 dbody.bgProperties = 'fixed'; // ie
21288 //Roo.DomHelper.applyStyles(dbody, ss);
21289 Roo.EventManager.on(this.doc, {
21290 //'mousedown': this.onEditorEvent,
21291 'mouseup': this.onEditorEvent,
21292 'dblclick': this.onEditorEvent,
21293 'click': this.onEditorEvent,
21294 'keyup': this.onEditorEvent,
21299 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21301 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21302 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21304 this.initialized = true;
21306 this.owner.fireEvent('initialize', this);
21311 onDestroy : function(){
21317 //for (var i =0; i < this.toolbars.length;i++) {
21318 // // fixme - ask toolbars for heights?
21319 // this.toolbars[i].onDestroy();
21322 //this.wrap.dom.innerHTML = '';
21323 //this.wrap.remove();
21328 onFirstFocus : function(){
21330 this.assignDocWin();
21333 this.activated = true;
21336 if(Roo.isGecko){ // prevent silly gecko errors
21338 var s = this.win.getSelection();
21339 if(!s.focusNode || s.focusNode.nodeType != 3){
21340 var r = s.getRangeAt(0);
21341 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21346 this.execCmd('useCSS', true);
21347 this.execCmd('styleWithCSS', false);
21350 this.owner.fireEvent('activate', this);
21354 adjustFont: function(btn){
21355 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21356 //if(Roo.isSafari){ // safari
21359 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21360 if(Roo.isSafari){ // safari
21361 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21362 v = (v < 10) ? 10 : v;
21363 v = (v > 48) ? 48 : v;
21364 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21369 v = Math.max(1, v+adjust);
21371 this.execCmd('FontSize', v );
21374 onEditorEvent : function(e)
21376 this.owner.fireEvent('editorevent', this, e);
21377 // this.updateToolbar();
21378 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21381 insertTag : function(tg)
21383 // could be a bit smarter... -> wrap the current selected tRoo..
21384 if (tg.toLowerCase() == 'span' ||
21385 tg.toLowerCase() == 'code' ||
21386 tg.toLowerCase() == 'sup' ||
21387 tg.toLowerCase() == 'sub'
21390 range = this.createRange(this.getSelection());
21391 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21392 wrappingNode.appendChild(range.extractContents());
21393 range.insertNode(wrappingNode);
21400 this.execCmd("formatblock", tg);
21404 insertText : function(txt)
21408 var range = this.createRange();
21409 range.deleteContents();
21410 //alert(Sender.getAttribute('label'));
21412 range.insertNode(this.doc.createTextNode(txt));
21418 * Executes a Midas editor command on the editor document and performs necessary focus and
21419 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21420 * @param {String} cmd The Midas command
21421 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21423 relayCmd : function(cmd, value){
21425 this.execCmd(cmd, value);
21426 this.owner.fireEvent('editorevent', this);
21427 //this.updateToolbar();
21428 this.owner.deferFocus();
21432 * Executes a Midas editor command directly on the editor document.
21433 * For visual commands, you should use {@link #relayCmd} instead.
21434 * <b>This should only be called after the editor is initialized.</b>
21435 * @param {String} cmd The Midas command
21436 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21438 execCmd : function(cmd, value){
21439 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21446 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21448 * @param {String} text | dom node..
21450 insertAtCursor : function(text)
21453 if(!this.activated){
21459 var r = this.doc.selection.createRange();
21470 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21474 // from jquery ui (MIT licenced)
21476 var win = this.win;
21478 if (win.getSelection && win.getSelection().getRangeAt) {
21479 range = win.getSelection().getRangeAt(0);
21480 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21481 range.insertNode(node);
21482 } else if (win.document.selection && win.document.selection.createRange) {
21483 // no firefox support
21484 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21485 win.document.selection.createRange().pasteHTML(txt);
21487 // no firefox support
21488 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21489 this.execCmd('InsertHTML', txt);
21498 mozKeyPress : function(e){
21500 var c = e.getCharCode(), cmd;
21503 c = String.fromCharCode(c).toLowerCase();
21517 this.cleanUpPaste.defer(100, this);
21525 e.preventDefault();
21533 fixKeys : function(){ // load time branching for fastest keydown performance
21535 return function(e){
21536 var k = e.getKey(), r;
21539 r = this.doc.selection.createRange();
21542 r.pasteHTML('    ');
21549 r = this.doc.selection.createRange();
21551 var target = r.parentElement();
21552 if(!target || target.tagName.toLowerCase() != 'li'){
21554 r.pasteHTML('<br />');
21560 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21561 this.cleanUpPaste.defer(100, this);
21567 }else if(Roo.isOpera){
21568 return function(e){
21569 var k = e.getKey();
21573 this.execCmd('InsertHTML','    ');
21576 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21577 this.cleanUpPaste.defer(100, this);
21582 }else if(Roo.isSafari){
21583 return function(e){
21584 var k = e.getKey();
21588 this.execCmd('InsertText','\t');
21592 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21593 this.cleanUpPaste.defer(100, this);
21601 getAllAncestors: function()
21603 var p = this.getSelectedNode();
21606 a.push(p); // push blank onto stack..
21607 p = this.getParentElement();
21611 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21615 a.push(this.doc.body);
21619 lastSelNode : false,
21622 getSelection : function()
21624 this.assignDocWin();
21625 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21628 getSelectedNode: function()
21630 // this may only work on Gecko!!!
21632 // should we cache this!!!!
21637 var range = this.createRange(this.getSelection()).cloneRange();
21640 var parent = range.parentElement();
21642 var testRange = range.duplicate();
21643 testRange.moveToElementText(parent);
21644 if (testRange.inRange(range)) {
21647 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21650 parent = parent.parentElement;
21655 // is ancestor a text element.
21656 var ac = range.commonAncestorContainer;
21657 if (ac.nodeType == 3) {
21658 ac = ac.parentNode;
21661 var ar = ac.childNodes;
21664 var other_nodes = [];
21665 var has_other_nodes = false;
21666 for (var i=0;i<ar.length;i++) {
21667 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21670 // fullly contained node.
21672 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21677 // probably selected..
21678 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21679 other_nodes.push(ar[i]);
21683 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21688 has_other_nodes = true;
21690 if (!nodes.length && other_nodes.length) {
21691 nodes= other_nodes;
21693 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21699 createRange: function(sel)
21701 // this has strange effects when using with
21702 // top toolbar - not sure if it's a great idea.
21703 //this.editor.contentWindow.focus();
21704 if (typeof sel != "undefined") {
21706 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21708 return this.doc.createRange();
21711 return this.doc.createRange();
21714 getParentElement: function()
21717 this.assignDocWin();
21718 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21720 var range = this.createRange(sel);
21723 var p = range.commonAncestorContainer;
21724 while (p.nodeType == 3) { // text node
21735 * Range intersection.. the hard stuff...
21739 * [ -- selected range --- ]
21743 * if end is before start or hits it. fail.
21744 * if start is after end or hits it fail.
21746 * if either hits (but other is outside. - then it's not
21752 // @see http://www.thismuchiknow.co.uk/?p=64.
21753 rangeIntersectsNode : function(range, node)
21755 var nodeRange = node.ownerDocument.createRange();
21757 nodeRange.selectNode(node);
21759 nodeRange.selectNodeContents(node);
21762 var rangeStartRange = range.cloneRange();
21763 rangeStartRange.collapse(true);
21765 var rangeEndRange = range.cloneRange();
21766 rangeEndRange.collapse(false);
21768 var nodeStartRange = nodeRange.cloneRange();
21769 nodeStartRange.collapse(true);
21771 var nodeEndRange = nodeRange.cloneRange();
21772 nodeEndRange.collapse(false);
21774 return rangeStartRange.compareBoundaryPoints(
21775 Range.START_TO_START, nodeEndRange) == -1 &&
21776 rangeEndRange.compareBoundaryPoints(
21777 Range.START_TO_START, nodeStartRange) == 1;
21781 rangeCompareNode : function(range, node)
21783 var nodeRange = node.ownerDocument.createRange();
21785 nodeRange.selectNode(node);
21787 nodeRange.selectNodeContents(node);
21791 range.collapse(true);
21793 nodeRange.collapse(true);
21795 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21796 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21798 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21800 var nodeIsBefore = ss == 1;
21801 var nodeIsAfter = ee == -1;
21803 if (nodeIsBefore && nodeIsAfter) {
21806 if (!nodeIsBefore && nodeIsAfter) {
21807 return 1; //right trailed.
21810 if (nodeIsBefore && !nodeIsAfter) {
21811 return 2; // left trailed.
21817 // private? - in a new class?
21818 cleanUpPaste : function()
21820 // cleans up the whole document..
21821 Roo.log('cleanuppaste');
21823 this.cleanUpChildren(this.doc.body);
21824 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21825 if (clean != this.doc.body.innerHTML) {
21826 this.doc.body.innerHTML = clean;
21831 cleanWordChars : function(input) {// change the chars to hex code
21832 var he = Roo.HtmlEditorCore;
21834 var output = input;
21835 Roo.each(he.swapCodes, function(sw) {
21836 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21838 output = output.replace(swapper, sw[1]);
21845 cleanUpChildren : function (n)
21847 if (!n.childNodes.length) {
21850 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21851 this.cleanUpChild(n.childNodes[i]);
21858 cleanUpChild : function (node)
21861 //console.log(node);
21862 if (node.nodeName == "#text") {
21863 // clean up silly Windows -- stuff?
21866 if (node.nodeName == "#comment") {
21867 node.parentNode.removeChild(node);
21868 // clean up silly Windows -- stuff?
21871 var lcname = node.tagName.toLowerCase();
21872 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21873 // whitelist of tags..
21875 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21877 node.parentNode.removeChild(node);
21882 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21884 // spans with no attributes - just remove them..
21885 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21886 remove_keep_children = true;
21889 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21890 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21892 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21893 // remove_keep_children = true;
21896 if (remove_keep_children) {
21897 this.cleanUpChildren(node);
21898 // inserts everything just before this node...
21899 while (node.childNodes.length) {
21900 var cn = node.childNodes[0];
21901 node.removeChild(cn);
21902 node.parentNode.insertBefore(cn, node);
21904 node.parentNode.removeChild(node);
21908 if (!node.attributes || !node.attributes.length) {
21913 this.cleanUpChildren(node);
21917 function cleanAttr(n,v)
21920 if (v.match(/^\./) || v.match(/^\//)) {
21923 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21926 if (v.match(/^#/)) {
21929 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21930 node.removeAttribute(n);
21934 var cwhite = this.cwhite;
21935 var cblack = this.cblack;
21937 function cleanStyle(n,v)
21939 if (v.match(/expression/)) { //XSS?? should we even bother..
21940 node.removeAttribute(n);
21944 var parts = v.split(/;/);
21947 Roo.each(parts, function(p) {
21948 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21952 var l = p.split(':').shift().replace(/\s+/g,'');
21953 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21955 if ( cwhite.length && cblack.indexOf(l) > -1) {
21956 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21957 //node.removeAttribute(n);
21961 // only allow 'c whitelisted system attributes'
21962 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21963 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21964 //node.removeAttribute(n);
21974 if (clean.length) {
21975 node.setAttribute(n, clean.join(';'));
21977 node.removeAttribute(n);
21983 for (var i = node.attributes.length-1; i > -1 ; i--) {
21984 var a = node.attributes[i];
21987 if (a.name.toLowerCase().substr(0,2)=='on') {
21988 node.removeAttribute(a.name);
21991 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21992 node.removeAttribute(a.name);
21995 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21996 cleanAttr(a.name,a.value); // fixme..
21999 if (a.name == 'style') {
22000 cleanStyle(a.name,a.value);
22003 /// clean up MS crap..
22004 // tecnically this should be a list of valid class'es..
22007 if (a.name == 'class') {
22008 if (a.value.match(/^Mso/)) {
22009 node.removeAttribute('class');
22012 if (a.value.match(/^body$/)) {
22013 node.removeAttribute('class');
22024 this.cleanUpChildren(node);
22030 * Clean up MS wordisms...
22032 cleanWord : function(node)
22035 this.cleanWord(this.doc.body);
22040 node.nodeName == 'SPAN' &&
22041 !node.hasAttributes() &&
22042 node.childNodes.length == 1 &&
22043 node.firstChild.nodeName == "#text"
22045 var textNode = node.firstChild;
22046 node.removeChild(textNode);
22047 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22048 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22050 node.parentNode.insertBefore(textNode, node);
22051 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22052 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22054 node.parentNode.removeChild(node);
22057 if (node.nodeName == "#text") {
22058 // clean up silly Windows -- stuff?
22061 if (node.nodeName == "#comment") {
22062 node.parentNode.removeChild(node);
22063 // clean up silly Windows -- stuff?
22067 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22068 node.parentNode.removeChild(node);
22071 //Roo.log(node.tagName);
22072 // remove - but keep children..
22073 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22074 //Roo.log('-- removed');
22075 while (node.childNodes.length) {
22076 var cn = node.childNodes[0];
22077 node.removeChild(cn);
22078 node.parentNode.insertBefore(cn, node);
22079 // move node to parent - and clean it..
22080 this.cleanWord(cn);
22082 node.parentNode.removeChild(node);
22083 /// no need to iterate chidlren = it's got none..
22084 //this.iterateChildren(node, this.cleanWord);
22088 if (node.className.length) {
22090 var cn = node.className.split(/\W+/);
22092 Roo.each(cn, function(cls) {
22093 if (cls.match(/Mso[a-zA-Z]+/)) {
22098 node.className = cna.length ? cna.join(' ') : '';
22100 node.removeAttribute("class");
22104 if (node.hasAttribute("lang")) {
22105 node.removeAttribute("lang");
22108 if (node.hasAttribute("style")) {
22110 var styles = node.getAttribute("style").split(";");
22112 Roo.each(styles, function(s) {
22113 if (!s.match(/:/)) {
22116 var kv = s.split(":");
22117 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22120 // what ever is left... we allow.
22123 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22124 if (!nstyle.length) {
22125 node.removeAttribute('style');
22128 this.iterateChildren(node, this.cleanWord);
22134 * iterateChildren of a Node, calling fn each time, using this as the scole..
22135 * @param {DomNode} node node to iterate children of.
22136 * @param {Function} fn method of this class to call on each item.
22138 iterateChildren : function(node, fn)
22140 if (!node.childNodes.length) {
22143 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22144 fn.call(this, node.childNodes[i])
22150 * cleanTableWidths.
22152 * Quite often pasting from word etc.. results in tables with column and widths.
22153 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22156 cleanTableWidths : function(node)
22161 this.cleanTableWidths(this.doc.body);
22166 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22169 Roo.log(node.tagName);
22170 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22171 this.iterateChildren(node, this.cleanTableWidths);
22174 if (node.hasAttribute('width')) {
22175 node.removeAttribute('width');
22179 if (node.hasAttribute("style")) {
22182 var styles = node.getAttribute("style").split(";");
22184 Roo.each(styles, function(s) {
22185 if (!s.match(/:/)) {
22188 var kv = s.split(":");
22189 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22192 // what ever is left... we allow.
22195 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22196 if (!nstyle.length) {
22197 node.removeAttribute('style');
22201 this.iterateChildren(node, this.cleanTableWidths);
22209 domToHTML : function(currentElement, depth, nopadtext) {
22211 depth = depth || 0;
22212 nopadtext = nopadtext || false;
22214 if (!currentElement) {
22215 return this.domToHTML(this.doc.body);
22218 //Roo.log(currentElement);
22220 var allText = false;
22221 var nodeName = currentElement.nodeName;
22222 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22224 if (nodeName == '#text') {
22226 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22231 if (nodeName != 'BODY') {
22234 // Prints the node tagName, such as <A>, <IMG>, etc
22237 for(i = 0; i < currentElement.attributes.length;i++) {
22239 var aname = currentElement.attributes.item(i).name;
22240 if (!currentElement.attributes.item(i).value.length) {
22243 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22246 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22255 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22258 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22263 // Traverse the tree
22265 var currentElementChild = currentElement.childNodes.item(i);
22266 var allText = true;
22267 var innerHTML = '';
22269 while (currentElementChild) {
22270 // Formatting code (indent the tree so it looks nice on the screen)
22271 var nopad = nopadtext;
22272 if (lastnode == 'SPAN') {
22276 if (currentElementChild.nodeName == '#text') {
22277 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22278 toadd = nopadtext ? toadd : toadd.trim();
22279 if (!nopad && toadd.length > 80) {
22280 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22282 innerHTML += toadd;
22285 currentElementChild = currentElement.childNodes.item(i);
22291 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22293 // Recursively traverse the tree structure of the child node
22294 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22295 lastnode = currentElementChild.nodeName;
22297 currentElementChild=currentElement.childNodes.item(i);
22303 // The remaining code is mostly for formatting the tree
22304 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22309 ret+= "</"+tagName+">";
22315 applyBlacklists : function()
22317 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22318 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22322 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22323 if (b.indexOf(tag) > -1) {
22326 this.white.push(tag);
22330 Roo.each(w, function(tag) {
22331 if (b.indexOf(tag) > -1) {
22334 if (this.white.indexOf(tag) > -1) {
22337 this.white.push(tag);
22342 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22343 if (w.indexOf(tag) > -1) {
22346 this.black.push(tag);
22350 Roo.each(b, function(tag) {
22351 if (w.indexOf(tag) > -1) {
22354 if (this.black.indexOf(tag) > -1) {
22357 this.black.push(tag);
22362 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22363 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22367 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22368 if (b.indexOf(tag) > -1) {
22371 this.cwhite.push(tag);
22375 Roo.each(w, function(tag) {
22376 if (b.indexOf(tag) > -1) {
22379 if (this.cwhite.indexOf(tag) > -1) {
22382 this.cwhite.push(tag);
22387 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22388 if (w.indexOf(tag) > -1) {
22391 this.cblack.push(tag);
22395 Roo.each(b, function(tag) {
22396 if (w.indexOf(tag) > -1) {
22399 if (this.cblack.indexOf(tag) > -1) {
22402 this.cblack.push(tag);
22407 setStylesheets : function(stylesheets)
22409 if(typeof(stylesheets) == 'string'){
22410 Roo.get(this.iframe.contentDocument.head).createChild({
22412 rel : 'stylesheet',
22421 Roo.each(stylesheets, function(s) {
22426 Roo.get(_this.iframe.contentDocument.head).createChild({
22428 rel : 'stylesheet',
22437 removeStylesheets : function()
22441 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22446 setStyle : function(style)
22448 Roo.get(this.iframe.contentDocument.head).createChild({
22457 // hide stuff that is not compatible
22471 * @event specialkey
22475 * @cfg {String} fieldClass @hide
22478 * @cfg {String} focusClass @hide
22481 * @cfg {String} autoCreate @hide
22484 * @cfg {String} inputType @hide
22487 * @cfg {String} invalidClass @hide
22490 * @cfg {String} invalidText @hide
22493 * @cfg {String} msgFx @hide
22496 * @cfg {String} validateOnBlur @hide
22500 Roo.HtmlEditorCore.white = [
22501 'area', 'br', 'img', 'input', 'hr', 'wbr',
22503 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22504 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22505 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22506 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22507 'table', 'ul', 'xmp',
22509 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22512 'dir', 'menu', 'ol', 'ul', 'dl',
22518 Roo.HtmlEditorCore.black = [
22519 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22521 'base', 'basefont', 'bgsound', 'blink', 'body',
22522 'frame', 'frameset', 'head', 'html', 'ilayer',
22523 'iframe', 'layer', 'link', 'meta', 'object',
22524 'script', 'style' ,'title', 'xml' // clean later..
22526 Roo.HtmlEditorCore.clean = [
22527 'script', 'style', 'title', 'xml'
22529 Roo.HtmlEditorCore.remove = [
22534 Roo.HtmlEditorCore.ablack = [
22538 Roo.HtmlEditorCore.aclean = [
22539 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22543 Roo.HtmlEditorCore.pwhite= [
22544 'http', 'https', 'mailto'
22547 // white listed style attributes.
22548 Roo.HtmlEditorCore.cwhite= [
22549 // 'text-align', /// default is to allow most things..
22555 // black listed style attributes.
22556 Roo.HtmlEditorCore.cblack= [
22557 // 'font-size' -- this can be set by the project
22561 Roo.HtmlEditorCore.swapCodes =[
22572 //<script type="text/javascript">
22575 * Ext JS Library 1.1.1
22576 * Copyright(c) 2006-2007, Ext JS, LLC.
22582 Roo.form.HtmlEditor = function(config){
22586 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22588 if (!this.toolbars) {
22589 this.toolbars = [];
22591 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22597 * @class Roo.form.HtmlEditor
22598 * @extends Roo.form.Field
22599 * Provides a lightweight HTML Editor component.
22601 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22603 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22604 * supported by this editor.</b><br/><br/>
22605 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22606 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22608 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22610 * @cfg {Boolean} clearUp
22614 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22619 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22624 * @cfg {Number} height (in pixels)
22628 * @cfg {Number} width (in pixels)
22633 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22636 stylesheets: false,
22640 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22645 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22651 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22656 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22664 // private properties
22665 validationEvent : false,
22667 initialized : false,
22670 onFocus : Roo.emptyFn,
22672 hideMode:'offsets',
22674 actionMode : 'container', // defaults to hiding it...
22676 defaultAutoCreate : { // modified by initCompnoent..
22678 style:"width:500px;height:300px;",
22679 autocomplete: "new-password"
22683 initComponent : function(){
22686 * @event initialize
22687 * Fires when the editor is fully initialized (including the iframe)
22688 * @param {HtmlEditor} this
22693 * Fires when the editor is first receives the focus. Any insertion must wait
22694 * until after this event.
22695 * @param {HtmlEditor} this
22699 * @event beforesync
22700 * Fires before the textarea is updated with content from the editor iframe. Return false
22701 * to cancel the sync.
22702 * @param {HtmlEditor} this
22703 * @param {String} html
22707 * @event beforepush
22708 * Fires before the iframe editor is updated with content from the textarea. Return false
22709 * to cancel the push.
22710 * @param {HtmlEditor} this
22711 * @param {String} html
22716 * Fires when the textarea is updated with content from the editor iframe.
22717 * @param {HtmlEditor} this
22718 * @param {String} html
22723 * Fires when the iframe editor is updated with content from the textarea.
22724 * @param {HtmlEditor} this
22725 * @param {String} html
22729 * @event editmodechange
22730 * Fires when the editor switches edit modes
22731 * @param {HtmlEditor} this
22732 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22734 editmodechange: true,
22736 * @event editorevent
22737 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22738 * @param {HtmlEditor} this
22742 * @event firstfocus
22743 * Fires when on first focus - needed by toolbars..
22744 * @param {HtmlEditor} this
22749 * Auto save the htmlEditor value as a file into Events
22750 * @param {HtmlEditor} this
22754 * @event savedpreview
22755 * preview the saved version of htmlEditor
22756 * @param {HtmlEditor} this
22758 savedpreview: true,
22761 * @event stylesheetsclick
22762 * Fires when press the Sytlesheets button
22763 * @param {Roo.HtmlEditorCore} this
22765 stylesheetsclick: true
22767 this.defaultAutoCreate = {
22769 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22770 autocomplete: "new-password"
22775 * Protected method that will not generally be called directly. It
22776 * is called when the editor creates its toolbar. Override this method if you need to
22777 * add custom toolbar buttons.
22778 * @param {HtmlEditor} editor
22780 createToolbar : function(editor){
22781 Roo.log("create toolbars");
22782 if (!editor.toolbars || !editor.toolbars.length) {
22783 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22786 for (var i =0 ; i < editor.toolbars.length;i++) {
22787 editor.toolbars[i] = Roo.factory(
22788 typeof(editor.toolbars[i]) == 'string' ?
22789 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22790 Roo.form.HtmlEditor);
22791 editor.toolbars[i].init(editor);
22799 onRender : function(ct, position)
22802 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22804 this.wrap = this.el.wrap({
22805 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22808 this.editorcore.onRender(ct, position);
22810 if (this.resizable) {
22811 this.resizeEl = new Roo.Resizable(this.wrap, {
22815 minHeight : this.height,
22816 height: this.height,
22817 handles : this.resizable,
22820 resize : function(r, w, h) {
22821 _t.onResize(w,h); // -something
22827 this.createToolbar(this);
22831 this.setSize(this.wrap.getSize());
22833 if (this.resizeEl) {
22834 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22835 // should trigger onReize..
22838 this.keyNav = new Roo.KeyNav(this.el, {
22840 "tab" : function(e){
22841 e.preventDefault();
22843 var value = this.getValue();
22845 var start = this.el.dom.selectionStart;
22846 var end = this.el.dom.selectionEnd;
22850 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22851 this.el.dom.setSelectionRange(end + 1, end + 1);
22855 var f = value.substring(0, start).split("\t");
22857 if(f.pop().length != 0){
22861 this.setValue(f.join("\t") + value.substring(end));
22862 this.el.dom.setSelectionRange(start - 1, start - 1);
22866 "home" : function(e){
22867 e.preventDefault();
22869 var curr = this.el.dom.selectionStart;
22870 var lines = this.getValue().split("\n");
22877 this.el.dom.setSelectionRange(0, 0);
22883 for (var i = 0; i < lines.length;i++) {
22884 pos += lines[i].length;
22894 pos -= lines[i].length;
22900 this.el.dom.setSelectionRange(pos, pos);
22904 this.el.dom.selectionStart = pos;
22905 this.el.dom.selectionEnd = curr;
22908 "end" : function(e){
22909 e.preventDefault();
22911 var curr = this.el.dom.selectionStart;
22912 var lines = this.getValue().split("\n");
22919 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22925 for (var i = 0; i < lines.length;i++) {
22927 pos += lines[i].length;
22941 this.el.dom.setSelectionRange(pos, pos);
22945 this.el.dom.selectionStart = curr;
22946 this.el.dom.selectionEnd = pos;
22951 doRelay : function(foo, bar, hname){
22952 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22958 // if(this.autosave && this.w){
22959 // this.autoSaveFn = setInterval(this.autosave, 1000);
22964 onResize : function(w, h)
22966 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22971 if(typeof w == 'number'){
22972 var aw = w - this.wrap.getFrameWidth('lr');
22973 this.el.setWidth(this.adjustWidth('textarea', aw));
22976 if(typeof h == 'number'){
22978 for (var i =0; i < this.toolbars.length;i++) {
22979 // fixme - ask toolbars for heights?
22980 tbh += this.toolbars[i].tb.el.getHeight();
22981 if (this.toolbars[i].footer) {
22982 tbh += this.toolbars[i].footer.el.getHeight();
22989 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22990 ah -= 5; // knock a few pixes off for look..
22992 this.el.setHeight(this.adjustWidth('textarea', ah));
22996 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22997 this.editorcore.onResize(ew,eh);
23002 * Toggles the editor between standard and source edit mode.
23003 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23005 toggleSourceEdit : function(sourceEditMode)
23007 this.editorcore.toggleSourceEdit(sourceEditMode);
23009 if(this.editorcore.sourceEditMode){
23010 Roo.log('editor - showing textarea');
23013 // Roo.log(this.syncValue());
23014 this.editorcore.syncValue();
23015 this.el.removeClass('x-hidden');
23016 this.el.dom.removeAttribute('tabIndex');
23019 for (var i = 0; i < this.toolbars.length; i++) {
23020 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23021 this.toolbars[i].tb.hide();
23022 this.toolbars[i].footer.hide();
23027 Roo.log('editor - hiding textarea');
23029 // Roo.log(this.pushValue());
23030 this.editorcore.pushValue();
23032 this.el.addClass('x-hidden');
23033 this.el.dom.setAttribute('tabIndex', -1);
23035 for (var i = 0; i < this.toolbars.length; i++) {
23036 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23037 this.toolbars[i].tb.show();
23038 this.toolbars[i].footer.show();
23042 //this.deferFocus();
23045 this.setSize(this.wrap.getSize());
23046 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23048 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23051 // private (for BoxComponent)
23052 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23054 // private (for BoxComponent)
23055 getResizeEl : function(){
23059 // private (for BoxComponent)
23060 getPositionEl : function(){
23065 initEvents : function(){
23066 this.originalValue = this.getValue();
23070 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23073 markInvalid : Roo.emptyFn,
23075 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23078 clearInvalid : Roo.emptyFn,
23080 setValue : function(v){
23081 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23082 this.editorcore.pushValue();
23087 deferFocus : function(){
23088 this.focus.defer(10, this);
23092 focus : function(){
23093 this.editorcore.focus();
23099 onDestroy : function(){
23105 for (var i =0; i < this.toolbars.length;i++) {
23106 // fixme - ask toolbars for heights?
23107 this.toolbars[i].onDestroy();
23110 this.wrap.dom.innerHTML = '';
23111 this.wrap.remove();
23116 onFirstFocus : function(){
23117 //Roo.log("onFirstFocus");
23118 this.editorcore.onFirstFocus();
23119 for (var i =0; i < this.toolbars.length;i++) {
23120 this.toolbars[i].onFirstFocus();
23126 syncValue : function()
23128 this.editorcore.syncValue();
23131 pushValue : function()
23133 this.editorcore.pushValue();
23136 setStylesheets : function(stylesheets)
23138 this.editorcore.setStylesheets(stylesheets);
23141 removeStylesheets : function()
23143 this.editorcore.removeStylesheets();
23147 // hide stuff that is not compatible
23161 * @event specialkey
23165 * @cfg {String} fieldClass @hide
23168 * @cfg {String} focusClass @hide
23171 * @cfg {String} autoCreate @hide
23174 * @cfg {String} inputType @hide
23177 * @cfg {String} invalidClass @hide
23180 * @cfg {String} invalidText @hide
23183 * @cfg {String} msgFx @hide
23186 * @cfg {String} validateOnBlur @hide
23190 // <script type="text/javascript">
23193 * Ext JS Library 1.1.1
23194 * Copyright(c) 2006-2007, Ext JS, LLC.
23200 * @class Roo.form.HtmlEditorToolbar1
23205 new Roo.form.HtmlEditor({
23208 new Roo.form.HtmlEditorToolbar1({
23209 disable : { fonts: 1 , format: 1, ..., ... , ...],
23215 * @cfg {Object} disable List of elements to disable..
23216 * @cfg {Array} btns List of additional buttons.
23220 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23223 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23226 Roo.apply(this, config);
23228 // default disabled, based on 'good practice'..
23229 this.disable = this.disable || {};
23230 Roo.applyIf(this.disable, {
23233 specialElements : true
23237 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23238 // dont call parent... till later.
23241 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
23248 editorcore : false,
23250 * @cfg {Object} disable List of toolbar elements to disable
23257 * @cfg {String} createLinkText The default text for the create link prompt
23259 createLinkText : 'Please enter the URL for the link:',
23261 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23263 defaultLinkValue : 'http:/'+'/',
23267 * @cfg {Array} fontFamilies An array of available font families
23285 // "á" , ?? a acute?
23290 "°" // , // degrees
23292 // "é" , // e ecute
23293 // "ú" , // u ecute?
23296 specialElements : [
23298 text: "Insert Table",
23301 ihtml : '<table><tr><td>Cell</td></tr></table>'
23305 text: "Insert Image",
23308 ihtml : '<img src="about:blank"/>'
23317 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23318 "input:submit", "input:button", "select", "textarea", "label" ],
23321 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23323 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23332 * @cfg {String} defaultFont default font to use.
23334 defaultFont: 'tahoma',
23336 fontSelect : false,
23339 formatCombo : false,
23341 init : function(editor)
23343 this.editor = editor;
23344 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23345 var editorcore = this.editorcore;
23349 var fid = editorcore.frameId;
23351 function btn(id, toggle, handler){
23352 var xid = fid + '-'+ id ;
23356 cls : 'x-btn-icon x-edit-'+id,
23357 enableToggle:toggle !== false,
23358 scope: _t, // was editor...
23359 handler:handler||_t.relayBtnCmd,
23360 clickEvent:'mousedown',
23361 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23368 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23370 // stop form submits
23371 tb.el.on('click', function(e){
23372 e.preventDefault(); // what does this do?
23375 if(!this.disable.font) { // && !Roo.isSafari){
23376 /* why no safari for fonts
23377 editor.fontSelect = tb.el.createChild({
23380 cls:'x-font-select',
23381 html: this.createFontOptions()
23384 editor.fontSelect.on('change', function(){
23385 var font = editor.fontSelect.dom.value;
23386 editor.relayCmd('fontname', font);
23387 editor.deferFocus();
23391 editor.fontSelect.dom,
23397 if(!this.disable.formats){
23398 this.formatCombo = new Roo.form.ComboBox({
23399 store: new Roo.data.SimpleStore({
23402 data : this.formats // from states.js
23406 //autoCreate : {tag: "div", size: "20"},
23407 displayField:'tag',
23411 triggerAction: 'all',
23412 emptyText:'Add tag',
23413 selectOnFocus:true,
23416 'select': function(c, r, i) {
23417 editorcore.insertTag(r.get('tag'));
23423 tb.addField(this.formatCombo);
23427 if(!this.disable.format){
23432 btn('strikethrough')
23435 if(!this.disable.fontSize){
23440 btn('increasefontsize', false, editorcore.adjustFont),
23441 btn('decreasefontsize', false, editorcore.adjustFont)
23446 if(!this.disable.colors){
23449 id:editorcore.frameId +'-forecolor',
23450 cls:'x-btn-icon x-edit-forecolor',
23451 clickEvent:'mousedown',
23452 tooltip: this.buttonTips['forecolor'] || undefined,
23454 menu : new Roo.menu.ColorMenu({
23455 allowReselect: true,
23456 focus: Roo.emptyFn,
23459 selectHandler: function(cp, color){
23460 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23461 editor.deferFocus();
23464 clickEvent:'mousedown'
23467 id:editorcore.frameId +'backcolor',
23468 cls:'x-btn-icon x-edit-backcolor',
23469 clickEvent:'mousedown',
23470 tooltip: this.buttonTips['backcolor'] || undefined,
23472 menu : new Roo.menu.ColorMenu({
23473 focus: Roo.emptyFn,
23476 allowReselect: true,
23477 selectHandler: function(cp, color){
23479 editorcore.execCmd('useCSS', false);
23480 editorcore.execCmd('hilitecolor', color);
23481 editorcore.execCmd('useCSS', true);
23482 editor.deferFocus();
23484 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23485 Roo.isSafari || Roo.isIE ? '#'+color : color);
23486 editor.deferFocus();
23490 clickEvent:'mousedown'
23495 // now add all the items...
23498 if(!this.disable.alignments){
23501 btn('justifyleft'),
23502 btn('justifycenter'),
23503 btn('justifyright')
23507 //if(!Roo.isSafari){
23508 if(!this.disable.links){
23511 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23515 if(!this.disable.lists){
23518 btn('insertorderedlist'),
23519 btn('insertunorderedlist')
23522 if(!this.disable.sourceEdit){
23525 btn('sourceedit', true, function(btn){
23526 this.toggleSourceEdit(btn.pressed);
23533 // special menu.. - needs to be tidied up..
23534 if (!this.disable.special) {
23537 cls: 'x-edit-none',
23543 for (var i =0; i < this.specialChars.length; i++) {
23544 smenu.menu.items.push({
23546 html: this.specialChars[i],
23547 handler: function(a,b) {
23548 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23549 //editor.insertAtCursor(a.html);
23563 if (!this.disable.cleanStyles) {
23565 cls: 'x-btn-icon x-btn-clear',
23571 for (var i =0; i < this.cleanStyles.length; i++) {
23572 cmenu.menu.items.push({
23573 actiontype : this.cleanStyles[i],
23574 html: 'Remove ' + this.cleanStyles[i],
23575 handler: function(a,b) {
23578 var c = Roo.get(editorcore.doc.body);
23579 c.select('[style]').each(function(s) {
23580 s.dom.style.removeProperty(a.actiontype);
23582 editorcore.syncValue();
23587 cmenu.menu.items.push({
23588 actiontype : 'tablewidths',
23589 html: 'Remove Table Widths',
23590 handler: function(a,b) {
23591 editorcore.cleanTableWidths();
23592 editorcore.syncValue();
23596 cmenu.menu.items.push({
23597 actiontype : 'word',
23598 html: 'Remove MS Word Formating',
23599 handler: function(a,b) {
23600 editorcore.cleanWord();
23601 editorcore.syncValue();
23606 cmenu.menu.items.push({
23607 actiontype : 'all',
23608 html: 'Remove All Styles',
23609 handler: function(a,b) {
23611 var c = Roo.get(editorcore.doc.body);
23612 c.select('[style]').each(function(s) {
23613 s.dom.removeAttribute('style');
23615 editorcore.syncValue();
23620 cmenu.menu.items.push({
23621 actiontype : 'all',
23622 html: 'Remove All CSS Classes',
23623 handler: function(a,b) {
23625 var c = Roo.get(editorcore.doc.body);
23626 c.select('[class]').each(function(s) {
23627 s.dom.removeAttribute('class');
23629 editorcore.cleanWord();
23630 editorcore.syncValue();
23635 cmenu.menu.items.push({
23636 actiontype : 'tidy',
23637 html: 'Tidy HTML Source',
23638 handler: function(a,b) {
23639 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23640 editorcore.syncValue();
23649 if (!this.disable.specialElements) {
23652 cls: 'x-edit-none',
23657 for (var i =0; i < this.specialElements.length; i++) {
23658 semenu.menu.items.push(
23660 handler: function(a,b) {
23661 editor.insertAtCursor(this.ihtml);
23663 }, this.specialElements[i])
23675 for(var i =0; i< this.btns.length;i++) {
23676 var b = Roo.factory(this.btns[i],Roo.form);
23677 b.cls = 'x-edit-none';
23679 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23680 b.cls += ' x-init-enable';
23683 b.scope = editorcore;
23691 // disable everything...
23693 this.tb.items.each(function(item){
23696 item.id != editorcore.frameId+ '-sourceedit' &&
23697 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23703 this.rendered = true;
23705 // the all the btns;
23706 editor.on('editorevent', this.updateToolbar, this);
23707 // other toolbars need to implement this..
23708 //editor.on('editmodechange', this.updateToolbar, this);
23712 relayBtnCmd : function(btn) {
23713 this.editorcore.relayCmd(btn.cmd);
23715 // private used internally
23716 createLink : function(){
23717 Roo.log("create link?");
23718 var url = prompt(this.createLinkText, this.defaultLinkValue);
23719 if(url && url != 'http:/'+'/'){
23720 this.editorcore.relayCmd('createlink', url);
23726 * Protected method that will not generally be called directly. It triggers
23727 * a toolbar update by reading the markup state of the current selection in the editor.
23729 updateToolbar: function(){
23731 if(!this.editorcore.activated){
23732 this.editor.onFirstFocus();
23736 var btns = this.tb.items.map,
23737 doc = this.editorcore.doc,
23738 frameId = this.editorcore.frameId;
23740 if(!this.disable.font && !Roo.isSafari){
23742 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23743 if(name != this.fontSelect.dom.value){
23744 this.fontSelect.dom.value = name;
23748 if(!this.disable.format){
23749 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23750 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23751 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23752 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23754 if(!this.disable.alignments){
23755 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23756 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23757 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23759 if(!Roo.isSafari && !this.disable.lists){
23760 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23761 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23764 var ans = this.editorcore.getAllAncestors();
23765 if (this.formatCombo) {
23768 var store = this.formatCombo.store;
23769 this.formatCombo.setValue("");
23770 for (var i =0; i < ans.length;i++) {
23771 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23773 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23781 // hides menus... - so this cant be on a menu...
23782 Roo.menu.MenuMgr.hideAll();
23784 //this.editorsyncValue();
23788 createFontOptions : function(){
23789 var buf = [], fs = this.fontFamilies, ff, lc;
23793 for(var i = 0, len = fs.length; i< len; i++){
23795 lc = ff.toLowerCase();
23797 '<option value="',lc,'" style="font-family:',ff,';"',
23798 (this.defaultFont == lc ? ' selected="true">' : '>'),
23803 return buf.join('');
23806 toggleSourceEdit : function(sourceEditMode){
23808 Roo.log("toolbar toogle");
23809 if(sourceEditMode === undefined){
23810 sourceEditMode = !this.sourceEditMode;
23812 this.sourceEditMode = sourceEditMode === true;
23813 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23814 // just toggle the button?
23815 if(btn.pressed !== this.sourceEditMode){
23816 btn.toggle(this.sourceEditMode);
23820 if(sourceEditMode){
23821 Roo.log("disabling buttons");
23822 this.tb.items.each(function(item){
23823 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23829 Roo.log("enabling buttons");
23830 if(this.editorcore.initialized){
23831 this.tb.items.each(function(item){
23837 Roo.log("calling toggole on editor");
23838 // tell the editor that it's been pressed..
23839 this.editor.toggleSourceEdit(sourceEditMode);
23843 * Object collection of toolbar tooltips for the buttons in the editor. The key
23844 * is the command id associated with that button and the value is a valid QuickTips object.
23849 title: 'Bold (Ctrl+B)',
23850 text: 'Make the selected text bold.',
23851 cls: 'x-html-editor-tip'
23854 title: 'Italic (Ctrl+I)',
23855 text: 'Make the selected text italic.',
23856 cls: 'x-html-editor-tip'
23864 title: 'Bold (Ctrl+B)',
23865 text: 'Make the selected text bold.',
23866 cls: 'x-html-editor-tip'
23869 title: 'Italic (Ctrl+I)',
23870 text: 'Make the selected text italic.',
23871 cls: 'x-html-editor-tip'
23874 title: 'Underline (Ctrl+U)',
23875 text: 'Underline the selected text.',
23876 cls: 'x-html-editor-tip'
23879 title: 'Strikethrough',
23880 text: 'Strikethrough the selected text.',
23881 cls: 'x-html-editor-tip'
23883 increasefontsize : {
23884 title: 'Grow Text',
23885 text: 'Increase the font size.',
23886 cls: 'x-html-editor-tip'
23888 decreasefontsize : {
23889 title: 'Shrink Text',
23890 text: 'Decrease the font size.',
23891 cls: 'x-html-editor-tip'
23894 title: 'Text Highlight Color',
23895 text: 'Change the background color of the selected text.',
23896 cls: 'x-html-editor-tip'
23899 title: 'Font Color',
23900 text: 'Change the color of the selected text.',
23901 cls: 'x-html-editor-tip'
23904 title: 'Align Text Left',
23905 text: 'Align text to the left.',
23906 cls: 'x-html-editor-tip'
23909 title: 'Center Text',
23910 text: 'Center text in the editor.',
23911 cls: 'x-html-editor-tip'
23914 title: 'Align Text Right',
23915 text: 'Align text to the right.',
23916 cls: 'x-html-editor-tip'
23918 insertunorderedlist : {
23919 title: 'Bullet List',
23920 text: 'Start a bulleted list.',
23921 cls: 'x-html-editor-tip'
23923 insertorderedlist : {
23924 title: 'Numbered List',
23925 text: 'Start a numbered list.',
23926 cls: 'x-html-editor-tip'
23929 title: 'Hyperlink',
23930 text: 'Make the selected text a hyperlink.',
23931 cls: 'x-html-editor-tip'
23934 title: 'Source Edit',
23935 text: 'Switch to source editing mode.',
23936 cls: 'x-html-editor-tip'
23940 onDestroy : function(){
23943 this.tb.items.each(function(item){
23945 item.menu.removeAll();
23947 item.menu.el.destroy();
23955 onFirstFocus: function() {
23956 this.tb.items.each(function(item){
23965 // <script type="text/javascript">
23968 * Ext JS Library 1.1.1
23969 * Copyright(c) 2006-2007, Ext JS, LLC.
23976 * @class Roo.form.HtmlEditor.ToolbarContext
23981 new Roo.form.HtmlEditor({
23984 { xtype: 'ToolbarStandard', styles : {} }
23985 { xtype: 'ToolbarContext', disable : {} }
23991 * @config : {Object} disable List of elements to disable.. (not done yet.)
23992 * @config : {Object} styles Map of styles available.
23996 Roo.form.HtmlEditor.ToolbarContext = function(config)
23999 Roo.apply(this, config);
24000 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24001 // dont call parent... till later.
24002 this.styles = this.styles || {};
24007 Roo.form.HtmlEditor.ToolbarContext.types = {
24019 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24085 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24090 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24100 style : 'fontFamily',
24101 displayField: 'display',
24102 optname : 'font-family',
24151 // should we really allow this??
24152 // should this just be
24163 style : 'fontFamily',
24164 displayField: 'display',
24165 optname : 'font-family',
24172 style : 'fontFamily',
24173 displayField: 'display',
24174 optname : 'font-family',
24181 style : 'fontFamily',
24182 displayField: 'display',
24183 optname : 'font-family',
24194 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24195 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24197 Roo.form.HtmlEditor.ToolbarContext.options = {
24199 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24200 [ 'Courier New', 'Courier New'],
24201 [ 'Tahoma', 'Tahoma'],
24202 [ 'Times New Roman,serif', 'Times'],
24203 [ 'Verdana','Verdana' ]
24207 // fixme - these need to be configurable..
24210 //Roo.form.HtmlEditor.ToolbarContext.types
24213 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
24220 editorcore : false,
24222 * @cfg {Object} disable List of toolbar elements to disable
24227 * @cfg {Object} styles List of styles
24228 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
24230 * These must be defined in the page, so they get rendered correctly..
24241 init : function(editor)
24243 this.editor = editor;
24244 this.editorcore = editor.editorcore ? editor.editorcore : editor;
24245 var editorcore = this.editorcore;
24247 var fid = editorcore.frameId;
24249 function btn(id, toggle, handler){
24250 var xid = fid + '-'+ id ;
24254 cls : 'x-btn-icon x-edit-'+id,
24255 enableToggle:toggle !== false,
24256 scope: editorcore, // was editor...
24257 handler:handler||editorcore.relayBtnCmd,
24258 clickEvent:'mousedown',
24259 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24263 // create a new element.
24264 var wdiv = editor.wrap.createChild({
24266 }, editor.wrap.dom.firstChild.nextSibling, true);
24268 // can we do this more than once??
24270 // stop form submits
24273 // disable everything...
24274 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24275 this.toolbars = {};
24277 for (var i in ty) {
24279 this.toolbars[i] = this.buildToolbar(ty[i],i);
24281 this.tb = this.toolbars.BODY;
24283 this.buildFooter();
24284 this.footer.show();
24285 editor.on('hide', function( ) { this.footer.hide() }, this);
24286 editor.on('show', function( ) { this.footer.show() }, this);
24289 this.rendered = true;
24291 // the all the btns;
24292 editor.on('editorevent', this.updateToolbar, this);
24293 // other toolbars need to implement this..
24294 //editor.on('editmodechange', this.updateToolbar, this);
24300 * Protected method that will not generally be called directly. It triggers
24301 * a toolbar update by reading the markup state of the current selection in the editor.
24303 * Note you can force an update by calling on('editorevent', scope, false)
24305 updateToolbar: function(editor,ev,sel){
24308 // capture mouse up - this is handy for selecting images..
24309 // perhaps should go somewhere else...
24310 if(!this.editorcore.activated){
24311 this.editor.onFirstFocus();
24317 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24318 // selectNode - might want to handle IE?
24320 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24321 ev.target && ev.target.tagName == 'IMG') {
24322 // they have click on an image...
24323 // let's see if we can change the selection...
24326 var nodeRange = sel.ownerDocument.createRange();
24328 nodeRange.selectNode(sel);
24330 nodeRange.selectNodeContents(sel);
24332 //nodeRange.collapse(true);
24333 var s = this.editorcore.win.getSelection();
24334 s.removeAllRanges();
24335 s.addRange(nodeRange);
24339 var updateFooter = sel ? false : true;
24342 var ans = this.editorcore.getAllAncestors();
24345 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24348 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24349 sel = sel ? sel : this.editorcore.doc.body;
24350 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24353 // pick a menu that exists..
24354 var tn = sel.tagName.toUpperCase();
24355 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24357 tn = sel.tagName.toUpperCase();
24359 var lastSel = this.tb.selectedNode;
24361 this.tb.selectedNode = sel;
24363 // if current menu does not match..
24365 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24368 ///console.log("show: " + tn);
24369 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24372 this.tb.items.first().el.innerHTML = tn + ': ';
24375 // update attributes
24376 if (this.tb.fields) {
24377 this.tb.fields.each(function(e) {
24379 e.setValue(sel.style[e.stylename]);
24382 e.setValue(sel.getAttribute(e.attrname));
24386 var hasStyles = false;
24387 for(var i in this.styles) {
24394 var st = this.tb.fields.item(0);
24396 st.store.removeAll();
24399 var cn = sel.className.split(/\s+/);
24402 if (this.styles['*']) {
24404 Roo.each(this.styles['*'], function(v) {
24405 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24408 if (this.styles[tn]) {
24409 Roo.each(this.styles[tn], function(v) {
24410 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24414 st.store.loadData(avs);
24418 // flag our selected Node.
24419 this.tb.selectedNode = sel;
24422 Roo.menu.MenuMgr.hideAll();
24426 if (!updateFooter) {
24427 //this.footDisp.dom.innerHTML = '';
24430 // update the footer
24434 this.footerEls = ans.reverse();
24435 Roo.each(this.footerEls, function(a,i) {
24436 if (!a) { return; }
24437 html += html.length ? ' > ' : '';
24439 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24444 var sz = this.footDisp.up('td').getSize();
24445 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24446 this.footDisp.dom.style.marginLeft = '5px';
24448 this.footDisp.dom.style.overflow = 'hidden';
24450 this.footDisp.dom.innerHTML = html;
24452 //this.editorsyncValue();
24459 onDestroy : function(){
24462 this.tb.items.each(function(item){
24464 item.menu.removeAll();
24466 item.menu.el.destroy();
24474 onFirstFocus: function() {
24475 // need to do this for all the toolbars..
24476 this.tb.items.each(function(item){
24480 buildToolbar: function(tlist, nm)
24482 var editor = this.editor;
24483 var editorcore = this.editorcore;
24484 // create a new element.
24485 var wdiv = editor.wrap.createChild({
24487 }, editor.wrap.dom.firstChild.nextSibling, true);
24490 var tb = new Roo.Toolbar(wdiv);
24493 tb.add(nm+ ": ");
24496 for(var i in this.styles) {
24501 if (styles && styles.length) {
24503 // this needs a multi-select checkbox...
24504 tb.addField( new Roo.form.ComboBox({
24505 store: new Roo.data.SimpleStore({
24507 fields: ['val', 'selected'],
24510 name : '-roo-edit-className',
24511 attrname : 'className',
24512 displayField: 'val',
24516 triggerAction: 'all',
24517 emptyText:'Select Style',
24518 selectOnFocus:true,
24521 'select': function(c, r, i) {
24522 // initial support only for on class per el..
24523 tb.selectedNode.className = r ? r.get('val') : '';
24524 editorcore.syncValue();
24531 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24532 var tbops = tbc.options;
24534 for (var i in tlist) {
24536 var item = tlist[i];
24537 tb.add(item.title + ": ");
24540 //optname == used so you can configure the options available..
24541 var opts = item.opts ? item.opts : false;
24542 if (item.optname) {
24543 opts = tbops[item.optname];
24548 // opts == pulldown..
24549 tb.addField( new Roo.form.ComboBox({
24550 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24552 fields: ['val', 'display'],
24555 name : '-roo-edit-' + i,
24557 stylename : item.style ? item.style : false,
24558 displayField: item.displayField ? item.displayField : 'val',
24559 valueField : 'val',
24561 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24563 triggerAction: 'all',
24564 emptyText:'Select',
24565 selectOnFocus:true,
24566 width: item.width ? item.width : 130,
24568 'select': function(c, r, i) {
24570 tb.selectedNode.style[c.stylename] = r.get('val');
24573 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24582 tb.addField( new Roo.form.TextField({
24585 //allowBlank:false,
24590 tb.addField( new Roo.form.TextField({
24591 name: '-roo-edit-' + i,
24598 'change' : function(f, nv, ov) {
24599 tb.selectedNode.setAttribute(f.attrname, nv);
24600 editorcore.syncValue();
24613 text: 'Stylesheets',
24616 click : function ()
24618 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24626 text: 'Remove Tag',
24629 click : function ()
24632 // undo does not work.
24634 var sn = tb.selectedNode;
24636 var pn = sn.parentNode;
24638 var stn = sn.childNodes[0];
24639 var en = sn.childNodes[sn.childNodes.length - 1 ];
24640 while (sn.childNodes.length) {
24641 var node = sn.childNodes[0];
24642 sn.removeChild(node);
24644 pn.insertBefore(node, sn);
24647 pn.removeChild(sn);
24648 var range = editorcore.createRange();
24650 range.setStart(stn,0);
24651 range.setEnd(en,0); //????
24652 //range.selectNode(sel);
24655 var selection = editorcore.getSelection();
24656 selection.removeAllRanges();
24657 selection.addRange(range);
24661 //_this.updateToolbar(null, null, pn);
24662 _this.updateToolbar(null, null, null);
24663 _this.footDisp.dom.innerHTML = '';
24673 tb.el.on('click', function(e){
24674 e.preventDefault(); // what does this do?
24676 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24679 // dont need to disable them... as they will get hidden
24684 buildFooter : function()
24687 var fel = this.editor.wrap.createChild();
24688 this.footer = new Roo.Toolbar(fel);
24689 // toolbar has scrolly on left / right?
24690 var footDisp= new Roo.Toolbar.Fill();
24696 handler : function() {
24697 _t.footDisp.scrollTo('left',0,true)
24701 this.footer.add( footDisp );
24706 handler : function() {
24708 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24712 var fel = Roo.get(footDisp.el);
24713 fel.addClass('x-editor-context');
24714 this.footDispWrap = fel;
24715 this.footDispWrap.overflow = 'hidden';
24717 this.footDisp = fel.createChild();
24718 this.footDispWrap.on('click', this.onContextClick, this)
24722 onContextClick : function (ev,dom)
24724 ev.preventDefault();
24725 var cn = dom.className;
24727 if (!cn.match(/x-ed-loc-/)) {
24730 var n = cn.split('-').pop();
24731 var ans = this.footerEls;
24735 var range = this.editorcore.createRange();
24737 range.selectNodeContents(sel);
24738 //range.selectNode(sel);
24741 var selection = this.editorcore.getSelection();
24742 selection.removeAllRanges();
24743 selection.addRange(range);
24747 this.updateToolbar(null, null, sel);
24764 * Ext JS Library 1.1.1
24765 * Copyright(c) 2006-2007, Ext JS, LLC.
24767 * Originally Released Under LGPL - original licence link has changed is not relivant.
24770 * <script type="text/javascript">
24774 * @class Roo.form.BasicForm
24775 * @extends Roo.util.Observable
24776 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24778 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24779 * @param {Object} config Configuration options
24781 Roo.form.BasicForm = function(el, config){
24782 this.allItems = [];
24783 this.childForms = [];
24784 Roo.apply(this, config);
24786 * The Roo.form.Field items in this form.
24787 * @type MixedCollection
24791 this.items = new Roo.util.MixedCollection(false, function(o){
24792 return o.id || (o.id = Roo.id());
24796 * @event beforeaction
24797 * Fires before any action is performed. Return false to cancel the action.
24798 * @param {Form} this
24799 * @param {Action} action The action to be performed
24801 beforeaction: true,
24803 * @event actionfailed
24804 * Fires when an action fails.
24805 * @param {Form} this
24806 * @param {Action} action The action that failed
24808 actionfailed : true,
24810 * @event actioncomplete
24811 * Fires when an action is completed.
24812 * @param {Form} this
24813 * @param {Action} action The action that completed
24815 actioncomplete : true
24820 Roo.form.BasicForm.superclass.constructor.call(this);
24822 Roo.form.BasicForm.popover.apply();
24825 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24827 * @cfg {String} method
24828 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24831 * @cfg {DataReader} reader
24832 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24833 * This is optional as there is built-in support for processing JSON.
24836 * @cfg {DataReader} errorReader
24837 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24838 * This is completely optional as there is built-in support for processing JSON.
24841 * @cfg {String} url
24842 * The URL to use for form actions if one isn't supplied in the action options.
24845 * @cfg {Boolean} fileUpload
24846 * Set to true if this form is a file upload.
24850 * @cfg {Object} baseParams
24851 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24856 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24861 activeAction : null,
24864 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24865 * or setValues() data instead of when the form was first created.
24867 trackResetOnLoad : false,
24871 * childForms - used for multi-tab forms
24874 childForms : false,
24877 * allItems - full list of fields.
24883 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24884 * element by passing it or its id or mask the form itself by passing in true.
24887 waitMsgTarget : false,
24892 disableMask : false,
24895 * @cfg {Boolean} errorMask (true|false) default false
24900 * @cfg {Number} maskOffset Default 100
24905 initEl : function(el){
24906 this.el = Roo.get(el);
24907 this.id = this.el.id || Roo.id();
24908 this.el.on('submit', this.onSubmit, this);
24909 this.el.addClass('x-form');
24913 onSubmit : function(e){
24918 * Returns true if client-side validation on the form is successful.
24921 isValid : function(){
24923 var target = false;
24924 this.items.each(function(f){
24931 if(!target && f.el.isVisible(true)){
24936 if(this.errorMask && !valid){
24937 Roo.form.BasicForm.popover.mask(this, target);
24944 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24947 isDirty : function(){
24949 this.items.each(function(f){
24959 * Returns true if any fields in this form have changed since their original load. (New version)
24963 hasChanged : function()
24966 this.items.each(function(f){
24967 if(f.hasChanged()){
24976 * Resets all hasChanged to 'false' -
24977 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24978 * So hasChanged storage is only to be used for this purpose
24981 resetHasChanged : function()
24983 this.items.each(function(f){
24984 f.resetHasChanged();
24991 * Performs a predefined action (submit or load) or custom actions you define on this form.
24992 * @param {String} actionName The name of the action type
24993 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24994 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24995 * accept other config options):
24997 Property Type Description
24998 ---------------- --------------- ----------------------------------------------------------------------------------
24999 url String The url for the action (defaults to the form's url)
25000 method String The form method to use (defaults to the form's method, or POST if not defined)
25001 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
25002 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
25003 validate the form on the client (defaults to false)
25005 * @return {BasicForm} this
25007 doAction : function(action, options){
25008 if(typeof action == 'string'){
25009 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25011 if(this.fireEvent('beforeaction', this, action) !== false){
25012 this.beforeAction(action);
25013 action.run.defer(100, action);
25019 * Shortcut to do a submit action.
25020 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25021 * @return {BasicForm} this
25023 submit : function(options){
25024 this.doAction('submit', options);
25029 * Shortcut to do a load action.
25030 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25031 * @return {BasicForm} this
25033 load : function(options){
25034 this.doAction('load', options);
25039 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25040 * @param {Record} record The record to edit
25041 * @return {BasicForm} this
25043 updateRecord : function(record){
25044 record.beginEdit();
25045 var fs = record.fields;
25046 fs.each(function(f){
25047 var field = this.findField(f.name);
25049 record.set(f.name, field.getValue());
25057 * Loads an Roo.data.Record into this form.
25058 * @param {Record} record The record to load
25059 * @return {BasicForm} this
25061 loadRecord : function(record){
25062 this.setValues(record.data);
25067 beforeAction : function(action){
25068 var o = action.options;
25070 if(!this.disableMask) {
25071 if(this.waitMsgTarget === true){
25072 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25073 }else if(this.waitMsgTarget){
25074 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25075 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25077 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25085 afterAction : function(action, success){
25086 this.activeAction = null;
25087 var o = action.options;
25089 if(!this.disableMask) {
25090 if(this.waitMsgTarget === true){
25092 }else if(this.waitMsgTarget){
25093 this.waitMsgTarget.unmask();
25095 Roo.MessageBox.updateProgress(1);
25096 Roo.MessageBox.hide();
25104 Roo.callback(o.success, o.scope, [this, action]);
25105 this.fireEvent('actioncomplete', this, action);
25109 // failure condition..
25110 // we have a scenario where updates need confirming.
25111 // eg. if a locking scenario exists..
25112 // we look for { errors : { needs_confirm : true }} in the response.
25114 (typeof(action.result) != 'undefined') &&
25115 (typeof(action.result.errors) != 'undefined') &&
25116 (typeof(action.result.errors.needs_confirm) != 'undefined')
25119 Roo.MessageBox.confirm(
25120 "Change requires confirmation",
25121 action.result.errorMsg,
25126 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
25136 Roo.callback(o.failure, o.scope, [this, action]);
25137 // show an error message if no failed handler is set..
25138 if (!this.hasListener('actionfailed')) {
25139 Roo.MessageBox.alert("Error",
25140 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25141 action.result.errorMsg :
25142 "Saving Failed, please check your entries or try again"
25146 this.fireEvent('actionfailed', this, action);
25152 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25153 * @param {String} id The value to search for
25156 findField : function(id){
25157 var field = this.items.get(id);
25159 this.items.each(function(f){
25160 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25166 return field || null;
25170 * Add a secondary form to this one,
25171 * Used to provide tabbed forms. One form is primary, with hidden values
25172 * which mirror the elements from the other forms.
25174 * @param {Roo.form.Form} form to add.
25177 addForm : function(form)
25180 if (this.childForms.indexOf(form) > -1) {
25184 this.childForms.push(form);
25186 Roo.each(form.allItems, function (fe) {
25188 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25189 if (this.findField(n)) { // already added..
25192 var add = new Roo.form.Hidden({
25195 add.render(this.el);
25202 * Mark fields in this form invalid in bulk.
25203 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25204 * @return {BasicForm} this
25206 markInvalid : function(errors){
25207 if(errors instanceof Array){
25208 for(var i = 0, len = errors.length; i < len; i++){
25209 var fieldError = errors[i];
25210 var f = this.findField(fieldError.id);
25212 f.markInvalid(fieldError.msg);
25218 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25219 field.markInvalid(errors[id]);
25223 Roo.each(this.childForms || [], function (f) {
25224 f.markInvalid(errors);
25231 * Set values for fields in this form in bulk.
25232 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25233 * @return {BasicForm} this
25235 setValues : function(values){
25236 if(values instanceof Array){ // array of objects
25237 for(var i = 0, len = values.length; i < len; i++){
25239 var f = this.findField(v.id);
25241 f.setValue(v.value);
25242 if(this.trackResetOnLoad){
25243 f.originalValue = f.getValue();
25247 }else{ // object hash
25250 if(typeof values[id] != 'function' && (field = this.findField(id))){
25252 if (field.setFromData &&
25253 field.valueField &&
25254 field.displayField &&
25255 // combos' with local stores can
25256 // be queried via setValue()
25257 // to set their value..
25258 (field.store && !field.store.isLocal)
25262 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25263 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25264 field.setFromData(sd);
25267 field.setValue(values[id]);
25271 if(this.trackResetOnLoad){
25272 field.originalValue = field.getValue();
25277 this.resetHasChanged();
25280 Roo.each(this.childForms || [], function (f) {
25281 f.setValues(values);
25282 f.resetHasChanged();
25289 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25290 * they are returned as an array.
25291 * @param {Boolean} asString
25294 getValues : function(asString){
25295 if (this.childForms) {
25296 // copy values from the child forms
25297 Roo.each(this.childForms, function (f) {
25298 this.setValues(f.getValues());
25303 if (typeof(FormData) != 'undefined' && asString !== true) {
25304 var fd = (new FormData(this.el.dom)).entries();
25306 var ent = fd.next();
25307 while (!ent.done) {
25308 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25315 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25316 if(asString === true){
25319 return Roo.urlDecode(fs);
25323 * Returns the fields in this form as an object with key/value pairs.
25324 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25327 getFieldValues : function(with_hidden)
25329 if (this.childForms) {
25330 // copy values from the child forms
25331 // should this call getFieldValues - probably not as we do not currently copy
25332 // hidden fields when we generate..
25333 Roo.each(this.childForms, function (f) {
25334 this.setValues(f.getValues());
25339 this.items.each(function(f){
25340 if (!f.getName()) {
25343 var v = f.getValue();
25344 if (f.inputType =='radio') {
25345 if (typeof(ret[f.getName()]) == 'undefined') {
25346 ret[f.getName()] = ''; // empty..
25349 if (!f.el.dom.checked) {
25353 v = f.el.dom.value;
25357 // not sure if this supported any more..
25358 if ((typeof(v) == 'object') && f.getRawValue) {
25359 v = f.getRawValue() ; // dates..
25361 // combo boxes where name != hiddenName...
25362 if (f.name != f.getName()) {
25363 ret[f.name] = f.getRawValue();
25365 ret[f.getName()] = v;
25372 * Clears all invalid messages in this form.
25373 * @return {BasicForm} this
25375 clearInvalid : function(){
25376 this.items.each(function(f){
25380 Roo.each(this.childForms || [], function (f) {
25389 * Resets this form.
25390 * @return {BasicForm} this
25392 reset : function(){
25393 this.items.each(function(f){
25397 Roo.each(this.childForms || [], function (f) {
25400 this.resetHasChanged();
25406 * Add Roo.form components to this form.
25407 * @param {Field} field1
25408 * @param {Field} field2 (optional)
25409 * @param {Field} etc (optional)
25410 * @return {BasicForm} this
25413 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25419 * Removes a field from the items collection (does NOT remove its markup).
25420 * @param {Field} field
25421 * @return {BasicForm} this
25423 remove : function(field){
25424 this.items.remove(field);
25429 * Looks at the fields in this form, checks them for an id attribute,
25430 * and calls applyTo on the existing dom element with that id.
25431 * @return {BasicForm} this
25433 render : function(){
25434 this.items.each(function(f){
25435 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25443 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25444 * @param {Object} values
25445 * @return {BasicForm} this
25447 applyToFields : function(o){
25448 this.items.each(function(f){
25455 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25456 * @param {Object} values
25457 * @return {BasicForm} this
25459 applyIfToFields : function(o){
25460 this.items.each(function(f){
25468 Roo.BasicForm = Roo.form.BasicForm;
25470 Roo.apply(Roo.form.BasicForm, {
25484 intervalID : false,
25490 if(this.isApplied){
25495 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25496 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25497 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25498 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25501 this.maskEl.top.enableDisplayMode("block");
25502 this.maskEl.left.enableDisplayMode("block");
25503 this.maskEl.bottom.enableDisplayMode("block");
25504 this.maskEl.right.enableDisplayMode("block");
25506 Roo.get(document.body).on('click', function(){
25510 Roo.get(document.body).on('touchstart', function(){
25514 this.isApplied = true
25517 mask : function(form, target)
25521 this.target = target;
25523 if(!this.form.errorMask || !target.el){
25527 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25529 var ot = this.target.el.calcOffsetsTo(scrollable);
25531 var scrollTo = ot[1] - this.form.maskOffset;
25533 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25535 scrollable.scrollTo('top', scrollTo);
25537 var el = this.target.wrap || this.target.el;
25539 var box = el.getBox();
25541 this.maskEl.top.setStyle('position', 'absolute');
25542 this.maskEl.top.setStyle('z-index', 10000);
25543 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25544 this.maskEl.top.setLeft(0);
25545 this.maskEl.top.setTop(0);
25546 this.maskEl.top.show();
25548 this.maskEl.left.setStyle('position', 'absolute');
25549 this.maskEl.left.setStyle('z-index', 10000);
25550 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25551 this.maskEl.left.setLeft(0);
25552 this.maskEl.left.setTop(box.y - this.padding);
25553 this.maskEl.left.show();
25555 this.maskEl.bottom.setStyle('position', 'absolute');
25556 this.maskEl.bottom.setStyle('z-index', 10000);
25557 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25558 this.maskEl.bottom.setLeft(0);
25559 this.maskEl.bottom.setTop(box.bottom + this.padding);
25560 this.maskEl.bottom.show();
25562 this.maskEl.right.setStyle('position', 'absolute');
25563 this.maskEl.right.setStyle('z-index', 10000);
25564 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25565 this.maskEl.right.setLeft(box.right + this.padding);
25566 this.maskEl.right.setTop(box.y - this.padding);
25567 this.maskEl.right.show();
25569 this.intervalID = window.setInterval(function() {
25570 Roo.form.BasicForm.popover.unmask();
25573 window.onwheel = function(){ return false;};
25575 (function(){ this.isMasked = true; }).defer(500, this);
25579 unmask : function()
25581 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25585 this.maskEl.top.setStyle('position', 'absolute');
25586 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25587 this.maskEl.top.hide();
25589 this.maskEl.left.setStyle('position', 'absolute');
25590 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25591 this.maskEl.left.hide();
25593 this.maskEl.bottom.setStyle('position', 'absolute');
25594 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25595 this.maskEl.bottom.hide();
25597 this.maskEl.right.setStyle('position', 'absolute');
25598 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25599 this.maskEl.right.hide();
25601 window.onwheel = function(){ return true;};
25603 if(this.intervalID){
25604 window.clearInterval(this.intervalID);
25605 this.intervalID = false;
25608 this.isMasked = false;
25616 * Ext JS Library 1.1.1
25617 * Copyright(c) 2006-2007, Ext JS, LLC.
25619 * Originally Released Under LGPL - original licence link has changed is not relivant.
25622 * <script type="text/javascript">
25626 * @class Roo.form.Form
25627 * @extends Roo.form.BasicForm
25628 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25630 * @param {Object} config Configuration options
25632 Roo.form.Form = function(config){
25634 if (config.items) {
25635 xitems = config.items;
25636 delete config.items;
25640 Roo.form.Form.superclass.constructor.call(this, null, config);
25641 this.url = this.url || this.action;
25643 this.root = new Roo.form.Layout(Roo.applyIf({
25647 this.active = this.root;
25649 * Array of all the buttons that have been added to this form via {@link addButton}
25653 this.allItems = [];
25656 * @event clientvalidation
25657 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25658 * @param {Form} this
25659 * @param {Boolean} valid true if the form has passed client-side validation
25661 clientvalidation: true,
25664 * Fires when the form is rendered
25665 * @param {Roo.form.Form} form
25670 if (this.progressUrl) {
25671 // push a hidden field onto the list of fields..
25675 name : 'UPLOAD_IDENTIFIER'
25680 Roo.each(xitems, this.addxtype, this);
25684 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25686 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25689 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25692 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25694 buttonAlign:'center',
25697 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25702 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25703 * This property cascades to child containers if not set.
25708 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25709 * fires a looping event with that state. This is required to bind buttons to the valid
25710 * state using the config value formBind:true on the button.
25712 monitorValid : false,
25715 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25720 * @cfg {String} progressUrl - Url to return progress data
25723 progressUrl : false,
25725 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25726 * sending a formdata with extra parameters - eg uploaded elements.
25732 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25733 * fields are added and the column is closed. If no fields are passed the column remains open
25734 * until end() is called.
25735 * @param {Object} config The config to pass to the column
25736 * @param {Field} field1 (optional)
25737 * @param {Field} field2 (optional)
25738 * @param {Field} etc (optional)
25739 * @return Column The column container object
25741 column : function(c){
25742 var col = new Roo.form.Column(c);
25744 if(arguments.length > 1){ // duplicate code required because of Opera
25745 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25752 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25753 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25754 * until end() is called.
25755 * @param {Object} config The config to pass to the fieldset
25756 * @param {Field} field1 (optional)
25757 * @param {Field} field2 (optional)
25758 * @param {Field} etc (optional)
25759 * @return FieldSet The fieldset container object
25761 fieldset : function(c){
25762 var fs = new Roo.form.FieldSet(c);
25764 if(arguments.length > 1){ // duplicate code required because of Opera
25765 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25772 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25773 * fields are added and the container is closed. If no fields are passed the container remains open
25774 * until end() is called.
25775 * @param {Object} config The config to pass to the Layout
25776 * @param {Field} field1 (optional)
25777 * @param {Field} field2 (optional)
25778 * @param {Field} etc (optional)
25779 * @return Layout The container object
25781 container : function(c){
25782 var l = new Roo.form.Layout(c);
25784 if(arguments.length > 1){ // duplicate code required because of Opera
25785 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25792 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25793 * @param {Object} container A Roo.form.Layout or subclass of Layout
25794 * @return {Form} this
25796 start : function(c){
25797 // cascade label info
25798 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25799 this.active.stack.push(c);
25800 c.ownerCt = this.active;
25806 * Closes the current open container
25807 * @return {Form} this
25810 if(this.active == this.root){
25813 this.active = this.active.ownerCt;
25818 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25819 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25820 * as the label of the field.
25821 * @param {Field} field1
25822 * @param {Field} field2 (optional)
25823 * @param {Field} etc. (optional)
25824 * @return {Form} this
25827 this.active.stack.push.apply(this.active.stack, arguments);
25828 this.allItems.push.apply(this.allItems,arguments);
25830 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25831 if(a[i].isFormField){
25836 Roo.form.Form.superclass.add.apply(this, r);
25846 * Find any element that has been added to a form, using it's ID or name
25847 * This can include framesets, columns etc. along with regular fields..
25848 * @param {String} id - id or name to find.
25850 * @return {Element} e - or false if nothing found.
25852 findbyId : function(id)
25858 Roo.each(this.allItems, function(f){
25859 if (f.id == id || f.name == id ){
25870 * Render this form into the passed container. This should only be called once!
25871 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25872 * @return {Form} this
25874 render : function(ct)
25880 var o = this.autoCreate || {
25882 method : this.method || 'POST',
25883 id : this.id || Roo.id()
25885 this.initEl(ct.createChild(o));
25887 this.root.render(this.el);
25891 this.items.each(function(f){
25892 f.render('x-form-el-'+f.id);
25895 if(this.buttons.length > 0){
25896 // tables are required to maintain order and for correct IE layout
25897 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25898 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25899 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25901 var tr = tb.getElementsByTagName('tr')[0];
25902 for(var i = 0, len = this.buttons.length; i < len; i++) {
25903 var b = this.buttons[i];
25904 var td = document.createElement('td');
25905 td.className = 'x-form-btn-td';
25906 b.render(tr.appendChild(td));
25909 if(this.monitorValid){ // initialize after render
25910 this.startMonitoring();
25912 this.fireEvent('rendered', this);
25917 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25918 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25919 * object or a valid Roo.DomHelper element config
25920 * @param {Function} handler The function called when the button is clicked
25921 * @param {Object} scope (optional) The scope of the handler function
25922 * @return {Roo.Button}
25924 addButton : function(config, handler, scope){
25928 minWidth: this.minButtonWidth,
25931 if(typeof config == "string"){
25934 Roo.apply(bc, config);
25936 var btn = new Roo.Button(null, bc);
25937 this.buttons.push(btn);
25942 * Adds a series of form elements (using the xtype property as the factory method.
25943 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25944 * @param {Object} config
25947 addxtype : function()
25949 var ar = Array.prototype.slice.call(arguments, 0);
25951 for(var i = 0; i < ar.length; i++) {
25953 continue; // skip -- if this happends something invalid got sent, we
25954 // should ignore it, as basically that interface element will not show up
25955 // and that should be pretty obvious!!
25958 if (Roo.form[ar[i].xtype]) {
25960 var fe = Roo.factory(ar[i], Roo.form);
25966 fe.store.form = this;
25971 this.allItems.push(fe);
25972 if (fe.items && fe.addxtype) {
25973 fe.addxtype.apply(fe, fe.items);
25983 // console.log('adding ' + ar[i].xtype);
25985 if (ar[i].xtype == 'Button') {
25986 //console.log('adding button');
25987 //console.log(ar[i]);
25988 this.addButton(ar[i]);
25989 this.allItems.push(fe);
25993 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25994 alert('end is not supported on xtype any more, use items');
25996 // //console.log('adding end');
26004 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26005 * option "monitorValid"
26007 startMonitoring : function(){
26010 Roo.TaskMgr.start({
26011 run : this.bindHandler,
26012 interval : this.monitorPoll || 200,
26019 * Stops monitoring of the valid state of this form
26021 stopMonitoring : function(){
26022 this.bound = false;
26026 bindHandler : function(){
26028 return false; // stops binding
26031 this.items.each(function(f){
26032 if(!f.isValid(true)){
26037 for(var i = 0, len = this.buttons.length; i < len; i++){
26038 var btn = this.buttons[i];
26039 if(btn.formBind === true && btn.disabled === valid){
26040 btn.setDisabled(!valid);
26043 this.fireEvent('clientvalidation', this, valid);
26057 Roo.Form = Roo.form.Form;
26060 * Ext JS Library 1.1.1
26061 * Copyright(c) 2006-2007, Ext JS, LLC.
26063 * Originally Released Under LGPL - original licence link has changed is not relivant.
26066 * <script type="text/javascript">
26069 // as we use this in bootstrap.
26070 Roo.namespace('Roo.form');
26072 * @class Roo.form.Action
26073 * Internal Class used to handle form actions
26075 * @param {Roo.form.BasicForm} el The form element or its id
26076 * @param {Object} config Configuration options
26081 // define the action interface
26082 Roo.form.Action = function(form, options){
26084 this.options = options || {};
26087 * Client Validation Failed
26090 Roo.form.Action.CLIENT_INVALID = 'client';
26092 * Server Validation Failed
26095 Roo.form.Action.SERVER_INVALID = 'server';
26097 * Connect to Server Failed
26100 Roo.form.Action.CONNECT_FAILURE = 'connect';
26102 * Reading Data from Server Failed
26105 Roo.form.Action.LOAD_FAILURE = 'load';
26107 Roo.form.Action.prototype = {
26109 failureType : undefined,
26110 response : undefined,
26111 result : undefined,
26113 // interface method
26114 run : function(options){
26118 // interface method
26119 success : function(response){
26123 // interface method
26124 handleResponse : function(response){
26128 // default connection failure
26129 failure : function(response){
26131 this.response = response;
26132 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26133 this.form.afterAction(this, false);
26136 processResponse : function(response){
26137 this.response = response;
26138 if(!response.responseText){
26141 this.result = this.handleResponse(response);
26142 return this.result;
26145 // utility functions used internally
26146 getUrl : function(appendParams){
26147 var url = this.options.url || this.form.url || this.form.el.dom.action;
26149 var p = this.getParams();
26151 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26157 getMethod : function(){
26158 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26161 getParams : function(){
26162 var bp = this.form.baseParams;
26163 var p = this.options.params;
26165 if(typeof p == "object"){
26166 p = Roo.urlEncode(Roo.applyIf(p, bp));
26167 }else if(typeof p == 'string' && bp){
26168 p += '&' + Roo.urlEncode(bp);
26171 p = Roo.urlEncode(bp);
26176 createCallback : function(){
26178 success: this.success,
26179 failure: this.failure,
26181 timeout: (this.form.timeout*1000),
26182 upload: this.form.fileUpload ? this.success : undefined
26187 Roo.form.Action.Submit = function(form, options){
26188 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26191 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26194 haveProgress : false,
26195 uploadComplete : false,
26197 // uploadProgress indicator.
26198 uploadProgress : function()
26200 if (!this.form.progressUrl) {
26204 if (!this.haveProgress) {
26205 Roo.MessageBox.progress("Uploading", "Uploading");
26207 if (this.uploadComplete) {
26208 Roo.MessageBox.hide();
26212 this.haveProgress = true;
26214 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26216 var c = new Roo.data.Connection();
26218 url : this.form.progressUrl,
26223 success : function(req){
26224 //console.log(data);
26228 rdata = Roo.decode(req.responseText)
26230 Roo.log("Invalid data from server..");
26234 if (!rdata || !rdata.success) {
26236 Roo.MessageBox.alert(Roo.encode(rdata));
26239 var data = rdata.data;
26241 if (this.uploadComplete) {
26242 Roo.MessageBox.hide();
26247 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26248 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26251 this.uploadProgress.defer(2000,this);
26254 failure: function(data) {
26255 Roo.log('progress url failed ');
26266 // run get Values on the form, so it syncs any secondary forms.
26267 this.form.getValues();
26269 var o = this.options;
26270 var method = this.getMethod();
26271 var isPost = method == 'POST';
26272 if(o.clientValidation === false || this.form.isValid()){
26274 if (this.form.progressUrl) {
26275 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26276 (new Date() * 1) + '' + Math.random());
26281 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26282 form:this.form.el.dom,
26283 url:this.getUrl(!isPost),
26285 params:isPost ? this.getParams() : null,
26286 isUpload: this.form.fileUpload,
26287 formData : this.form.formData
26290 this.uploadProgress();
26292 }else if (o.clientValidation !== false){ // client validation failed
26293 this.failureType = Roo.form.Action.CLIENT_INVALID;
26294 this.form.afterAction(this, false);
26298 success : function(response)
26300 this.uploadComplete= true;
26301 if (this.haveProgress) {
26302 Roo.MessageBox.hide();
26306 var result = this.processResponse(response);
26307 if(result === true || result.success){
26308 this.form.afterAction(this, true);
26312 this.form.markInvalid(result.errors);
26313 this.failureType = Roo.form.Action.SERVER_INVALID;
26315 this.form.afterAction(this, false);
26317 failure : function(response)
26319 this.uploadComplete= true;
26320 if (this.haveProgress) {
26321 Roo.MessageBox.hide();
26324 this.response = response;
26325 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26326 this.form.afterAction(this, false);
26329 handleResponse : function(response){
26330 if(this.form.errorReader){
26331 var rs = this.form.errorReader.read(response);
26334 for(var i = 0, len = rs.records.length; i < len; i++) {
26335 var r = rs.records[i];
26336 errors[i] = r.data;
26339 if(errors.length < 1){
26343 success : rs.success,
26349 ret = Roo.decode(response.responseText);
26353 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26363 Roo.form.Action.Load = function(form, options){
26364 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26365 this.reader = this.form.reader;
26368 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26373 Roo.Ajax.request(Roo.apply(
26374 this.createCallback(), {
26375 method:this.getMethod(),
26376 url:this.getUrl(false),
26377 params:this.getParams()
26381 success : function(response){
26383 var result = this.processResponse(response);
26384 if(result === true || !result.success || !result.data){
26385 this.failureType = Roo.form.Action.LOAD_FAILURE;
26386 this.form.afterAction(this, false);
26389 this.form.clearInvalid();
26390 this.form.setValues(result.data);
26391 this.form.afterAction(this, true);
26394 handleResponse : function(response){
26395 if(this.form.reader){
26396 var rs = this.form.reader.read(response);
26397 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26399 success : rs.success,
26403 return Roo.decode(response.responseText);
26407 Roo.form.Action.ACTION_TYPES = {
26408 'load' : Roo.form.Action.Load,
26409 'submit' : Roo.form.Action.Submit
26412 * Ext JS Library 1.1.1
26413 * Copyright(c) 2006-2007, Ext JS, LLC.
26415 * Originally Released Under LGPL - original licence link has changed is not relivant.
26418 * <script type="text/javascript">
26422 * @class Roo.form.Layout
26423 * @extends Roo.Component
26424 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26426 * @param {Object} config Configuration options
26428 Roo.form.Layout = function(config){
26430 if (config.items) {
26431 xitems = config.items;
26432 delete config.items;
26434 Roo.form.Layout.superclass.constructor.call(this, config);
26436 Roo.each(xitems, this.addxtype, this);
26440 Roo.extend(Roo.form.Layout, Roo.Component, {
26442 * @cfg {String/Object} autoCreate
26443 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26446 * @cfg {String/Object/Function} style
26447 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26448 * a function which returns such a specification.
26451 * @cfg {String} labelAlign
26452 * Valid values are "left," "top" and "right" (defaults to "left")
26455 * @cfg {Number} labelWidth
26456 * Fixed width in pixels of all field labels (defaults to undefined)
26459 * @cfg {Boolean} clear
26460 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26464 * @cfg {String} labelSeparator
26465 * The separator to use after field labels (defaults to ':')
26467 labelSeparator : ':',
26469 * @cfg {Boolean} hideLabels
26470 * True to suppress the display of field labels in this layout (defaults to false)
26472 hideLabels : false,
26475 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26480 onRender : function(ct, position){
26481 if(this.el){ // from markup
26482 this.el = Roo.get(this.el);
26483 }else { // generate
26484 var cfg = this.getAutoCreate();
26485 this.el = ct.createChild(cfg, position);
26488 this.el.applyStyles(this.style);
26490 if(this.labelAlign){
26491 this.el.addClass('x-form-label-'+this.labelAlign);
26493 if(this.hideLabels){
26494 this.labelStyle = "display:none";
26495 this.elementStyle = "padding-left:0;";
26497 if(typeof this.labelWidth == 'number'){
26498 this.labelStyle = "width:"+this.labelWidth+"px;";
26499 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26501 if(this.labelAlign == 'top'){
26502 this.labelStyle = "width:auto;";
26503 this.elementStyle = "padding-left:0;";
26506 var stack = this.stack;
26507 var slen = stack.length;
26509 if(!this.fieldTpl){
26510 var t = new Roo.Template(
26511 '<div class="x-form-item {5}">',
26512 '<label for="{0}" style="{2}">{1}{4}</label>',
26513 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26515 '</div><div class="x-form-clear-left"></div>'
26517 t.disableFormats = true;
26519 Roo.form.Layout.prototype.fieldTpl = t;
26521 for(var i = 0; i < slen; i++) {
26522 if(stack[i].isFormField){
26523 this.renderField(stack[i]);
26525 this.renderComponent(stack[i]);
26530 this.el.createChild({cls:'x-form-clear'});
26535 renderField : function(f){
26536 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26539 f.labelStyle||this.labelStyle||'', //2
26540 this.elementStyle||'', //3
26541 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26542 f.itemCls||this.itemCls||'' //5
26543 ], true).getPrevSibling());
26547 renderComponent : function(c){
26548 c.render(c.isLayout ? this.el : this.el.createChild());
26551 * Adds a object form elements (using the xtype property as the factory method.)
26552 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26553 * @param {Object} config
26555 addxtype : function(o)
26557 // create the lement.
26558 o.form = this.form;
26559 var fe = Roo.factory(o, Roo.form);
26560 this.form.allItems.push(fe);
26561 this.stack.push(fe);
26563 if (fe.isFormField) {
26564 this.form.items.add(fe);
26572 * @class Roo.form.Column
26573 * @extends Roo.form.Layout
26574 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26576 * @param {Object} config Configuration options
26578 Roo.form.Column = function(config){
26579 Roo.form.Column.superclass.constructor.call(this, config);
26582 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26584 * @cfg {Number/String} width
26585 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26588 * @cfg {String/Object} autoCreate
26589 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26593 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26596 onRender : function(ct, position){
26597 Roo.form.Column.superclass.onRender.call(this, ct, position);
26599 this.el.setWidth(this.width);
26606 * @class Roo.form.Row
26607 * @extends Roo.form.Layout
26608 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26610 * @param {Object} config Configuration options
26614 Roo.form.Row = function(config){
26615 Roo.form.Row.superclass.constructor.call(this, config);
26618 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26620 * @cfg {Number/String} width
26621 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26624 * @cfg {Number/String} height
26625 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26627 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26631 onRender : function(ct, position){
26632 //console.log('row render');
26634 var t = new Roo.Template(
26635 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26636 '<label for="{0}" style="{2}">{1}{4}</label>',
26637 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26641 t.disableFormats = true;
26643 Roo.form.Layout.prototype.rowTpl = t;
26645 this.fieldTpl = this.rowTpl;
26647 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26648 var labelWidth = 100;
26650 if ((this.labelAlign != 'top')) {
26651 if (typeof this.labelWidth == 'number') {
26652 labelWidth = this.labelWidth
26654 this.padWidth = 20 + labelWidth;
26658 Roo.form.Column.superclass.onRender.call(this, ct, position);
26660 this.el.setWidth(this.width);
26663 this.el.setHeight(this.height);
26668 renderField : function(f){
26669 f.fieldEl = this.fieldTpl.append(this.el, [
26670 f.id, f.fieldLabel,
26671 f.labelStyle||this.labelStyle||'',
26672 this.elementStyle||'',
26673 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26674 f.itemCls||this.itemCls||'',
26675 f.width ? f.width + this.padWidth : 160 + this.padWidth
26682 * @class Roo.form.FieldSet
26683 * @extends Roo.form.Layout
26684 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26686 * @param {Object} config Configuration options
26688 Roo.form.FieldSet = function(config){
26689 Roo.form.FieldSet.superclass.constructor.call(this, config);
26692 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26694 * @cfg {String} legend
26695 * The text to display as the legend for the FieldSet (defaults to '')
26698 * @cfg {String/Object} autoCreate
26699 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26703 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26706 onRender : function(ct, position){
26707 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26709 this.setLegend(this.legend);
26714 setLegend : function(text){
26716 this.el.child('legend').update(text);
26721 * Ext JS Library 1.1.1
26722 * Copyright(c) 2006-2007, Ext JS, LLC.
26724 * Originally Released Under LGPL - original licence link has changed is not relivant.
26727 * <script type="text/javascript">
26730 * @class Roo.form.VTypes
26731 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26734 Roo.form.VTypes = function(){
26735 // closure these in so they are only created once.
26736 var alpha = /^[a-zA-Z_]+$/;
26737 var alphanum = /^[a-zA-Z0-9_]+$/;
26738 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26739 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26741 // All these messages and functions are configurable
26744 * The function used to validate email addresses
26745 * @param {String} value The email address
26747 'email' : function(v){
26748 return email.test(v);
26751 * The error text to display when the email validation function returns false
26754 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26756 * The keystroke filter mask to be applied on email input
26759 'emailMask' : /[a-z0-9_\.\-@]/i,
26762 * The function used to validate URLs
26763 * @param {String} value The URL
26765 'url' : function(v){
26766 return url.test(v);
26769 * The error text to display when the url validation function returns false
26772 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26775 * The function used to validate alpha values
26776 * @param {String} value The value
26778 'alpha' : function(v){
26779 return alpha.test(v);
26782 * The error text to display when the alpha validation function returns false
26785 'alphaText' : 'This field should only contain letters and _',
26787 * The keystroke filter mask to be applied on alpha input
26790 'alphaMask' : /[a-z_]/i,
26793 * The function used to validate alphanumeric values
26794 * @param {String} value The value
26796 'alphanum' : function(v){
26797 return alphanum.test(v);
26800 * The error text to display when the alphanumeric validation function returns false
26803 'alphanumText' : 'This field should only contain letters, numbers and _',
26805 * The keystroke filter mask to be applied on alphanumeric input
26808 'alphanumMask' : /[a-z0-9_]/i
26810 }();//<script type="text/javascript">
26813 * @class Roo.form.FCKeditor
26814 * @extends Roo.form.TextArea
26815 * Wrapper around the FCKEditor http://www.fckeditor.net
26817 * Creates a new FCKeditor
26818 * @param {Object} config Configuration options
26820 Roo.form.FCKeditor = function(config){
26821 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26824 * @event editorinit
26825 * Fired when the editor is initialized - you can add extra handlers here..
26826 * @param {FCKeditor} this
26827 * @param {Object} the FCK object.
26834 Roo.form.FCKeditor.editors = { };
26835 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26837 //defaultAutoCreate : {
26838 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26842 * @cfg {Object} fck options - see fck manual for details.
26847 * @cfg {Object} fck toolbar set (Basic or Default)
26849 toolbarSet : 'Basic',
26851 * @cfg {Object} fck BasePath
26853 basePath : '/fckeditor/',
26861 onRender : function(ct, position)
26864 this.defaultAutoCreate = {
26866 style:"width:300px;height:60px;",
26867 autocomplete: "new-password"
26870 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26873 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26874 if(this.preventScrollbars){
26875 this.el.setStyle("overflow", "hidden");
26877 this.el.setHeight(this.growMin);
26880 //console.log('onrender' + this.getId() );
26881 Roo.form.FCKeditor.editors[this.getId()] = this;
26884 this.replaceTextarea() ;
26888 getEditor : function() {
26889 return this.fckEditor;
26892 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26893 * @param {Mixed} value The value to set
26897 setValue : function(value)
26899 //console.log('setValue: ' + value);
26901 if(typeof(value) == 'undefined') { // not sure why this is happending...
26904 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26906 //if(!this.el || !this.getEditor()) {
26907 // this.value = value;
26908 //this.setValue.defer(100,this,[value]);
26912 if(!this.getEditor()) {
26916 this.getEditor().SetData(value);
26923 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26924 * @return {Mixed} value The field value
26926 getValue : function()
26929 if (this.frame && this.frame.dom.style.display == 'none') {
26930 return Roo.form.FCKeditor.superclass.getValue.call(this);
26933 if(!this.el || !this.getEditor()) {
26935 // this.getValue.defer(100,this);
26940 var value=this.getEditor().GetData();
26941 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26942 return Roo.form.FCKeditor.superclass.getValue.call(this);
26948 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26949 * @return {Mixed} value The field value
26951 getRawValue : function()
26953 if (this.frame && this.frame.dom.style.display == 'none') {
26954 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26957 if(!this.el || !this.getEditor()) {
26958 //this.getRawValue.defer(100,this);
26965 var value=this.getEditor().GetData();
26966 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26967 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26971 setSize : function(w,h) {
26975 //if (this.frame && this.frame.dom.style.display == 'none') {
26976 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26979 //if(!this.el || !this.getEditor()) {
26980 // this.setSize.defer(100,this, [w,h]);
26986 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26988 this.frame.dom.setAttribute('width', w);
26989 this.frame.dom.setAttribute('height', h);
26990 this.frame.setSize(w,h);
26994 toggleSourceEdit : function(value) {
26998 this.el.dom.style.display = value ? '' : 'none';
26999 this.frame.dom.style.display = value ? 'none' : '';
27004 focus: function(tag)
27006 if (this.frame.dom.style.display == 'none') {
27007 return Roo.form.FCKeditor.superclass.focus.call(this);
27009 if(!this.el || !this.getEditor()) {
27010 this.focus.defer(100,this, [tag]);
27017 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27018 this.getEditor().Focus();
27020 if (!this.getEditor().Selection.GetSelection()) {
27021 this.focus.defer(100,this, [tag]);
27026 var r = this.getEditor().EditorDocument.createRange();
27027 r.setStart(tgs[0],0);
27028 r.setEnd(tgs[0],0);
27029 this.getEditor().Selection.GetSelection().removeAllRanges();
27030 this.getEditor().Selection.GetSelection().addRange(r);
27031 this.getEditor().Focus();
27038 replaceTextarea : function()
27040 if ( document.getElementById( this.getId() + '___Frame' ) ) {
27043 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27045 // We must check the elements firstly using the Id and then the name.
27046 var oTextarea = document.getElementById( this.getId() );
27048 var colElementsByName = document.getElementsByName( this.getId() ) ;
27050 oTextarea.style.display = 'none' ;
27052 if ( oTextarea.tabIndex ) {
27053 this.TabIndex = oTextarea.tabIndex ;
27056 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27057 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27058 this.frame = Roo.get(this.getId() + '___Frame')
27061 _getConfigHtml : function()
27065 for ( var o in this.fckconfig ) {
27066 sConfig += sConfig.length > 0 ? '&' : '';
27067 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27070 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27074 _getIFrameHtml : function()
27076 var sFile = 'fckeditor.html' ;
27077 /* no idea what this is about..
27080 if ( (/fcksource=true/i).test( window.top.location.search ) )
27081 sFile = 'fckeditor.original.html' ;
27086 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27087 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
27090 var html = '<iframe id="' + this.getId() +
27091 '___Frame" src="' + sLink +
27092 '" width="' + this.width +
27093 '" height="' + this.height + '"' +
27094 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
27095 ' frameborder="0" scrolling="no"></iframe>' ;
27100 _insertHtmlBefore : function( html, element )
27102 if ( element.insertAdjacentHTML ) {
27104 element.insertAdjacentHTML( 'beforeBegin', html ) ;
27106 var oRange = document.createRange() ;
27107 oRange.setStartBefore( element ) ;
27108 var oFragment = oRange.createContextualFragment( html );
27109 element.parentNode.insertBefore( oFragment, element ) ;
27122 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27124 function FCKeditor_OnComplete(editorInstance){
27125 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27126 f.fckEditor = editorInstance;
27127 //console.log("loaded");
27128 f.fireEvent('editorinit', f, editorInstance);
27148 //<script type="text/javascript">
27150 * @class Roo.form.GridField
27151 * @extends Roo.form.Field
27152 * Embed a grid (or editable grid into a form)
27155 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27157 * xgrid.store = Roo.data.Store
27158 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27159 * xgrid.store.reader = Roo.data.JsonReader
27163 * Creates a new GridField
27164 * @param {Object} config Configuration options
27166 Roo.form.GridField = function(config){
27167 Roo.form.GridField.superclass.constructor.call(this, config);
27171 Roo.extend(Roo.form.GridField, Roo.form.Field, {
27173 * @cfg {Number} width - used to restrict width of grid..
27177 * @cfg {Number} height - used to restrict height of grid..
27181 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27187 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27188 * {tag: "input", type: "checkbox", autocomplete: "off"})
27190 // defaultAutoCreate : { tag: 'div' },
27191 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27193 * @cfg {String} addTitle Text to include for adding a title.
27197 onResize : function(){
27198 Roo.form.Field.superclass.onResize.apply(this, arguments);
27201 initEvents : function(){
27202 // Roo.form.Checkbox.superclass.initEvents.call(this);
27203 // has no events...
27208 getResizeEl : function(){
27212 getPositionEl : function(){
27217 onRender : function(ct, position){
27219 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27220 var style = this.style;
27223 Roo.form.GridField.superclass.onRender.call(this, ct, position);
27224 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27225 this.viewEl = this.wrap.createChild({ tag: 'div' });
27227 this.viewEl.applyStyles(style);
27230 this.viewEl.setWidth(this.width);
27233 this.viewEl.setHeight(this.height);
27235 //if(this.inputValue !== undefined){
27236 //this.setValue(this.value);
27239 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27242 this.grid.render();
27243 this.grid.getDataSource().on('remove', this.refreshValue, this);
27244 this.grid.getDataSource().on('update', this.refreshValue, this);
27245 this.grid.on('afteredit', this.refreshValue, this);
27251 * Sets the value of the item.
27252 * @param {String} either an object or a string..
27254 setValue : function(v){
27256 v = v || []; // empty set..
27257 // this does not seem smart - it really only affects memoryproxy grids..
27258 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27259 var ds = this.grid.getDataSource();
27260 // assumes a json reader..
27262 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27263 ds.loadData( data);
27265 // clear selection so it does not get stale.
27266 if (this.grid.sm) {
27267 this.grid.sm.clearSelections();
27270 Roo.form.GridField.superclass.setValue.call(this, v);
27271 this.refreshValue();
27272 // should load data in the grid really....
27276 refreshValue: function() {
27278 this.grid.getDataSource().each(function(r) {
27281 this.el.dom.value = Roo.encode(val);
27289 * Ext JS Library 1.1.1
27290 * Copyright(c) 2006-2007, Ext JS, LLC.
27292 * Originally Released Under LGPL - original licence link has changed is not relivant.
27295 * <script type="text/javascript">
27298 * @class Roo.form.DisplayField
27299 * @extends Roo.form.Field
27300 * A generic Field to display non-editable data.
27301 * @cfg {Boolean} closable (true|false) default false
27303 * Creates a new Display Field item.
27304 * @param {Object} config Configuration options
27306 Roo.form.DisplayField = function(config){
27307 Roo.form.DisplayField.superclass.constructor.call(this, config);
27312 * Fires after the click the close btn
27313 * @param {Roo.form.DisplayField} this
27319 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27320 inputType: 'hidden',
27326 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27328 focusClass : undefined,
27330 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27332 fieldClass: 'x-form-field',
27335 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27337 valueRenderer: undefined,
27341 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27342 * {tag: "input", type: "checkbox", autocomplete: "off"})
27345 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27349 onResize : function(){
27350 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27354 initEvents : function(){
27355 // Roo.form.Checkbox.superclass.initEvents.call(this);
27356 // has no events...
27359 this.closeEl.on('click', this.onClose, this);
27365 getResizeEl : function(){
27369 getPositionEl : function(){
27374 onRender : function(ct, position){
27376 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27377 //if(this.inputValue !== undefined){
27378 this.wrap = this.el.wrap();
27380 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27383 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27386 if (this.bodyStyle) {
27387 this.viewEl.applyStyles(this.bodyStyle);
27389 //this.viewEl.setStyle('padding', '2px');
27391 this.setValue(this.value);
27396 initValue : Roo.emptyFn,
27401 onClick : function(){
27406 * Sets the checked state of the checkbox.
27407 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27409 setValue : function(v){
27411 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27412 // this might be called before we have a dom element..
27413 if (!this.viewEl) {
27416 this.viewEl.dom.innerHTML = html;
27417 Roo.form.DisplayField.superclass.setValue.call(this, v);
27421 onClose : function(e)
27423 e.preventDefault();
27425 this.fireEvent('close', this);
27434 * @class Roo.form.DayPicker
27435 * @extends Roo.form.Field
27436 * A Day picker show [M] [T] [W] ....
27438 * Creates a new Day Picker
27439 * @param {Object} config Configuration options
27441 Roo.form.DayPicker= function(config){
27442 Roo.form.DayPicker.superclass.constructor.call(this, config);
27446 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27448 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27450 focusClass : undefined,
27452 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27454 fieldClass: "x-form-field",
27457 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27458 * {tag: "input", type: "checkbox", autocomplete: "off"})
27460 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27463 actionMode : 'viewEl',
27467 inputType : 'hidden',
27470 inputElement: false, // real input element?
27471 basedOn: false, // ????
27473 isFormField: true, // not sure where this is needed!!!!
27475 onResize : function(){
27476 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27477 if(!this.boxLabel){
27478 this.el.alignTo(this.wrap, 'c-c');
27482 initEvents : function(){
27483 Roo.form.Checkbox.superclass.initEvents.call(this);
27484 this.el.on("click", this.onClick, this);
27485 this.el.on("change", this.onClick, this);
27489 getResizeEl : function(){
27493 getPositionEl : function(){
27499 onRender : function(ct, position){
27500 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27502 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27504 var r1 = '<table><tr>';
27505 var r2 = '<tr class="x-form-daypick-icons">';
27506 for (var i=0; i < 7; i++) {
27507 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27508 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27511 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27512 viewEl.select('img').on('click', this.onClick, this);
27513 this.viewEl = viewEl;
27516 // this will not work on Chrome!!!
27517 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27518 this.el.on('propertychange', this.setFromHidden, this); //ie
27526 initValue : Roo.emptyFn,
27529 * Returns the checked state of the checkbox.
27530 * @return {Boolean} True if checked, else false
27532 getValue : function(){
27533 return this.el.dom.value;
27538 onClick : function(e){
27539 //this.setChecked(!this.checked);
27540 Roo.get(e.target).toggleClass('x-menu-item-checked');
27541 this.refreshValue();
27542 //if(this.el.dom.checked != this.checked){
27543 // this.setValue(this.el.dom.checked);
27548 refreshValue : function()
27551 this.viewEl.select('img',true).each(function(e,i,n) {
27552 val += e.is(".x-menu-item-checked") ? String(n) : '';
27554 this.setValue(val, true);
27558 * Sets the checked state of the checkbox.
27559 * On is always based on a string comparison between inputValue and the param.
27560 * @param {Boolean/String} value - the value to set
27561 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27563 setValue : function(v,suppressEvent){
27564 if (!this.el.dom) {
27567 var old = this.el.dom.value ;
27568 this.el.dom.value = v;
27569 if (suppressEvent) {
27573 // update display..
27574 this.viewEl.select('img',true).each(function(e,i,n) {
27576 var on = e.is(".x-menu-item-checked");
27577 var newv = v.indexOf(String(n)) > -1;
27579 e.toggleClass('x-menu-item-checked');
27585 this.fireEvent('change', this, v, old);
27590 // handle setting of hidden value by some other method!!?!?
27591 setFromHidden: function()
27596 //console.log("SET FROM HIDDEN");
27597 //alert('setFrom hidden');
27598 this.setValue(this.el.dom.value);
27601 onDestroy : function()
27604 Roo.get(this.viewEl).remove();
27607 Roo.form.DayPicker.superclass.onDestroy.call(this);
27611 * RooJS Library 1.1.1
27612 * Copyright(c) 2008-2011 Alan Knowles
27619 * @class Roo.form.ComboCheck
27620 * @extends Roo.form.ComboBox
27621 * A combobox for multiple select items.
27623 * FIXME - could do with a reset button..
27626 * Create a new ComboCheck
27627 * @param {Object} config Configuration options
27629 Roo.form.ComboCheck = function(config){
27630 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27631 // should verify some data...
27633 // hiddenName = required..
27634 // displayField = required
27635 // valudField == required
27636 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27638 Roo.each(req, function(e) {
27639 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27640 throw "Roo.form.ComboCheck : missing value for: " + e;
27647 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27652 selectedClass: 'x-menu-item-checked',
27655 onRender : function(ct, position){
27661 var cls = 'x-combo-list';
27664 this.tpl = new Roo.Template({
27665 html : '<div class="'+cls+'-item x-menu-check-item">' +
27666 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27667 '<span>{' + this.displayField + '}</span>' +
27674 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27675 this.view.singleSelect = false;
27676 this.view.multiSelect = true;
27677 this.view.toggleSelect = true;
27678 this.pageTb.add(new Roo.Toolbar.Fill(), {
27681 handler: function()
27688 onViewOver : function(e, t){
27694 onViewClick : function(doFocus,index){
27698 select: function () {
27699 //Roo.log("SELECT CALLED");
27702 selectByValue : function(xv, scrollIntoView){
27703 var ar = this.getValueArray();
27706 Roo.each(ar, function(v) {
27707 if(v === undefined || v === null){
27710 var r = this.findRecord(this.valueField, v);
27712 sels.push(this.store.indexOf(r))
27716 this.view.select(sels);
27722 onSelect : function(record, index){
27723 // Roo.log("onselect Called");
27724 // this is only called by the clear button now..
27725 this.view.clearSelections();
27726 this.setValue('[]');
27727 if (this.value != this.valueBefore) {
27728 this.fireEvent('change', this, this.value, this.valueBefore);
27729 this.valueBefore = this.value;
27732 getValueArray : function()
27737 //Roo.log(this.value);
27738 if (typeof(this.value) == 'undefined') {
27741 var ar = Roo.decode(this.value);
27742 return ar instanceof Array ? ar : []; //?? valid?
27745 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27750 expand : function ()
27753 Roo.form.ComboCheck.superclass.expand.call(this);
27754 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27755 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27760 collapse : function(){
27761 Roo.form.ComboCheck.superclass.collapse.call(this);
27762 var sl = this.view.getSelectedIndexes();
27763 var st = this.store;
27767 Roo.each(sl, function(i) {
27769 nv.push(r.get(this.valueField));
27771 this.setValue(Roo.encode(nv));
27772 if (this.value != this.valueBefore) {
27774 this.fireEvent('change', this, this.value, this.valueBefore);
27775 this.valueBefore = this.value;
27780 setValue : function(v){
27784 var vals = this.getValueArray();
27786 Roo.each(vals, function(k) {
27787 var r = this.findRecord(this.valueField, k);
27789 tv.push(r.data[this.displayField]);
27790 }else if(this.valueNotFoundText !== undefined){
27791 tv.push( this.valueNotFoundText );
27796 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27797 this.hiddenField.value = v;
27803 * Ext JS Library 1.1.1
27804 * Copyright(c) 2006-2007, Ext JS, LLC.
27806 * Originally Released Under LGPL - original licence link has changed is not relivant.
27809 * <script type="text/javascript">
27813 * @class Roo.form.Signature
27814 * @extends Roo.form.Field
27818 * @param {Object} config Configuration options
27821 Roo.form.Signature = function(config){
27822 Roo.form.Signature.superclass.constructor.call(this, config);
27824 this.addEvents({// not in used??
27827 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27828 * @param {Roo.form.Signature} combo This combo box
27833 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27834 * @param {Roo.form.ComboBox} combo This combo box
27835 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27841 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27843 * @cfg {Object} labels Label to use when rendering a form.
27847 * confirm : "Confirm"
27852 confirm : "Confirm"
27855 * @cfg {Number} width The signature panel width (defaults to 300)
27859 * @cfg {Number} height The signature panel height (defaults to 100)
27863 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27865 allowBlank : false,
27868 // {Object} signPanel The signature SVG panel element (defaults to {})
27870 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27871 isMouseDown : false,
27872 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27873 isConfirmed : false,
27874 // {String} signatureTmp SVG mapping string (defaults to empty string)
27878 defaultAutoCreate : { // modified by initCompnoent..
27884 onRender : function(ct, position){
27886 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27888 this.wrap = this.el.wrap({
27889 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27892 this.createToolbar(this);
27893 this.signPanel = this.wrap.createChild({
27895 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27899 this.svgID = Roo.id();
27900 this.svgEl = this.signPanel.createChild({
27901 xmlns : 'http://www.w3.org/2000/svg',
27903 id : this.svgID + "-svg",
27905 height: this.height,
27906 viewBox: '0 0 '+this.width+' '+this.height,
27910 id: this.svgID + "-svg-r",
27912 height: this.height,
27917 id: this.svgID + "-svg-l",
27919 y1: (this.height*0.8), // start set the line in 80% of height
27920 x2: this.width, // end
27921 y2: (this.height*0.8), // end set the line in 80% of height
27923 'stroke-width': "1",
27924 'stroke-dasharray': "3",
27925 'shape-rendering': "crispEdges",
27926 'pointer-events': "none"
27930 id: this.svgID + "-svg-p",
27932 'stroke-width': "3",
27934 'pointer-events': 'none'
27939 this.svgBox = this.svgEl.dom.getScreenCTM();
27941 createSVG : function(){
27942 var svg = this.signPanel;
27943 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27946 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27947 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27948 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27949 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27950 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27951 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27952 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27955 isTouchEvent : function(e){
27956 return e.type.match(/^touch/);
27958 getCoords : function (e) {
27959 var pt = this.svgEl.dom.createSVGPoint();
27962 if (this.isTouchEvent(e)) {
27963 pt.x = e.targetTouches[0].clientX;
27964 pt.y = e.targetTouches[0].clientY;
27966 var a = this.svgEl.dom.getScreenCTM();
27967 var b = a.inverse();
27968 var mx = pt.matrixTransform(b);
27969 return mx.x + ',' + mx.y;
27971 //mouse event headler
27972 down : function (e) {
27973 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27974 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27976 this.isMouseDown = true;
27978 e.preventDefault();
27980 move : function (e) {
27981 if (this.isMouseDown) {
27982 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27983 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27986 e.preventDefault();
27988 up : function (e) {
27989 this.isMouseDown = false;
27990 var sp = this.signatureTmp.split(' ');
27993 if(!sp[sp.length-2].match(/^L/)){
27997 this.signatureTmp = sp.join(" ");
28000 if(this.getValue() != this.signatureTmp){
28001 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28002 this.isConfirmed = false;
28004 e.preventDefault();
28008 * Protected method that will not generally be called directly. It
28009 * is called when the editor creates its toolbar. Override this method if you need to
28010 * add custom toolbar buttons.
28011 * @param {HtmlEditor} editor
28013 createToolbar : function(editor){
28014 function btn(id, toggle, handler){
28015 var xid = fid + '-'+ id ;
28019 cls : 'x-btn-icon x-edit-'+id,
28020 enableToggle:toggle !== false,
28021 scope: editor, // was editor...
28022 handler:handler||editor.relayBtnCmd,
28023 clickEvent:'mousedown',
28024 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28030 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28034 cls : ' x-signature-btn x-signature-'+id,
28035 scope: editor, // was editor...
28036 handler: this.reset,
28037 clickEvent:'mousedown',
28038 text: this.labels.clear
28045 cls : ' x-signature-btn x-signature-'+id,
28046 scope: editor, // was editor...
28047 handler: this.confirmHandler,
28048 clickEvent:'mousedown',
28049 text: this.labels.confirm
28056 * when user is clicked confirm then show this image.....
28058 * @return {String} Image Data URI
28060 getImageDataURI : function(){
28061 var svg = this.svgEl.dom.parentNode.innerHTML;
28062 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28067 * @return {Boolean} this.isConfirmed
28069 getConfirmed : function(){
28070 return this.isConfirmed;
28074 * @return {Number} this.width
28076 getWidth : function(){
28081 * @return {Number} this.height
28083 getHeight : function(){
28084 return this.height;
28087 getSignature : function(){
28088 return this.signatureTmp;
28091 reset : function(){
28092 this.signatureTmp = '';
28093 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28094 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28095 this.isConfirmed = false;
28096 Roo.form.Signature.superclass.reset.call(this);
28098 setSignature : function(s){
28099 this.signatureTmp = s;
28100 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28101 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28103 this.isConfirmed = false;
28104 Roo.form.Signature.superclass.reset.call(this);
28107 // Roo.log(this.signPanel.dom.contentWindow.up())
28110 setConfirmed : function(){
28114 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28117 confirmHandler : function(){
28118 if(!this.getSignature()){
28122 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28123 this.setValue(this.getSignature());
28124 this.isConfirmed = true;
28126 this.fireEvent('confirm', this);
28129 // Subclasses should provide the validation implementation by overriding this
28130 validateValue : function(value){
28131 if(this.allowBlank){
28135 if(this.isConfirmed){
28142 * Ext JS Library 1.1.1
28143 * Copyright(c) 2006-2007, Ext JS, LLC.
28145 * Originally Released Under LGPL - original licence link has changed is not relivant.
28148 * <script type="text/javascript">
28153 * @class Roo.form.ComboBox
28154 * @extends Roo.form.TriggerField
28155 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28157 * Create a new ComboBox.
28158 * @param {Object} config Configuration options
28160 Roo.form.Select = function(config){
28161 Roo.form.Select.superclass.constructor.call(this, config);
28165 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28167 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28170 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28171 * rendering into an Roo.Editor, defaults to false)
28174 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28175 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28178 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28181 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28182 * the dropdown list (defaults to undefined, with no header element)
28186 * @cfg {String/Roo.Template} tpl The template to use to render the output
28190 defaultAutoCreate : {tag: "select" },
28192 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28194 listWidth: undefined,
28196 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28197 * mode = 'remote' or 'text' if mode = 'local')
28199 displayField: undefined,
28201 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28202 * mode = 'remote' or 'value' if mode = 'local').
28203 * Note: use of a valueField requires the user make a selection
28204 * in order for a value to be mapped.
28206 valueField: undefined,
28210 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28211 * field's data value (defaults to the underlying DOM element's name)
28213 hiddenName: undefined,
28215 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28219 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28221 selectedClass: 'x-combo-selected',
28223 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
28224 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28225 * which displays a downward arrow icon).
28227 triggerClass : 'x-form-arrow-trigger',
28229 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28233 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28234 * anchor positions (defaults to 'tl-bl')
28236 listAlign: 'tl-bl?',
28238 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28242 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
28243 * query specified by the allQuery config option (defaults to 'query')
28245 triggerAction: 'query',
28247 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28248 * (defaults to 4, does not apply if editable = false)
28252 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28253 * delay (typeAheadDelay) if it matches a known value (defaults to false)
28257 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28258 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28262 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28263 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28267 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28268 * when editable = true (defaults to false)
28270 selectOnFocus:false,
28272 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28274 queryParam: 'query',
28276 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28277 * when mode = 'remote' (defaults to 'Loading...')
28279 loadingText: 'Loading...',
28281 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28285 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28289 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28290 * traditional select (defaults to true)
28294 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28298 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28302 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28303 * listWidth has a higher value)
28307 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28308 * allow the user to set arbitrary text into the field (defaults to false)
28310 forceSelection:false,
28312 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28313 * if typeAhead = true (defaults to 250)
28315 typeAheadDelay : 250,
28317 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28318 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28320 valueNotFoundText : undefined,
28323 * @cfg {String} defaultValue The value displayed after loading the store.
28328 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28330 blockFocus : false,
28333 * @cfg {Boolean} disableClear Disable showing of clear button.
28335 disableClear : false,
28337 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28339 alwaysQuery : false,
28345 // element that contains real text value.. (when hidden is used..)
28348 onRender : function(ct, position){
28349 Roo.form.Field.prototype.onRender.call(this, ct, position);
28352 this.store.on('beforeload', this.onBeforeLoad, this);
28353 this.store.on('load', this.onLoad, this);
28354 this.store.on('loadexception', this.onLoadException, this);
28355 this.store.load({});
28363 initEvents : function(){
28364 //Roo.form.ComboBox.superclass.initEvents.call(this);
28368 onDestroy : function(){
28371 this.store.un('beforeload', this.onBeforeLoad, this);
28372 this.store.un('load', this.onLoad, this);
28373 this.store.un('loadexception', this.onLoadException, this);
28375 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28379 fireKey : function(e){
28380 if(e.isNavKeyPress() && !this.list.isVisible()){
28381 this.fireEvent("specialkey", this, e);
28386 onResize: function(w, h){
28394 * Allow or prevent the user from directly editing the field text. If false is passed,
28395 * the user will only be able to select from the items defined in the dropdown list. This method
28396 * is the runtime equivalent of setting the 'editable' config option at config time.
28397 * @param {Boolean} value True to allow the user to directly edit the field text
28399 setEditable : function(value){
28404 onBeforeLoad : function(){
28406 Roo.log("Select before load");
28409 this.innerList.update(this.loadingText ?
28410 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28411 //this.restrictHeight();
28412 this.selectedIndex = -1;
28416 onLoad : function(){
28419 var dom = this.el.dom;
28420 dom.innerHTML = '';
28421 var od = dom.ownerDocument;
28423 if (this.emptyText) {
28424 var op = od.createElement('option');
28425 op.setAttribute('value', '');
28426 op.innerHTML = String.format('{0}', this.emptyText);
28427 dom.appendChild(op);
28429 if(this.store.getCount() > 0){
28431 var vf = this.valueField;
28432 var df = this.displayField;
28433 this.store.data.each(function(r) {
28434 // which colmsn to use... testing - cdoe / title..
28435 var op = od.createElement('option');
28436 op.setAttribute('value', r.data[vf]);
28437 op.innerHTML = String.format('{0}', r.data[df]);
28438 dom.appendChild(op);
28440 if (typeof(this.defaultValue != 'undefined')) {
28441 this.setValue(this.defaultValue);
28446 //this.onEmptyResults();
28451 onLoadException : function()
28453 dom.innerHTML = '';
28455 Roo.log("Select on load exception");
28459 Roo.log(this.store.reader.jsonData);
28460 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28461 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28467 onTypeAhead : function(){
28472 onSelect : function(record, index){
28473 Roo.log('on select?');
28475 if(this.fireEvent('beforeselect', this, record, index) !== false){
28476 this.setFromData(index > -1 ? record.data : false);
28478 this.fireEvent('select', this, record, index);
28483 * Returns the currently selected field value or empty string if no value is set.
28484 * @return {String} value The selected value
28486 getValue : function(){
28487 var dom = this.el.dom;
28488 this.value = dom.options[dom.selectedIndex].value;
28494 * Clears any text/value currently set in the field
28496 clearValue : function(){
28498 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28503 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28504 * will be displayed in the field. If the value does not match the data value of an existing item,
28505 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28506 * Otherwise the field will be blank (although the value will still be set).
28507 * @param {String} value The value to match
28509 setValue : function(v){
28510 var d = this.el.dom;
28511 for (var i =0; i < d.options.length;i++) {
28512 if (v == d.options[i].value) {
28513 d.selectedIndex = i;
28521 * @property {Object} the last set data for the element
28526 * Sets the value of the field based on a object which is related to the record format for the store.
28527 * @param {Object} value the value to set as. or false on reset?
28529 setFromData : function(o){
28530 Roo.log('setfrom data?');
28536 reset : function(){
28540 findRecord : function(prop, value){
28545 if(this.store.getCount() > 0){
28546 this.store.each(function(r){
28547 if(r.data[prop] == value){
28557 getName: function()
28559 // returns hidden if it's set..
28560 if (!this.rendered) {return ''};
28561 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28569 onEmptyResults : function(){
28570 Roo.log('empty results');
28575 * Returns true if the dropdown list is expanded, else false.
28577 isExpanded : function(){
28582 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28583 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28584 * @param {String} value The data value of the item to select
28585 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28586 * selected item if it is not currently in view (defaults to true)
28587 * @return {Boolean} True if the value matched an item in the list, else false
28589 selectByValue : function(v, scrollIntoView){
28590 Roo.log('select By Value');
28593 if(v !== undefined && v !== null){
28594 var r = this.findRecord(this.valueField || this.displayField, v);
28596 this.select(this.store.indexOf(r), scrollIntoView);
28604 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28605 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28606 * @param {Number} index The zero-based index of the list item to select
28607 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28608 * selected item if it is not currently in view (defaults to true)
28610 select : function(index, scrollIntoView){
28611 Roo.log('select ');
28614 this.selectedIndex = index;
28615 this.view.select(index);
28616 if(scrollIntoView !== false){
28617 var el = this.view.getNode(index);
28619 this.innerList.scrollChildIntoView(el, false);
28627 validateBlur : function(){
28634 initQuery : function(){
28635 this.doQuery(this.getRawValue());
28639 doForce : function(){
28640 if(this.el.dom.value.length > 0){
28641 this.el.dom.value =
28642 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28648 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28649 * query allowing the query action to be canceled if needed.
28650 * @param {String} query The SQL query to execute
28651 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28652 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28653 * saved in the current store (defaults to false)
28655 doQuery : function(q, forceAll){
28657 Roo.log('doQuery?');
28658 if(q === undefined || q === null){
28663 forceAll: forceAll,
28667 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28671 forceAll = qe.forceAll;
28672 if(forceAll === true || (q.length >= this.minChars)){
28673 if(this.lastQuery != q || this.alwaysQuery){
28674 this.lastQuery = q;
28675 if(this.mode == 'local'){
28676 this.selectedIndex = -1;
28678 this.store.clearFilter();
28680 this.store.filter(this.displayField, q);
28684 this.store.baseParams[this.queryParam] = q;
28686 params: this.getParams(q)
28691 this.selectedIndex = -1;
28698 getParams : function(q){
28700 //p[this.queryParam] = q;
28703 p.limit = this.pageSize;
28709 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28711 collapse : function(){
28716 collapseIf : function(e){
28721 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28723 expand : function(){
28731 * @cfg {Boolean} grow
28735 * @cfg {Number} growMin
28739 * @cfg {Number} growMax
28747 setWidth : function()
28751 getResizeEl : function(){
28754 });//<script type="text/javasscript">
28758 * @class Roo.DDView
28759 * A DnD enabled version of Roo.View.
28760 * @param {Element/String} container The Element in which to create the View.
28761 * @param {String} tpl The template string used to create the markup for each element of the View
28762 * @param {Object} config The configuration properties. These include all the config options of
28763 * {@link Roo.View} plus some specific to this class.<br>
28765 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28766 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28768 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28769 .x-view-drag-insert-above {
28770 border-top:1px dotted #3366cc;
28772 .x-view-drag-insert-below {
28773 border-bottom:1px dotted #3366cc;
28779 Roo.DDView = function(container, tpl, config) {
28780 Roo.DDView.superclass.constructor.apply(this, arguments);
28781 this.getEl().setStyle("outline", "0px none");
28782 this.getEl().unselectable();
28783 if (this.dragGroup) {
28784 this.setDraggable(this.dragGroup.split(","));
28786 if (this.dropGroup) {
28787 this.setDroppable(this.dropGroup.split(","));
28789 if (this.deletable) {
28790 this.setDeletable();
28792 this.isDirtyFlag = false;
28798 Roo.extend(Roo.DDView, Roo.View, {
28799 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28800 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28801 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28802 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28806 reset: Roo.emptyFn,
28808 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28810 validate: function() {
28814 destroy: function() {
28815 this.purgeListeners();
28816 this.getEl.removeAllListeners();
28817 this.getEl().remove();
28818 if (this.dragZone) {
28819 if (this.dragZone.destroy) {
28820 this.dragZone.destroy();
28823 if (this.dropZone) {
28824 if (this.dropZone.destroy) {
28825 this.dropZone.destroy();
28830 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28831 getName: function() {
28835 /** Loads the View from a JSON string representing the Records to put into the Store. */
28836 setValue: function(v) {
28838 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28841 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28842 this.store.proxy = new Roo.data.MemoryProxy(data);
28846 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28847 getValue: function() {
28849 this.store.each(function(rec) {
28850 result += rec.id + ',';
28852 return result.substr(0, result.length - 1) + ')';
28855 getIds: function() {
28856 var i = 0, result = new Array(this.store.getCount());
28857 this.store.each(function(rec) {
28858 result[i++] = rec.id;
28863 isDirty: function() {
28864 return this.isDirtyFlag;
28868 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28869 * whole Element becomes the target, and this causes the drop gesture to append.
28871 getTargetFromEvent : function(e) {
28872 var target = e.getTarget();
28873 while ((target !== null) && (target.parentNode != this.el.dom)) {
28874 target = target.parentNode;
28877 target = this.el.dom.lastChild || this.el.dom;
28883 * Create the drag data which consists of an object which has the property "ddel" as
28884 * the drag proxy element.
28886 getDragData : function(e) {
28887 var target = this.findItemFromChild(e.getTarget());
28889 this.handleSelection(e);
28890 var selNodes = this.getSelectedNodes();
28893 copy: this.copy || (this.allowCopy && e.ctrlKey),
28897 var selectedIndices = this.getSelectedIndexes();
28898 for (var i = 0; i < selectedIndices.length; i++) {
28899 dragData.records.push(this.store.getAt(selectedIndices[i]));
28901 if (selNodes.length == 1) {
28902 dragData.ddel = target.cloneNode(true); // the div element
28904 var div = document.createElement('div'); // create the multi element drag "ghost"
28905 div.className = 'multi-proxy';
28906 for (var i = 0, len = selNodes.length; i < len; i++) {
28907 div.appendChild(selNodes[i].cloneNode(true));
28909 dragData.ddel = div;
28911 //console.log(dragData)
28912 //console.log(dragData.ddel.innerHTML)
28915 //console.log('nodragData')
28919 /** Specify to which ddGroup items in this DDView may be dragged. */
28920 setDraggable: function(ddGroup) {
28921 if (ddGroup instanceof Array) {
28922 Roo.each(ddGroup, this.setDraggable, this);
28925 if (this.dragZone) {
28926 this.dragZone.addToGroup(ddGroup);
28928 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28929 containerScroll: true,
28933 // Draggability implies selection. DragZone's mousedown selects the element.
28934 if (!this.multiSelect) { this.singleSelect = true; }
28936 // Wire the DragZone's handlers up to methods in *this*
28937 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28941 /** Specify from which ddGroup this DDView accepts drops. */
28942 setDroppable: function(ddGroup) {
28943 if (ddGroup instanceof Array) {
28944 Roo.each(ddGroup, this.setDroppable, this);
28947 if (this.dropZone) {
28948 this.dropZone.addToGroup(ddGroup);
28950 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28951 containerScroll: true,
28955 // Wire the DropZone's handlers up to methods in *this*
28956 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28957 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28958 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28959 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28960 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28964 /** Decide whether to drop above or below a View node. */
28965 getDropPoint : function(e, n, dd){
28966 if (n == this.el.dom) { return "above"; }
28967 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28968 var c = t + (b - t) / 2;
28969 var y = Roo.lib.Event.getPageY(e);
28977 onNodeEnter : function(n, dd, e, data){
28981 onNodeOver : function(n, dd, e, data){
28982 var pt = this.getDropPoint(e, n, dd);
28983 // set the insert point style on the target node
28984 var dragElClass = this.dropNotAllowed;
28987 if (pt == "above"){
28988 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28989 targetElClass = "x-view-drag-insert-above";
28991 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28992 targetElClass = "x-view-drag-insert-below";
28994 if (this.lastInsertClass != targetElClass){
28995 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28996 this.lastInsertClass = targetElClass;
28999 return dragElClass;
29002 onNodeOut : function(n, dd, e, data){
29003 this.removeDropIndicators(n);
29006 onNodeDrop : function(n, dd, e, data){
29007 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29010 var pt = this.getDropPoint(e, n, dd);
29011 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29012 if (pt == "below") { insertAt++; }
29013 for (var i = 0; i < data.records.length; i++) {
29014 var r = data.records[i];
29015 var dup = this.store.getById(r.id);
29016 if (dup && (dd != this.dragZone)) {
29017 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29020 this.store.insert(insertAt++, r.copy());
29022 data.source.isDirtyFlag = true;
29024 this.store.insert(insertAt++, r);
29026 this.isDirtyFlag = true;
29029 this.dragZone.cachedTarget = null;
29033 removeDropIndicators : function(n){
29035 Roo.fly(n).removeClass([
29036 "x-view-drag-insert-above",
29037 "x-view-drag-insert-below"]);
29038 this.lastInsertClass = "_noclass";
29043 * Utility method. Add a delete option to the DDView's context menu.
29044 * @param {String} imageUrl The URL of the "delete" icon image.
29046 setDeletable: function(imageUrl) {
29047 if (!this.singleSelect && !this.multiSelect) {
29048 this.singleSelect = true;
29050 var c = this.getContextMenu();
29051 this.contextMenu.on("itemclick", function(item) {
29054 this.remove(this.getSelectedIndexes());
29058 this.contextMenu.add({
29065 /** Return the context menu for this DDView. */
29066 getContextMenu: function() {
29067 if (!this.contextMenu) {
29068 // Create the View's context menu
29069 this.contextMenu = new Roo.menu.Menu({
29070 id: this.id + "-contextmenu"
29072 this.el.on("contextmenu", this.showContextMenu, this);
29074 return this.contextMenu;
29077 disableContextMenu: function() {
29078 if (this.contextMenu) {
29079 this.el.un("contextmenu", this.showContextMenu, this);
29083 showContextMenu: function(e, item) {
29084 item = this.findItemFromChild(e.getTarget());
29087 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29088 this.contextMenu.showAt(e.getXY());
29093 * Remove {@link Roo.data.Record}s at the specified indices.
29094 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29096 remove: function(selectedIndices) {
29097 selectedIndices = [].concat(selectedIndices);
29098 for (var i = 0; i < selectedIndices.length; i++) {
29099 var rec = this.store.getAt(selectedIndices[i]);
29100 this.store.remove(rec);
29105 * Double click fires the event, but also, if this is draggable, and there is only one other
29106 * related DropZone, it transfers the selected node.
29108 onDblClick : function(e){
29109 var item = this.findItemFromChild(e.getTarget());
29111 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29114 if (this.dragGroup) {
29115 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29116 while (targets.indexOf(this.dropZone) > -1) {
29117 targets.remove(this.dropZone);
29119 if (targets.length == 1) {
29120 this.dragZone.cachedTarget = null;
29121 var el = Roo.get(targets[0].getEl());
29122 var box = el.getBox(true);
29123 targets[0].onNodeDrop(el.dom, {
29125 xy: [box.x, box.y + box.height - 1]
29126 }, null, this.getDragData(e));
29132 handleSelection: function(e) {
29133 this.dragZone.cachedTarget = null;
29134 var item = this.findItemFromChild(e.getTarget());
29136 this.clearSelections(true);
29139 if (item && (this.multiSelect || this.singleSelect)){
29140 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29141 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29142 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29143 this.unselect(item);
29145 this.select(item, this.multiSelect && e.ctrlKey);
29146 this.lastSelection = item;
29151 onItemClick : function(item, index, e){
29152 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29158 unselect : function(nodeInfo, suppressEvent){
29159 var node = this.getNode(nodeInfo);
29160 if(node && this.isSelected(node)){
29161 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29162 Roo.fly(node).removeClass(this.selectedClass);
29163 this.selections.remove(node);
29164 if(!suppressEvent){
29165 this.fireEvent("selectionchange", this, this.selections);
29173 * Ext JS Library 1.1.1
29174 * Copyright(c) 2006-2007, Ext JS, LLC.
29176 * Originally Released Under LGPL - original licence link has changed is not relivant.
29179 * <script type="text/javascript">
29183 * @class Roo.LayoutManager
29184 * @extends Roo.util.Observable
29185 * Base class for layout managers.
29187 Roo.LayoutManager = function(container, config){
29188 Roo.LayoutManager.superclass.constructor.call(this);
29189 this.el = Roo.get(container);
29190 // ie scrollbar fix
29191 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29192 document.body.scroll = "no";
29193 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29194 this.el.position('relative');
29196 this.id = this.el.id;
29197 this.el.addClass("x-layout-container");
29198 /** false to disable window resize monitoring @type Boolean */
29199 this.monitorWindowResize = true;
29204 * Fires when a layout is performed.
29205 * @param {Roo.LayoutManager} this
29209 * @event regionresized
29210 * Fires when the user resizes a region.
29211 * @param {Roo.LayoutRegion} region The resized region
29212 * @param {Number} newSize The new size (width for east/west, height for north/south)
29214 "regionresized" : true,
29216 * @event regioncollapsed
29217 * Fires when a region is collapsed.
29218 * @param {Roo.LayoutRegion} region The collapsed region
29220 "regioncollapsed" : true,
29222 * @event regionexpanded
29223 * Fires when a region is expanded.
29224 * @param {Roo.LayoutRegion} region The expanded region
29226 "regionexpanded" : true
29228 this.updating = false;
29229 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29232 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29234 * Returns true if this layout is currently being updated
29235 * @return {Boolean}
29237 isUpdating : function(){
29238 return this.updating;
29242 * Suspend the LayoutManager from doing auto-layouts while
29243 * making multiple add or remove calls
29245 beginUpdate : function(){
29246 this.updating = true;
29250 * Restore auto-layouts and optionally disable the manager from performing a layout
29251 * @param {Boolean} noLayout true to disable a layout update
29253 endUpdate : function(noLayout){
29254 this.updating = false;
29260 layout: function(){
29264 onRegionResized : function(region, newSize){
29265 this.fireEvent("regionresized", region, newSize);
29269 onRegionCollapsed : function(region){
29270 this.fireEvent("regioncollapsed", region);
29273 onRegionExpanded : function(region){
29274 this.fireEvent("regionexpanded", region);
29278 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29279 * performs box-model adjustments.
29280 * @return {Object} The size as an object {width: (the width), height: (the height)}
29282 getViewSize : function(){
29284 if(this.el.dom != document.body){
29285 size = this.el.getSize();
29287 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29289 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29290 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29295 * Returns the Element this layout is bound to.
29296 * @return {Roo.Element}
29298 getEl : function(){
29303 * Returns the specified region.
29304 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29305 * @return {Roo.LayoutRegion}
29307 getRegion : function(target){
29308 return this.regions[target.toLowerCase()];
29311 onWindowResize : function(){
29312 if(this.monitorWindowResize){
29318 * Ext JS Library 1.1.1
29319 * Copyright(c) 2006-2007, Ext JS, LLC.
29321 * Originally Released Under LGPL - original licence link has changed is not relivant.
29324 * <script type="text/javascript">
29327 * @class Roo.BorderLayout
29328 * @extends Roo.LayoutManager
29329 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29330 * please see: <br><br>
29331 * <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>
29332 * <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>
29335 var layout = new Roo.BorderLayout(document.body, {
29369 preferredTabWidth: 150
29374 var CP = Roo.ContentPanel;
29376 layout.beginUpdate();
29377 layout.add("north", new CP("north", "North"));
29378 layout.add("south", new CP("south", {title: "South", closable: true}));
29379 layout.add("west", new CP("west", {title: "West"}));
29380 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29381 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29382 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29383 layout.getRegion("center").showPanel("center1");
29384 layout.endUpdate();
29387 <b>The container the layout is rendered into can be either the body element or any other element.
29388 If it is not the body element, the container needs to either be an absolute positioned element,
29389 or you will need to add "position:relative" to the css of the container. You will also need to specify
29390 the container size if it is not the body element.</b>
29393 * Create a new BorderLayout
29394 * @param {String/HTMLElement/Element} container The container this layout is bound to
29395 * @param {Object} config Configuration options
29397 Roo.BorderLayout = function(container, config){
29398 config = config || {};
29399 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29400 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29401 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29402 var target = this.factory.validRegions[i];
29403 if(config[target]){
29404 this.addRegion(target, config[target]);
29409 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29411 * Creates and adds a new region if it doesn't already exist.
29412 * @param {String} target The target region key (north, south, east, west or center).
29413 * @param {Object} config The regions config object
29414 * @return {BorderLayoutRegion} The new region
29416 addRegion : function(target, config){
29417 if(!this.regions[target]){
29418 var r = this.factory.create(target, this, config);
29419 this.bindRegion(target, r);
29421 return this.regions[target];
29425 bindRegion : function(name, r){
29426 this.regions[name] = r;
29427 r.on("visibilitychange", this.layout, this);
29428 r.on("paneladded", this.layout, this);
29429 r.on("panelremoved", this.layout, this);
29430 r.on("invalidated", this.layout, this);
29431 r.on("resized", this.onRegionResized, this);
29432 r.on("collapsed", this.onRegionCollapsed, this);
29433 r.on("expanded", this.onRegionExpanded, this);
29437 * Performs a layout update.
29439 layout : function(){
29440 if(this.updating) {
29443 var size = this.getViewSize();
29444 var w = size.width;
29445 var h = size.height;
29450 //var x = 0, y = 0;
29452 var rs = this.regions;
29453 var north = rs["north"];
29454 var south = rs["south"];
29455 var west = rs["west"];
29456 var east = rs["east"];
29457 var center = rs["center"];
29458 //if(this.hideOnLayout){ // not supported anymore
29459 //c.el.setStyle("display", "none");
29461 if(north && north.isVisible()){
29462 var b = north.getBox();
29463 var m = north.getMargins();
29464 b.width = w - (m.left+m.right);
29467 centerY = b.height + b.y + m.bottom;
29468 centerH -= centerY;
29469 north.updateBox(this.safeBox(b));
29471 if(south && south.isVisible()){
29472 var b = south.getBox();
29473 var m = south.getMargins();
29474 b.width = w - (m.left+m.right);
29476 var totalHeight = (b.height + m.top + m.bottom);
29477 b.y = h - totalHeight + m.top;
29478 centerH -= totalHeight;
29479 south.updateBox(this.safeBox(b));
29481 if(west && west.isVisible()){
29482 var b = west.getBox();
29483 var m = west.getMargins();
29484 b.height = centerH - (m.top+m.bottom);
29486 b.y = centerY + m.top;
29487 var totalWidth = (b.width + m.left + m.right);
29488 centerX += totalWidth;
29489 centerW -= totalWidth;
29490 west.updateBox(this.safeBox(b));
29492 if(east && east.isVisible()){
29493 var b = east.getBox();
29494 var m = east.getMargins();
29495 b.height = centerH - (m.top+m.bottom);
29496 var totalWidth = (b.width + m.left + m.right);
29497 b.x = w - totalWidth + m.left;
29498 b.y = centerY + m.top;
29499 centerW -= totalWidth;
29500 east.updateBox(this.safeBox(b));
29503 var m = center.getMargins();
29505 x: centerX + m.left,
29506 y: centerY + m.top,
29507 width: centerW - (m.left+m.right),
29508 height: centerH - (m.top+m.bottom)
29510 //if(this.hideOnLayout){
29511 //center.el.setStyle("display", "block");
29513 center.updateBox(this.safeBox(centerBox));
29516 this.fireEvent("layout", this);
29520 safeBox : function(box){
29521 box.width = Math.max(0, box.width);
29522 box.height = Math.max(0, box.height);
29527 * Adds a ContentPanel (or subclass) to this layout.
29528 * @param {String} target The target region key (north, south, east, west or center).
29529 * @param {Roo.ContentPanel} panel The panel to add
29530 * @return {Roo.ContentPanel} The added panel
29532 add : function(target, panel){
29534 target = target.toLowerCase();
29535 return this.regions[target].add(panel);
29539 * Remove a ContentPanel (or subclass) to this layout.
29540 * @param {String} target The target region key (north, south, east, west or center).
29541 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29542 * @return {Roo.ContentPanel} The removed panel
29544 remove : function(target, panel){
29545 target = target.toLowerCase();
29546 return this.regions[target].remove(panel);
29550 * Searches all regions for a panel with the specified id
29551 * @param {String} panelId
29552 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29554 findPanel : function(panelId){
29555 var rs = this.regions;
29556 for(var target in rs){
29557 if(typeof rs[target] != "function"){
29558 var p = rs[target].getPanel(panelId);
29568 * Searches all regions for a panel with the specified id and activates (shows) it.
29569 * @param {String/ContentPanel} panelId The panels id or the panel itself
29570 * @return {Roo.ContentPanel} The shown panel or null
29572 showPanel : function(panelId) {
29573 var rs = this.regions;
29574 for(var target in rs){
29575 var r = rs[target];
29576 if(typeof r != "function"){
29577 if(r.hasPanel(panelId)){
29578 return r.showPanel(panelId);
29586 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29587 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29589 restoreState : function(provider){
29591 provider = Roo.state.Manager;
29593 var sm = new Roo.LayoutStateManager();
29594 sm.init(this, provider);
29598 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29599 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29600 * a valid ContentPanel config object. Example:
29602 // Create the main layout
29603 var layout = new Roo.BorderLayout('main-ct', {
29614 // Create and add multiple ContentPanels at once via configs
29617 id: 'source-files',
29619 title:'Ext Source Files',
29632 * @param {Object} regions An object containing ContentPanel configs by region name
29634 batchAdd : function(regions){
29635 this.beginUpdate();
29636 for(var rname in regions){
29637 var lr = this.regions[rname];
29639 this.addTypedPanels(lr, regions[rname]);
29646 addTypedPanels : function(lr, ps){
29647 if(typeof ps == 'string'){
29648 lr.add(new Roo.ContentPanel(ps));
29650 else if(ps instanceof Array){
29651 for(var i =0, len = ps.length; i < len; i++){
29652 this.addTypedPanels(lr, ps[i]);
29655 else if(!ps.events){ // raw config?
29657 delete ps.el; // prevent conflict
29658 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29660 else { // panel object assumed!
29665 * Adds a xtype elements to the layout.
29669 xtype : 'ContentPanel',
29676 xtype : 'NestedLayoutPanel',
29682 items : [ ... list of content panels or nested layout panels.. ]
29686 * @param {Object} cfg Xtype definition of item to add.
29688 addxtype : function(cfg)
29690 // basically accepts a pannel...
29691 // can accept a layout region..!?!?
29692 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29694 if (!cfg.xtype.match(/Panel$/)) {
29699 if (typeof(cfg.region) == 'undefined') {
29700 Roo.log("Failed to add Panel, region was not set");
29704 var region = cfg.region;
29710 xitems = cfg.items;
29717 case 'ContentPanel': // ContentPanel (el, cfg)
29718 case 'ScrollPanel': // ContentPanel (el, cfg)
29720 if(cfg.autoCreate) {
29721 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29723 var el = this.el.createChild();
29724 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29727 this.add(region, ret);
29731 case 'TreePanel': // our new panel!
29732 cfg.el = this.el.createChild();
29733 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29734 this.add(region, ret);
29737 case 'NestedLayoutPanel':
29738 // create a new Layout (which is a Border Layout...
29739 var el = this.el.createChild();
29740 var clayout = cfg.layout;
29742 clayout.items = clayout.items || [];
29743 // replace this exitems with the clayout ones..
29744 xitems = clayout.items;
29747 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29748 cfg.background = false;
29750 var layout = new Roo.BorderLayout(el, clayout);
29752 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29753 //console.log('adding nested layout panel ' + cfg.toSource());
29754 this.add(region, ret);
29755 nb = {}; /// find first...
29760 // needs grid and region
29762 //var el = this.getRegion(region).el.createChild();
29763 var el = this.el.createChild();
29764 // create the grid first...
29766 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29768 if (region == 'center' && this.active ) {
29769 cfg.background = false;
29771 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29773 this.add(region, ret);
29774 if (cfg.background) {
29775 ret.on('activate', function(gp) {
29776 if (!gp.grid.rendered) {
29791 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29793 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29794 this.add(region, ret);
29797 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29801 // GridPanel (grid, cfg)
29804 this.beginUpdate();
29808 Roo.each(xitems, function(i) {
29809 region = nb && i.region ? i.region : false;
29811 var add = ret.addxtype(i);
29814 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29815 if (!i.background) {
29816 abn[region] = nb[region] ;
29823 // make the last non-background panel active..
29824 //if (nb) { Roo.log(abn); }
29827 for(var r in abn) {
29828 region = this.getRegion(r);
29830 // tried using nb[r], but it does not work..
29832 region.showPanel(abn[r]);
29843 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29844 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29845 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29846 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29849 var CP = Roo.ContentPanel;
29851 var layout = Roo.BorderLayout.create({
29855 panels: [new CP("north", "North")]
29864 panels: [new CP("west", {title: "West"})]
29873 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29882 panels: [new CP("south", {title: "South", closable: true})]
29889 preferredTabWidth: 150,
29891 new CP("center1", {title: "Close Me", closable: true}),
29892 new CP("center2", {title: "Center Panel", closable: false})
29897 layout.getRegion("center").showPanel("center1");
29902 Roo.BorderLayout.create = function(config, targetEl){
29903 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29904 layout.beginUpdate();
29905 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29906 for(var j = 0, jlen = regions.length; j < jlen; j++){
29907 var lr = regions[j];
29908 if(layout.regions[lr] && config[lr].panels){
29909 var r = layout.regions[lr];
29910 var ps = config[lr].panels;
29911 layout.addTypedPanels(r, ps);
29914 layout.endUpdate();
29919 Roo.BorderLayout.RegionFactory = {
29921 validRegions : ["north","south","east","west","center"],
29924 create : function(target, mgr, config){
29925 target = target.toLowerCase();
29926 if(config.lightweight || config.basic){
29927 return new Roo.BasicLayoutRegion(mgr, config, target);
29931 return new Roo.NorthLayoutRegion(mgr, config);
29933 return new Roo.SouthLayoutRegion(mgr, config);
29935 return new Roo.EastLayoutRegion(mgr, config);
29937 return new Roo.WestLayoutRegion(mgr, config);
29939 return new Roo.CenterLayoutRegion(mgr, config);
29941 throw 'Layout region "'+target+'" not supported.';
29945 * Ext JS Library 1.1.1
29946 * Copyright(c) 2006-2007, Ext JS, LLC.
29948 * Originally Released Under LGPL - original licence link has changed is not relivant.
29951 * <script type="text/javascript">
29955 * @class Roo.BasicLayoutRegion
29956 * @extends Roo.util.Observable
29957 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29958 * and does not have a titlebar, tabs or any other features. All it does is size and position
29959 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29961 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29963 this.position = pos;
29966 * @scope Roo.BasicLayoutRegion
29970 * @event beforeremove
29971 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29972 * @param {Roo.LayoutRegion} this
29973 * @param {Roo.ContentPanel} panel The panel
29974 * @param {Object} e The cancel event object
29976 "beforeremove" : true,
29978 * @event invalidated
29979 * Fires when the layout for this region is changed.
29980 * @param {Roo.LayoutRegion} this
29982 "invalidated" : true,
29984 * @event visibilitychange
29985 * Fires when this region is shown or hidden
29986 * @param {Roo.LayoutRegion} this
29987 * @param {Boolean} visibility true or false
29989 "visibilitychange" : true,
29991 * @event paneladded
29992 * Fires when a panel is added.
29993 * @param {Roo.LayoutRegion} this
29994 * @param {Roo.ContentPanel} panel The panel
29996 "paneladded" : true,
29998 * @event panelremoved
29999 * Fires when a panel is removed.
30000 * @param {Roo.LayoutRegion} this
30001 * @param {Roo.ContentPanel} panel The panel
30003 "panelremoved" : true,
30005 * @event beforecollapse
30006 * Fires when this region before collapse.
30007 * @param {Roo.LayoutRegion} this
30009 "beforecollapse" : true,
30012 * Fires when this region is collapsed.
30013 * @param {Roo.LayoutRegion} this
30015 "collapsed" : true,
30018 * Fires when this region is expanded.
30019 * @param {Roo.LayoutRegion} this
30024 * Fires when this region is slid into view.
30025 * @param {Roo.LayoutRegion} this
30027 "slideshow" : true,
30030 * Fires when this region slides out of view.
30031 * @param {Roo.LayoutRegion} this
30033 "slidehide" : true,
30035 * @event panelactivated
30036 * Fires when a panel is activated.
30037 * @param {Roo.LayoutRegion} this
30038 * @param {Roo.ContentPanel} panel The activated panel
30040 "panelactivated" : true,
30043 * Fires when the user resizes this region.
30044 * @param {Roo.LayoutRegion} this
30045 * @param {Number} newSize The new size (width for east/west, height for north/south)
30049 /** A collection of panels in this region. @type Roo.util.MixedCollection */
30050 this.panels = new Roo.util.MixedCollection();
30051 this.panels.getKey = this.getPanelId.createDelegate(this);
30053 this.activePanel = null;
30054 // ensure listeners are added...
30056 if (config.listeners || config.events) {
30057 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30058 listeners : config.listeners || {},
30059 events : config.events || {}
30063 if(skipConfig !== true){
30064 this.applyConfig(config);
30068 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30069 getPanelId : function(p){
30073 applyConfig : function(config){
30074 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30075 this.config = config;
30080 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
30081 * the width, for horizontal (north, south) the height.
30082 * @param {Number} newSize The new width or height
30084 resizeTo : function(newSize){
30085 var el = this.el ? this.el :
30086 (this.activePanel ? this.activePanel.getEl() : null);
30088 switch(this.position){
30091 el.setWidth(newSize);
30092 this.fireEvent("resized", this, newSize);
30096 el.setHeight(newSize);
30097 this.fireEvent("resized", this, newSize);
30103 getBox : function(){
30104 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30107 getMargins : function(){
30108 return this.margins;
30111 updateBox : function(box){
30113 var el = this.activePanel.getEl();
30114 el.dom.style.left = box.x + "px";
30115 el.dom.style.top = box.y + "px";
30116 this.activePanel.setSize(box.width, box.height);
30120 * Returns the container element for this region.
30121 * @return {Roo.Element}
30123 getEl : function(){
30124 return this.activePanel;
30128 * Returns true if this region is currently visible.
30129 * @return {Boolean}
30131 isVisible : function(){
30132 return this.activePanel ? true : false;
30135 setActivePanel : function(panel){
30136 panel = this.getPanel(panel);
30137 if(this.activePanel && this.activePanel != panel){
30138 this.activePanel.setActiveState(false);
30139 this.activePanel.getEl().setLeftTop(-10000,-10000);
30141 this.activePanel = panel;
30142 panel.setActiveState(true);
30144 panel.setSize(this.box.width, this.box.height);
30146 this.fireEvent("panelactivated", this, panel);
30147 this.fireEvent("invalidated");
30151 * Show the specified panel.
30152 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30153 * @return {Roo.ContentPanel} The shown panel or null
30155 showPanel : function(panel){
30156 if(panel = this.getPanel(panel)){
30157 this.setActivePanel(panel);
30163 * Get the active panel for this region.
30164 * @return {Roo.ContentPanel} The active panel or null
30166 getActivePanel : function(){
30167 return this.activePanel;
30171 * Add the passed ContentPanel(s)
30172 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30173 * @return {Roo.ContentPanel} The panel added (if only one was added)
30175 add : function(panel){
30176 if(arguments.length > 1){
30177 for(var i = 0, len = arguments.length; i < len; i++) {
30178 this.add(arguments[i]);
30182 if(this.hasPanel(panel)){
30183 this.showPanel(panel);
30186 var el = panel.getEl();
30187 if(el.dom.parentNode != this.mgr.el.dom){
30188 this.mgr.el.dom.appendChild(el.dom);
30190 if(panel.setRegion){
30191 panel.setRegion(this);
30193 this.panels.add(panel);
30194 el.setStyle("position", "absolute");
30195 if(!panel.background){
30196 this.setActivePanel(panel);
30197 if(this.config.initialSize && this.panels.getCount()==1){
30198 this.resizeTo(this.config.initialSize);
30201 this.fireEvent("paneladded", this, panel);
30206 * Returns true if the panel is in this region.
30207 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30208 * @return {Boolean}
30210 hasPanel : function(panel){
30211 if(typeof panel == "object"){ // must be panel obj
30212 panel = panel.getId();
30214 return this.getPanel(panel) ? true : false;
30218 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30219 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30220 * @param {Boolean} preservePanel Overrides the config preservePanel option
30221 * @return {Roo.ContentPanel} The panel that was removed
30223 remove : function(panel, preservePanel){
30224 panel = this.getPanel(panel);
30229 this.fireEvent("beforeremove", this, panel, e);
30230 if(e.cancel === true){
30233 var panelId = panel.getId();
30234 this.panels.removeKey(panelId);
30239 * Returns the panel specified or null if it's not in this region.
30240 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30241 * @return {Roo.ContentPanel}
30243 getPanel : function(id){
30244 if(typeof id == "object"){ // must be panel obj
30247 return this.panels.get(id);
30251 * Returns this regions position (north/south/east/west/center).
30254 getPosition: function(){
30255 return this.position;
30259 * Ext JS Library 1.1.1
30260 * Copyright(c) 2006-2007, Ext JS, LLC.
30262 * Originally Released Under LGPL - original licence link has changed is not relivant.
30265 * <script type="text/javascript">
30269 * @class Roo.LayoutRegion
30270 * @extends Roo.BasicLayoutRegion
30271 * This class represents a region in a layout manager.
30272 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30273 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30274 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30275 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30276 * @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})
30277 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30278 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30279 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30280 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30281 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30282 * @cfg {String} title The title for the region (overrides panel titles)
30283 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30284 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30285 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30286 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30287 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30288 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30289 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30290 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30291 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30292 * @cfg {Boolean} showPin True to show a pin button
30293 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30294 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30295 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30296 * @cfg {Number} width For East/West panels
30297 * @cfg {Number} height For North/South panels
30298 * @cfg {Boolean} split To show the splitter
30299 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30301 Roo.LayoutRegion = function(mgr, config, pos){
30302 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30303 var dh = Roo.DomHelper;
30304 /** This region's container element
30305 * @type Roo.Element */
30306 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30307 /** This region's title element
30308 * @type Roo.Element */
30310 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30311 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30312 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30314 this.titleEl.enableDisplayMode();
30315 /** This region's title text element
30316 * @type HTMLElement */
30317 this.titleTextEl = this.titleEl.dom.firstChild;
30318 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30319 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30320 this.closeBtn.enableDisplayMode();
30321 this.closeBtn.on("click", this.closeClicked, this);
30322 this.closeBtn.hide();
30324 this.createBody(config);
30325 this.visible = true;
30326 this.collapsed = false;
30328 if(config.hideWhenEmpty){
30330 this.on("paneladded", this.validateVisibility, this);
30331 this.on("panelremoved", this.validateVisibility, this);
30333 this.applyConfig(config);
30336 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30338 createBody : function(){
30339 /** This region's body element
30340 * @type Roo.Element */
30341 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30344 applyConfig : function(c){
30345 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30346 var dh = Roo.DomHelper;
30347 if(c.titlebar !== false){
30348 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30349 this.collapseBtn.on("click", this.collapse, this);
30350 this.collapseBtn.enableDisplayMode();
30352 if(c.showPin === true || this.showPin){
30353 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30354 this.stickBtn.enableDisplayMode();
30355 this.stickBtn.on("click", this.expand, this);
30356 this.stickBtn.hide();
30359 /** This region's collapsed element
30360 * @type Roo.Element */
30361 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30362 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30364 if(c.floatable !== false){
30365 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30366 this.collapsedEl.on("click", this.collapseClick, this);
30369 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30370 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30371 id: "message", unselectable: "on", style:{"float":"left"}});
30372 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30374 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30375 this.expandBtn.on("click", this.expand, this);
30377 if(this.collapseBtn){
30378 this.collapseBtn.setVisible(c.collapsible == true);
30380 this.cmargins = c.cmargins || this.cmargins ||
30381 (this.position == "west" || this.position == "east" ?
30382 {top: 0, left: 2, right:2, bottom: 0} :
30383 {top: 2, left: 0, right:0, bottom: 2});
30384 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30385 this.bottomTabs = c.tabPosition != "top";
30386 this.autoScroll = c.autoScroll || false;
30387 if(this.autoScroll){
30388 this.bodyEl.setStyle("overflow", "auto");
30390 this.bodyEl.setStyle("overflow", "hidden");
30392 //if(c.titlebar !== false){
30393 if((!c.titlebar && !c.title) || c.titlebar === false){
30394 this.titleEl.hide();
30396 this.titleEl.show();
30398 this.titleTextEl.innerHTML = c.title;
30402 this.duration = c.duration || .30;
30403 this.slideDuration = c.slideDuration || .45;
30406 this.collapse(true);
30413 * Returns true if this region is currently visible.
30414 * @return {Boolean}
30416 isVisible : function(){
30417 return this.visible;
30421 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30422 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30424 setCollapsedTitle : function(title){
30425 title = title || " ";
30426 if(this.collapsedTitleTextEl){
30427 this.collapsedTitleTextEl.innerHTML = title;
30431 getBox : function(){
30433 if(!this.collapsed){
30434 b = this.el.getBox(false, true);
30436 b = this.collapsedEl.getBox(false, true);
30441 getMargins : function(){
30442 return this.collapsed ? this.cmargins : this.margins;
30445 highlight : function(){
30446 this.el.addClass("x-layout-panel-dragover");
30449 unhighlight : function(){
30450 this.el.removeClass("x-layout-panel-dragover");
30453 updateBox : function(box){
30455 if(!this.collapsed){
30456 this.el.dom.style.left = box.x + "px";
30457 this.el.dom.style.top = box.y + "px";
30458 this.updateBody(box.width, box.height);
30460 this.collapsedEl.dom.style.left = box.x + "px";
30461 this.collapsedEl.dom.style.top = box.y + "px";
30462 this.collapsedEl.setSize(box.width, box.height);
30465 this.tabs.autoSizeTabs();
30469 updateBody : function(w, h){
30471 this.el.setWidth(w);
30472 w -= this.el.getBorderWidth("rl");
30473 if(this.config.adjustments){
30474 w += this.config.adjustments[0];
30478 this.el.setHeight(h);
30479 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30480 h -= this.el.getBorderWidth("tb");
30481 if(this.config.adjustments){
30482 h += this.config.adjustments[1];
30484 this.bodyEl.setHeight(h);
30486 h = this.tabs.syncHeight(h);
30489 if(this.panelSize){
30490 w = w !== null ? w : this.panelSize.width;
30491 h = h !== null ? h : this.panelSize.height;
30493 if(this.activePanel){
30494 var el = this.activePanel.getEl();
30495 w = w !== null ? w : el.getWidth();
30496 h = h !== null ? h : el.getHeight();
30497 this.panelSize = {width: w, height: h};
30498 this.activePanel.setSize(w, h);
30500 if(Roo.isIE && this.tabs){
30501 this.tabs.el.repaint();
30506 * Returns the container element for this region.
30507 * @return {Roo.Element}
30509 getEl : function(){
30514 * Hides this region.
30517 if(!this.collapsed){
30518 this.el.dom.style.left = "-2000px";
30521 this.collapsedEl.dom.style.left = "-2000px";
30522 this.collapsedEl.hide();
30524 this.visible = false;
30525 this.fireEvent("visibilitychange", this, false);
30529 * Shows this region if it was previously hidden.
30532 if(!this.collapsed){
30535 this.collapsedEl.show();
30537 this.visible = true;
30538 this.fireEvent("visibilitychange", this, true);
30541 closeClicked : function(){
30542 if(this.activePanel){
30543 this.remove(this.activePanel);
30547 collapseClick : function(e){
30549 e.stopPropagation();
30552 e.stopPropagation();
30558 * Collapses this region.
30559 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30561 collapse : function(skipAnim, skipCheck){
30562 if(this.collapsed) {
30566 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30568 this.collapsed = true;
30570 this.split.el.hide();
30572 if(this.config.animate && skipAnim !== true){
30573 this.fireEvent("invalidated", this);
30574 this.animateCollapse();
30576 this.el.setLocation(-20000,-20000);
30578 this.collapsedEl.show();
30579 this.fireEvent("collapsed", this);
30580 this.fireEvent("invalidated", this);
30586 animateCollapse : function(){
30591 * Expands this region if it was previously collapsed.
30592 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30593 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30595 expand : function(e, skipAnim){
30597 e.stopPropagation();
30599 if(!this.collapsed || this.el.hasActiveFx()) {
30603 this.afterSlideIn();
30606 this.collapsed = false;
30607 if(this.config.animate && skipAnim !== true){
30608 this.animateExpand();
30612 this.split.el.show();
30614 this.collapsedEl.setLocation(-2000,-2000);
30615 this.collapsedEl.hide();
30616 this.fireEvent("invalidated", this);
30617 this.fireEvent("expanded", this);
30621 animateExpand : function(){
30625 initTabs : function()
30627 this.bodyEl.setStyle("overflow", "hidden");
30628 var ts = new Roo.TabPanel(
30631 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30632 disableTooltips: this.config.disableTabTips,
30633 toolbar : this.config.toolbar
30636 if(this.config.hideTabs){
30637 ts.stripWrap.setDisplayed(false);
30640 ts.resizeTabs = this.config.resizeTabs === true;
30641 ts.minTabWidth = this.config.minTabWidth || 40;
30642 ts.maxTabWidth = this.config.maxTabWidth || 250;
30643 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30644 ts.monitorResize = false;
30645 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30646 ts.bodyEl.addClass('x-layout-tabs-body');
30647 this.panels.each(this.initPanelAsTab, this);
30650 initPanelAsTab : function(panel){
30651 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30652 this.config.closeOnTab && panel.isClosable());
30653 if(panel.tabTip !== undefined){
30654 ti.setTooltip(panel.tabTip);
30656 ti.on("activate", function(){
30657 this.setActivePanel(panel);
30659 if(this.config.closeOnTab){
30660 ti.on("beforeclose", function(t, e){
30662 this.remove(panel);
30668 updatePanelTitle : function(panel, title){
30669 if(this.activePanel == panel){
30670 this.updateTitle(title);
30673 var ti = this.tabs.getTab(panel.getEl().id);
30675 if(panel.tabTip !== undefined){
30676 ti.setTooltip(panel.tabTip);
30681 updateTitle : function(title){
30682 if(this.titleTextEl && !this.config.title){
30683 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30687 setActivePanel : function(panel){
30688 panel = this.getPanel(panel);
30689 if(this.activePanel && this.activePanel != panel){
30690 this.activePanel.setActiveState(false);
30692 this.activePanel = panel;
30693 panel.setActiveState(true);
30694 if(this.panelSize){
30695 panel.setSize(this.panelSize.width, this.panelSize.height);
30698 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30700 this.updateTitle(panel.getTitle());
30702 this.fireEvent("invalidated", this);
30704 this.fireEvent("panelactivated", this, panel);
30708 * Shows the specified panel.
30709 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30710 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30712 showPanel : function(panel)
30714 panel = this.getPanel(panel);
30717 var tab = this.tabs.getTab(panel.getEl().id);
30718 if(tab.isHidden()){
30719 this.tabs.unhideTab(tab.id);
30723 this.setActivePanel(panel);
30730 * Get the active panel for this region.
30731 * @return {Roo.ContentPanel} The active panel or null
30733 getActivePanel : function(){
30734 return this.activePanel;
30737 validateVisibility : function(){
30738 if(this.panels.getCount() < 1){
30739 this.updateTitle(" ");
30740 this.closeBtn.hide();
30743 if(!this.isVisible()){
30750 * Adds the passed ContentPanel(s) to this region.
30751 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30752 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30754 add : function(panel){
30755 if(arguments.length > 1){
30756 for(var i = 0, len = arguments.length; i < len; i++) {
30757 this.add(arguments[i]);
30761 if(this.hasPanel(panel)){
30762 this.showPanel(panel);
30765 panel.setRegion(this);
30766 this.panels.add(panel);
30767 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30768 this.bodyEl.dom.appendChild(panel.getEl().dom);
30769 if(panel.background !== true){
30770 this.setActivePanel(panel);
30772 this.fireEvent("paneladded", this, panel);
30778 this.initPanelAsTab(panel);
30780 if(panel.background !== true){
30781 this.tabs.activate(panel.getEl().id);
30783 this.fireEvent("paneladded", this, panel);
30788 * Hides the tab for the specified panel.
30789 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30791 hidePanel : function(panel){
30792 if(this.tabs && (panel = this.getPanel(panel))){
30793 this.tabs.hideTab(panel.getEl().id);
30798 * Unhides the tab for a previously hidden panel.
30799 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30801 unhidePanel : function(panel){
30802 if(this.tabs && (panel = this.getPanel(panel))){
30803 this.tabs.unhideTab(panel.getEl().id);
30807 clearPanels : function(){
30808 while(this.panels.getCount() > 0){
30809 this.remove(this.panels.first());
30814 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30815 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30816 * @param {Boolean} preservePanel Overrides the config preservePanel option
30817 * @return {Roo.ContentPanel} The panel that was removed
30819 remove : function(panel, preservePanel){
30820 panel = this.getPanel(panel);
30825 this.fireEvent("beforeremove", this, panel, e);
30826 if(e.cancel === true){
30829 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30830 var panelId = panel.getId();
30831 this.panels.removeKey(panelId);
30833 document.body.appendChild(panel.getEl().dom);
30836 this.tabs.removeTab(panel.getEl().id);
30837 }else if (!preservePanel){
30838 this.bodyEl.dom.removeChild(panel.getEl().dom);
30840 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30841 var p = this.panels.first();
30842 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30843 tempEl.appendChild(p.getEl().dom);
30844 this.bodyEl.update("");
30845 this.bodyEl.dom.appendChild(p.getEl().dom);
30847 this.updateTitle(p.getTitle());
30849 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30850 this.setActivePanel(p);
30852 panel.setRegion(null);
30853 if(this.activePanel == panel){
30854 this.activePanel = null;
30856 if(this.config.autoDestroy !== false && preservePanel !== true){
30857 try{panel.destroy();}catch(e){}
30859 this.fireEvent("panelremoved", this, panel);
30864 * Returns the TabPanel component used by this region
30865 * @return {Roo.TabPanel}
30867 getTabs : function(){
30871 createTool : function(parentEl, className){
30872 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30873 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30874 btn.addClassOnOver("x-layout-tools-button-over");
30879 * Ext JS Library 1.1.1
30880 * Copyright(c) 2006-2007, Ext JS, LLC.
30882 * Originally Released Under LGPL - original licence link has changed is not relivant.
30885 * <script type="text/javascript">
30891 * @class Roo.SplitLayoutRegion
30892 * @extends Roo.LayoutRegion
30893 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30895 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30896 this.cursor = cursor;
30897 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30900 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30901 splitTip : "Drag to resize.",
30902 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30903 useSplitTips : false,
30905 applyConfig : function(config){
30906 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30909 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30910 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30911 /** The SplitBar for this region
30912 * @type Roo.SplitBar */
30913 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30914 this.split.on("moved", this.onSplitMove, this);
30915 this.split.useShim = config.useShim === true;
30916 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30917 if(this.useSplitTips){
30918 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30920 if(config.collapsible){
30921 this.split.el.on("dblclick", this.collapse, this);
30924 if(typeof config.minSize != "undefined"){
30925 this.split.minSize = config.minSize;
30927 if(typeof config.maxSize != "undefined"){
30928 this.split.maxSize = config.maxSize;
30930 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30931 this.hideSplitter();
30936 getHMaxSize : function(){
30937 var cmax = this.config.maxSize || 10000;
30938 var center = this.mgr.getRegion("center");
30939 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30942 getVMaxSize : function(){
30943 var cmax = this.config.maxSize || 10000;
30944 var center = this.mgr.getRegion("center");
30945 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30948 onSplitMove : function(split, newSize){
30949 this.fireEvent("resized", this, newSize);
30953 * Returns the {@link Roo.SplitBar} for this region.
30954 * @return {Roo.SplitBar}
30956 getSplitBar : function(){
30961 this.hideSplitter();
30962 Roo.SplitLayoutRegion.superclass.hide.call(this);
30965 hideSplitter : function(){
30967 this.split.el.setLocation(-2000,-2000);
30968 this.split.el.hide();
30974 this.split.el.show();
30976 Roo.SplitLayoutRegion.superclass.show.call(this);
30979 beforeSlide: function(){
30980 if(Roo.isGecko){// firefox overflow auto bug workaround
30981 this.bodyEl.clip();
30983 this.tabs.bodyEl.clip();
30985 if(this.activePanel){
30986 this.activePanel.getEl().clip();
30988 if(this.activePanel.beforeSlide){
30989 this.activePanel.beforeSlide();
30995 afterSlide : function(){
30996 if(Roo.isGecko){// firefox overflow auto bug workaround
30997 this.bodyEl.unclip();
30999 this.tabs.bodyEl.unclip();
31001 if(this.activePanel){
31002 this.activePanel.getEl().unclip();
31003 if(this.activePanel.afterSlide){
31004 this.activePanel.afterSlide();
31010 initAutoHide : function(){
31011 if(this.autoHide !== false){
31012 if(!this.autoHideHd){
31013 var st = new Roo.util.DelayedTask(this.slideIn, this);
31014 this.autoHideHd = {
31015 "mouseout": function(e){
31016 if(!e.within(this.el, true)){
31020 "mouseover" : function(e){
31026 this.el.on(this.autoHideHd);
31030 clearAutoHide : function(){
31031 if(this.autoHide !== false){
31032 this.el.un("mouseout", this.autoHideHd.mouseout);
31033 this.el.un("mouseover", this.autoHideHd.mouseover);
31037 clearMonitor : function(){
31038 Roo.get(document).un("click", this.slideInIf, this);
31041 // these names are backwards but not changed for compat
31042 slideOut : function(){
31043 if(this.isSlid || this.el.hasActiveFx()){
31046 this.isSlid = true;
31047 if(this.collapseBtn){
31048 this.collapseBtn.hide();
31050 this.closeBtnState = this.closeBtn.getStyle('display');
31051 this.closeBtn.hide();
31053 this.stickBtn.show();
31056 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31057 this.beforeSlide();
31058 this.el.setStyle("z-index", 10001);
31059 this.el.slideIn(this.getSlideAnchor(), {
31060 callback: function(){
31062 this.initAutoHide();
31063 Roo.get(document).on("click", this.slideInIf, this);
31064 this.fireEvent("slideshow", this);
31071 afterSlideIn : function(){
31072 this.clearAutoHide();
31073 this.isSlid = false;
31074 this.clearMonitor();
31075 this.el.setStyle("z-index", "");
31076 if(this.collapseBtn){
31077 this.collapseBtn.show();
31079 this.closeBtn.setStyle('display', this.closeBtnState);
31081 this.stickBtn.hide();
31083 this.fireEvent("slidehide", this);
31086 slideIn : function(cb){
31087 if(!this.isSlid || this.el.hasActiveFx()){
31091 this.isSlid = false;
31092 this.beforeSlide();
31093 this.el.slideOut(this.getSlideAnchor(), {
31094 callback: function(){
31095 this.el.setLeftTop(-10000, -10000);
31097 this.afterSlideIn();
31105 slideInIf : function(e){
31106 if(!e.within(this.el)){
31111 animateCollapse : function(){
31112 this.beforeSlide();
31113 this.el.setStyle("z-index", 20000);
31114 var anchor = this.getSlideAnchor();
31115 this.el.slideOut(anchor, {
31116 callback : function(){
31117 this.el.setStyle("z-index", "");
31118 this.collapsedEl.slideIn(anchor, {duration:.3});
31120 this.el.setLocation(-10000,-10000);
31122 this.fireEvent("collapsed", this);
31129 animateExpand : function(){
31130 this.beforeSlide();
31131 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31132 this.el.setStyle("z-index", 20000);
31133 this.collapsedEl.hide({
31136 this.el.slideIn(this.getSlideAnchor(), {
31137 callback : function(){
31138 this.el.setStyle("z-index", "");
31141 this.split.el.show();
31143 this.fireEvent("invalidated", this);
31144 this.fireEvent("expanded", this);
31172 getAnchor : function(){
31173 return this.anchors[this.position];
31176 getCollapseAnchor : function(){
31177 return this.canchors[this.position];
31180 getSlideAnchor : function(){
31181 return this.sanchors[this.position];
31184 getAlignAdj : function(){
31185 var cm = this.cmargins;
31186 switch(this.position){
31202 getExpandAdj : function(){
31203 var c = this.collapsedEl, cm = this.cmargins;
31204 switch(this.position){
31206 return [-(cm.right+c.getWidth()+cm.left), 0];
31209 return [cm.right+c.getWidth()+cm.left, 0];
31212 return [0, -(cm.top+cm.bottom+c.getHeight())];
31215 return [0, cm.top+cm.bottom+c.getHeight()];
31221 * Ext JS Library 1.1.1
31222 * Copyright(c) 2006-2007, Ext JS, LLC.
31224 * Originally Released Under LGPL - original licence link has changed is not relivant.
31227 * <script type="text/javascript">
31230 * These classes are private internal classes
31232 Roo.CenterLayoutRegion = function(mgr, config){
31233 Roo.LayoutRegion.call(this, mgr, config, "center");
31234 this.visible = true;
31235 this.minWidth = config.minWidth || 20;
31236 this.minHeight = config.minHeight || 20;
31239 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31241 // center panel can't be hidden
31245 // center panel can't be hidden
31248 getMinWidth: function(){
31249 return this.minWidth;
31252 getMinHeight: function(){
31253 return this.minHeight;
31258 Roo.NorthLayoutRegion = function(mgr, config){
31259 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31261 this.split.placement = Roo.SplitBar.TOP;
31262 this.split.orientation = Roo.SplitBar.VERTICAL;
31263 this.split.el.addClass("x-layout-split-v");
31265 var size = config.initialSize || config.height;
31266 if(typeof size != "undefined"){
31267 this.el.setHeight(size);
31270 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31271 orientation: Roo.SplitBar.VERTICAL,
31272 getBox : function(){
31273 if(this.collapsed){
31274 return this.collapsedEl.getBox();
31276 var box = this.el.getBox();
31278 box.height += this.split.el.getHeight();
31283 updateBox : function(box){
31284 if(this.split && !this.collapsed){
31285 box.height -= this.split.el.getHeight();
31286 this.split.el.setLeft(box.x);
31287 this.split.el.setTop(box.y+box.height);
31288 this.split.el.setWidth(box.width);
31290 if(this.collapsed){
31291 this.updateBody(box.width, null);
31293 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31297 Roo.SouthLayoutRegion = function(mgr, config){
31298 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31300 this.split.placement = Roo.SplitBar.BOTTOM;
31301 this.split.orientation = Roo.SplitBar.VERTICAL;
31302 this.split.el.addClass("x-layout-split-v");
31304 var size = config.initialSize || config.height;
31305 if(typeof size != "undefined"){
31306 this.el.setHeight(size);
31309 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31310 orientation: Roo.SplitBar.VERTICAL,
31311 getBox : function(){
31312 if(this.collapsed){
31313 return this.collapsedEl.getBox();
31315 var box = this.el.getBox();
31317 var sh = this.split.el.getHeight();
31324 updateBox : function(box){
31325 if(this.split && !this.collapsed){
31326 var sh = this.split.el.getHeight();
31329 this.split.el.setLeft(box.x);
31330 this.split.el.setTop(box.y-sh);
31331 this.split.el.setWidth(box.width);
31333 if(this.collapsed){
31334 this.updateBody(box.width, null);
31336 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31340 Roo.EastLayoutRegion = function(mgr, config){
31341 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31343 this.split.placement = Roo.SplitBar.RIGHT;
31344 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31345 this.split.el.addClass("x-layout-split-h");
31347 var size = config.initialSize || config.width;
31348 if(typeof size != "undefined"){
31349 this.el.setWidth(size);
31352 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31353 orientation: Roo.SplitBar.HORIZONTAL,
31354 getBox : function(){
31355 if(this.collapsed){
31356 return this.collapsedEl.getBox();
31358 var box = this.el.getBox();
31360 var sw = this.split.el.getWidth();
31367 updateBox : function(box){
31368 if(this.split && !this.collapsed){
31369 var sw = this.split.el.getWidth();
31371 this.split.el.setLeft(box.x);
31372 this.split.el.setTop(box.y);
31373 this.split.el.setHeight(box.height);
31376 if(this.collapsed){
31377 this.updateBody(null, box.height);
31379 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31383 Roo.WestLayoutRegion = function(mgr, config){
31384 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31386 this.split.placement = Roo.SplitBar.LEFT;
31387 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31388 this.split.el.addClass("x-layout-split-h");
31390 var size = config.initialSize || config.width;
31391 if(typeof size != "undefined"){
31392 this.el.setWidth(size);
31395 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31396 orientation: Roo.SplitBar.HORIZONTAL,
31397 getBox : function(){
31398 if(this.collapsed){
31399 return this.collapsedEl.getBox();
31401 var box = this.el.getBox();
31403 box.width += this.split.el.getWidth();
31408 updateBox : function(box){
31409 if(this.split && !this.collapsed){
31410 var sw = this.split.el.getWidth();
31412 this.split.el.setLeft(box.x+box.width);
31413 this.split.el.setTop(box.y);
31414 this.split.el.setHeight(box.height);
31416 if(this.collapsed){
31417 this.updateBody(null, box.height);
31419 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31424 * Ext JS Library 1.1.1
31425 * Copyright(c) 2006-2007, Ext JS, LLC.
31427 * Originally Released Under LGPL - original licence link has changed is not relivant.
31430 * <script type="text/javascript">
31435 * Private internal class for reading and applying state
31437 Roo.LayoutStateManager = function(layout){
31438 // default empty state
31447 Roo.LayoutStateManager.prototype = {
31448 init : function(layout, provider){
31449 this.provider = provider;
31450 var state = provider.get(layout.id+"-layout-state");
31452 var wasUpdating = layout.isUpdating();
31454 layout.beginUpdate();
31456 for(var key in state){
31457 if(typeof state[key] != "function"){
31458 var rstate = state[key];
31459 var r = layout.getRegion(key);
31462 r.resizeTo(rstate.size);
31464 if(rstate.collapsed == true){
31467 r.expand(null, true);
31473 layout.endUpdate();
31475 this.state = state;
31477 this.layout = layout;
31478 layout.on("regionresized", this.onRegionResized, this);
31479 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31480 layout.on("regionexpanded", this.onRegionExpanded, this);
31483 storeState : function(){
31484 this.provider.set(this.layout.id+"-layout-state", this.state);
31487 onRegionResized : function(region, newSize){
31488 this.state[region.getPosition()].size = newSize;
31492 onRegionCollapsed : function(region){
31493 this.state[region.getPosition()].collapsed = true;
31497 onRegionExpanded : function(region){
31498 this.state[region.getPosition()].collapsed = false;
31503 * Ext JS Library 1.1.1
31504 * Copyright(c) 2006-2007, Ext JS, LLC.
31506 * Originally Released Under LGPL - original licence link has changed is not relivant.
31509 * <script type="text/javascript">
31512 * @class Roo.ContentPanel
31513 * @extends Roo.util.Observable
31514 * A basic ContentPanel element.
31515 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31516 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31517 * @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
31518 * @cfg {Boolean} closable True if the panel can be closed/removed
31519 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31520 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31521 * @cfg {Toolbar} toolbar A toolbar for this panel
31522 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31523 * @cfg {String} title The title for this panel
31524 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31525 * @cfg {String} url Calls {@link #setUrl} with this value
31526 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31527 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31528 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31529 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31532 * Create a new ContentPanel.
31533 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31534 * @param {String/Object} config A string to set only the title or a config object
31535 * @param {String} content (optional) Set the HTML content for this panel
31536 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31538 Roo.ContentPanel = function(el, config, content){
31542 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31546 if (config && config.parentLayout) {
31547 el = config.parentLayout.el.createChild();
31550 if(el.autoCreate){ // xtype is available if this is called from factory
31554 this.el = Roo.get(el);
31555 if(!this.el && config && config.autoCreate){
31556 if(typeof config.autoCreate == "object"){
31557 if(!config.autoCreate.id){
31558 config.autoCreate.id = config.id||el;
31560 this.el = Roo.DomHelper.append(document.body,
31561 config.autoCreate, true);
31563 this.el = Roo.DomHelper.append(document.body,
31564 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31567 this.closable = false;
31568 this.loaded = false;
31569 this.active = false;
31570 if(typeof config == "string"){
31571 this.title = config;
31573 Roo.apply(this, config);
31576 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31577 this.wrapEl = this.el.wrap();
31578 this.toolbar.container = this.el.insertSibling(false, 'before');
31579 this.toolbar = new Roo.Toolbar(this.toolbar);
31582 // xtype created footer. - not sure if will work as we normally have to render first..
31583 if (this.footer && !this.footer.el && this.footer.xtype) {
31584 if (!this.wrapEl) {
31585 this.wrapEl = this.el.wrap();
31588 this.footer.container = this.wrapEl.createChild();
31590 this.footer = Roo.factory(this.footer, Roo);
31595 this.resizeEl = Roo.get(this.resizeEl, true);
31597 this.resizeEl = this.el;
31599 // handle view.xtype
31607 * Fires when this panel is activated.
31608 * @param {Roo.ContentPanel} this
31612 * @event deactivate
31613 * Fires when this panel is activated.
31614 * @param {Roo.ContentPanel} this
31616 "deactivate" : true,
31620 * Fires when this panel is resized if fitToFrame is true.
31621 * @param {Roo.ContentPanel} this
31622 * @param {Number} width The width after any component adjustments
31623 * @param {Number} height The height after any component adjustments
31629 * Fires when this tab is created
31630 * @param {Roo.ContentPanel} this
31640 if(this.autoScroll){
31641 this.resizeEl.setStyle("overflow", "auto");
31643 // fix randome scrolling
31644 this.el.on('scroll', function() {
31645 Roo.log('fix random scolling');
31646 this.scrollTo('top',0);
31649 content = content || this.content;
31651 this.setContent(content);
31653 if(config && config.url){
31654 this.setUrl(this.url, this.params, this.loadOnce);
31659 Roo.ContentPanel.superclass.constructor.call(this);
31661 if (this.view && typeof(this.view.xtype) != 'undefined') {
31662 this.view.el = this.el.appendChild(document.createElement("div"));
31663 this.view = Roo.factory(this.view);
31664 this.view.render && this.view.render(false, '');
31668 this.fireEvent('render', this);
31671 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31673 setRegion : function(region){
31674 this.region = region;
31676 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31678 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31683 * Returns the toolbar for this Panel if one was configured.
31684 * @return {Roo.Toolbar}
31686 getToolbar : function(){
31687 return this.toolbar;
31690 setActiveState : function(active){
31691 this.active = active;
31693 this.fireEvent("deactivate", this);
31695 this.fireEvent("activate", this);
31699 * Updates this panel's element
31700 * @param {String} content The new content
31701 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31703 setContent : function(content, loadScripts){
31704 this.el.update(content, loadScripts);
31707 ignoreResize : function(w, h){
31708 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31711 this.lastSize = {width: w, height: h};
31716 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31717 * @return {Roo.UpdateManager} The UpdateManager
31719 getUpdateManager : function(){
31720 return this.el.getUpdateManager();
31723 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31724 * @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:
31727 url: "your-url.php",
31728 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31729 callback: yourFunction,
31730 scope: yourObject, //(optional scope)
31733 text: "Loading...",
31738 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31739 * 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.
31740 * @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}
31741 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31742 * @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.
31743 * @return {Roo.ContentPanel} this
31746 var um = this.el.getUpdateManager();
31747 um.update.apply(um, arguments);
31753 * 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.
31754 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31755 * @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)
31756 * @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)
31757 * @return {Roo.UpdateManager} The UpdateManager
31759 setUrl : function(url, params, loadOnce){
31760 if(this.refreshDelegate){
31761 this.removeListener("activate", this.refreshDelegate);
31763 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31764 this.on("activate", this.refreshDelegate);
31765 return this.el.getUpdateManager();
31768 _handleRefresh : function(url, params, loadOnce){
31769 if(!loadOnce || !this.loaded){
31770 var updater = this.el.getUpdateManager();
31771 updater.update(url, params, this._setLoaded.createDelegate(this));
31775 _setLoaded : function(){
31776 this.loaded = true;
31780 * Returns this panel's id
31783 getId : function(){
31788 * Returns this panel's element - used by regiosn to add.
31789 * @return {Roo.Element}
31791 getEl : function(){
31792 return this.wrapEl || this.el;
31795 adjustForComponents : function(width, height)
31797 //Roo.log('adjustForComponents ');
31798 if(this.resizeEl != this.el){
31799 width -= this.el.getFrameWidth('lr');
31800 height -= this.el.getFrameWidth('tb');
31803 var te = this.toolbar.getEl();
31804 height -= te.getHeight();
31805 te.setWidth(width);
31808 var te = this.footer.getEl();
31809 //Roo.log("footer:" + te.getHeight());
31811 height -= te.getHeight();
31812 te.setWidth(width);
31816 if(this.adjustments){
31817 width += this.adjustments[0];
31818 height += this.adjustments[1];
31820 return {"width": width, "height": height};
31823 setSize : function(width, height){
31824 if(this.fitToFrame && !this.ignoreResize(width, height)){
31825 if(this.fitContainer && this.resizeEl != this.el){
31826 this.el.setSize(width, height);
31828 var size = this.adjustForComponents(width, height);
31829 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31830 this.fireEvent('resize', this, size.width, size.height);
31835 * Returns this panel's title
31838 getTitle : function(){
31843 * Set this panel's title
31844 * @param {String} title
31846 setTitle : function(title){
31847 this.title = title;
31849 this.region.updatePanelTitle(this, title);
31854 * Returns true is this panel was configured to be closable
31855 * @return {Boolean}
31857 isClosable : function(){
31858 return this.closable;
31861 beforeSlide : function(){
31863 this.resizeEl.clip();
31866 afterSlide : function(){
31868 this.resizeEl.unclip();
31872 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31873 * Will fail silently if the {@link #setUrl} method has not been called.
31874 * This does not activate the panel, just updates its content.
31876 refresh : function(){
31877 if(this.refreshDelegate){
31878 this.loaded = false;
31879 this.refreshDelegate();
31884 * Destroys this panel
31886 destroy : function(){
31887 this.el.removeAllListeners();
31888 var tempEl = document.createElement("span");
31889 tempEl.appendChild(this.el.dom);
31890 tempEl.innerHTML = "";
31896 * form - if the content panel contains a form - this is a reference to it.
31897 * @type {Roo.form.Form}
31901 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31902 * This contains a reference to it.
31908 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31918 * @param {Object} cfg Xtype definition of item to add.
31921 addxtype : function(cfg) {
31923 if (cfg.xtype.match(/^Form$/)) {
31926 //if (this.footer) {
31927 // el = this.footer.container.insertSibling(false, 'before');
31929 el = this.el.createChild();
31932 this.form = new Roo.form.Form(cfg);
31935 if ( this.form.allItems.length) {
31936 this.form.render(el.dom);
31940 // should only have one of theses..
31941 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31942 // views.. should not be just added - used named prop 'view''
31944 cfg.el = this.el.appendChild(document.createElement("div"));
31947 var ret = new Roo.factory(cfg);
31949 ret.render && ret.render(false, ''); // render blank..
31958 * @class Roo.GridPanel
31959 * @extends Roo.ContentPanel
31961 * Create a new GridPanel.
31962 * @param {Roo.grid.Grid} grid The grid for this panel
31963 * @param {String/Object} config A string to set only the panel's title, or a config object
31965 Roo.GridPanel = function(grid, config){
31968 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31969 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31971 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31973 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31976 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31978 // xtype created footer. - not sure if will work as we normally have to render first..
31979 if (this.footer && !this.footer.el && this.footer.xtype) {
31981 this.footer.container = this.grid.getView().getFooterPanel(true);
31982 this.footer.dataSource = this.grid.dataSource;
31983 this.footer = Roo.factory(this.footer, Roo);
31987 grid.monitorWindowResize = false; // turn off autosizing
31988 grid.autoHeight = false;
31989 grid.autoWidth = false;
31991 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31994 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31995 getId : function(){
31996 return this.grid.id;
32000 * Returns the grid for this panel
32001 * @return {Roo.grid.Grid}
32003 getGrid : function(){
32007 setSize : function(width, height){
32008 if(!this.ignoreResize(width, height)){
32009 var grid = this.grid;
32010 var size = this.adjustForComponents(width, height);
32011 grid.getGridEl().setSize(size.width, size.height);
32016 beforeSlide : function(){
32017 this.grid.getView().scroller.clip();
32020 afterSlide : function(){
32021 this.grid.getView().scroller.unclip();
32024 destroy : function(){
32025 this.grid.destroy();
32027 Roo.GridPanel.superclass.destroy.call(this);
32033 * @class Roo.NestedLayoutPanel
32034 * @extends Roo.ContentPanel
32036 * Create a new NestedLayoutPanel.
32039 * @param {Roo.BorderLayout} layout The layout for this panel
32040 * @param {String/Object} config A string to set only the title or a config object
32042 Roo.NestedLayoutPanel = function(layout, config)
32044 // construct with only one argument..
32045 /* FIXME - implement nicer consturctors
32046 if (layout.layout) {
32048 layout = config.layout;
32049 delete config.layout;
32051 if (layout.xtype && !layout.getEl) {
32052 // then layout needs constructing..
32053 layout = Roo.factory(layout, Roo);
32058 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32060 layout.monitorWindowResize = false; // turn off autosizing
32061 this.layout = layout;
32062 this.layout.getEl().addClass("x-layout-nested-layout");
32069 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32071 setSize : function(width, height){
32072 if(!this.ignoreResize(width, height)){
32073 var size = this.adjustForComponents(width, height);
32074 var el = this.layout.getEl();
32075 el.setSize(size.width, size.height);
32076 var touch = el.dom.offsetWidth;
32077 this.layout.layout();
32078 // ie requires a double layout on the first pass
32079 if(Roo.isIE && !this.initialized){
32080 this.initialized = true;
32081 this.layout.layout();
32086 // activate all subpanels if not currently active..
32088 setActiveState : function(active){
32089 this.active = active;
32091 this.fireEvent("deactivate", this);
32095 this.fireEvent("activate", this);
32096 // not sure if this should happen before or after..
32097 if (!this.layout) {
32098 return; // should not happen..
32101 for (var r in this.layout.regions) {
32102 reg = this.layout.getRegion(r);
32103 if (reg.getActivePanel()) {
32104 //reg.showPanel(reg.getActivePanel()); // force it to activate..
32105 reg.setActivePanel(reg.getActivePanel());
32108 if (!reg.panels.length) {
32111 reg.showPanel(reg.getPanel(0));
32120 * Returns the nested BorderLayout for this panel
32121 * @return {Roo.BorderLayout}
32123 getLayout : function(){
32124 return this.layout;
32128 * Adds a xtype elements to the layout of the nested panel
32132 xtype : 'ContentPanel',
32139 xtype : 'NestedLayoutPanel',
32145 items : [ ... list of content panels or nested layout panels.. ]
32149 * @param {Object} cfg Xtype definition of item to add.
32151 addxtype : function(cfg) {
32152 return this.layout.addxtype(cfg);
32157 Roo.ScrollPanel = function(el, config, content){
32158 config = config || {};
32159 config.fitToFrame = true;
32160 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32162 this.el.dom.style.overflow = "hidden";
32163 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32164 this.el.removeClass("x-layout-inactive-content");
32165 this.el.on("mousewheel", this.onWheel, this);
32167 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
32168 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
32169 up.unselectable(); down.unselectable();
32170 up.on("click", this.scrollUp, this);
32171 down.on("click", this.scrollDown, this);
32172 up.addClassOnOver("x-scroller-btn-over");
32173 down.addClassOnOver("x-scroller-btn-over");
32174 up.addClassOnClick("x-scroller-btn-click");
32175 down.addClassOnClick("x-scroller-btn-click");
32176 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32178 this.resizeEl = this.el;
32179 this.el = wrap; this.up = up; this.down = down;
32182 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32184 wheelIncrement : 5,
32185 scrollUp : function(){
32186 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32189 scrollDown : function(){
32190 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32193 afterScroll : function(){
32194 var el = this.resizeEl;
32195 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32196 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32197 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32200 setSize : function(){
32201 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32202 this.afterScroll();
32205 onWheel : function(e){
32206 var d = e.getWheelDelta();
32207 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32208 this.afterScroll();
32212 setContent : function(content, loadScripts){
32213 this.resizeEl.update(content, loadScripts);
32227 * @class Roo.TreePanel
32228 * @extends Roo.ContentPanel
32230 * Create a new TreePanel. - defaults to fit/scoll contents.
32231 * @param {String/Object} config A string to set only the panel's title, or a config object
32232 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32234 Roo.TreePanel = function(config){
32235 var el = config.el;
32236 var tree = config.tree;
32237 delete config.tree;
32238 delete config.el; // hopefull!
32240 // wrapper for IE7 strict & safari scroll issue
32242 var treeEl = el.createChild();
32243 config.resizeEl = treeEl;
32247 Roo.TreePanel.superclass.constructor.call(this, el, config);
32250 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32251 //console.log(tree);
32252 this.on('activate', function()
32254 if (this.tree.rendered) {
32257 //console.log('render tree');
32258 this.tree.render();
32260 // this should not be needed.. - it's actually the 'el' that resizes?
32261 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32263 //this.on('resize', function (cp, w, h) {
32264 // this.tree.innerCt.setWidth(w);
32265 // this.tree.innerCt.setHeight(h);
32266 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32273 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32290 * Ext JS Library 1.1.1
32291 * Copyright(c) 2006-2007, Ext JS, LLC.
32293 * Originally Released Under LGPL - original licence link has changed is not relivant.
32296 * <script type="text/javascript">
32301 * @class Roo.ReaderLayout
32302 * @extends Roo.BorderLayout
32303 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32304 * center region containing two nested regions (a top one for a list view and one for item preview below),
32305 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32306 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32307 * expedites the setup of the overall layout and regions for this common application style.
32310 var reader = new Roo.ReaderLayout();
32311 var CP = Roo.ContentPanel; // shortcut for adding
32313 reader.beginUpdate();
32314 reader.add("north", new CP("north", "North"));
32315 reader.add("west", new CP("west", {title: "West"}));
32316 reader.add("east", new CP("east", {title: "East"}));
32318 reader.regions.listView.add(new CP("listView", "List"));
32319 reader.regions.preview.add(new CP("preview", "Preview"));
32320 reader.endUpdate();
32323 * Create a new ReaderLayout
32324 * @param {Object} config Configuration options
32325 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32326 * document.body if omitted)
32328 Roo.ReaderLayout = function(config, renderTo){
32329 var c = config || {size:{}};
32330 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32331 north: c.north !== false ? Roo.apply({
32335 }, c.north) : false,
32336 west: c.west !== false ? Roo.apply({
32344 margins:{left:5,right:0,bottom:5,top:5},
32345 cmargins:{left:5,right:5,bottom:5,top:5}
32346 }, c.west) : false,
32347 east: c.east !== false ? Roo.apply({
32355 margins:{left:0,right:5,bottom:5,top:5},
32356 cmargins:{left:5,right:5,bottom:5,top:5}
32357 }, c.east) : false,
32358 center: Roo.apply({
32359 tabPosition: 'top',
32363 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32367 this.el.addClass('x-reader');
32369 this.beginUpdate();
32371 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32372 south: c.preview !== false ? Roo.apply({
32379 cmargins:{top:5,left:0, right:0, bottom:0}
32380 }, c.preview) : false,
32381 center: Roo.apply({
32387 this.add('center', new Roo.NestedLayoutPanel(inner,
32388 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32392 this.regions.preview = inner.getRegion('south');
32393 this.regions.listView = inner.getRegion('center');
32396 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32398 * Ext JS Library 1.1.1
32399 * Copyright(c) 2006-2007, Ext JS, LLC.
32401 * Originally Released Under LGPL - original licence link has changed is not relivant.
32404 * <script type="text/javascript">
32408 * @class Roo.grid.Grid
32409 * @extends Roo.util.Observable
32410 * This class represents the primary interface of a component based grid control.
32411 * <br><br>Usage:<pre><code>
32412 var grid = new Roo.grid.Grid("my-container-id", {
32415 selModel: mySelectionModel,
32416 autoSizeColumns: true,
32417 monitorWindowResize: false,
32418 trackMouseOver: true
32423 * <b>Common Problems:</b><br/>
32424 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32425 * element will correct this<br/>
32426 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32427 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32428 * are unpredictable.<br/>
32429 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32430 * grid to calculate dimensions/offsets.<br/>
32432 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32433 * The container MUST have some type of size defined for the grid to fill. The container will be
32434 * automatically set to position relative if it isn't already.
32435 * @param {Object} config A config object that sets properties on this grid.
32437 Roo.grid.Grid = function(container, config){
32438 // initialize the container
32439 this.container = Roo.get(container);
32440 this.container.update("");
32441 this.container.setStyle("overflow", "hidden");
32442 this.container.addClass('x-grid-container');
32444 this.id = this.container.id;
32446 Roo.apply(this, config);
32447 // check and correct shorthanded configs
32449 this.dataSource = this.ds;
32453 this.colModel = this.cm;
32457 this.selModel = this.sm;
32461 if (this.selModel) {
32462 this.selModel = Roo.factory(this.selModel, Roo.grid);
32463 this.sm = this.selModel;
32464 this.sm.xmodule = this.xmodule || false;
32466 if (typeof(this.colModel.config) == 'undefined') {
32467 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32468 this.cm = this.colModel;
32469 this.cm.xmodule = this.xmodule || false;
32471 if (this.dataSource) {
32472 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32473 this.ds = this.dataSource;
32474 this.ds.xmodule = this.xmodule || false;
32481 this.container.setWidth(this.width);
32485 this.container.setHeight(this.height);
32492 * The raw click event for the entire grid.
32493 * @param {Roo.EventObject} e
32498 * The raw dblclick event for the entire grid.
32499 * @param {Roo.EventObject} e
32503 * @event contextmenu
32504 * The raw contextmenu event for the entire grid.
32505 * @param {Roo.EventObject} e
32507 "contextmenu" : true,
32510 * The raw mousedown event for the entire grid.
32511 * @param {Roo.EventObject} e
32513 "mousedown" : true,
32516 * The raw mouseup event for the entire grid.
32517 * @param {Roo.EventObject} e
32522 * The raw mouseover event for the entire grid.
32523 * @param {Roo.EventObject} e
32525 "mouseover" : true,
32528 * The raw mouseout event for the entire grid.
32529 * @param {Roo.EventObject} e
32534 * The raw keypress event for the entire grid.
32535 * @param {Roo.EventObject} e
32540 * The raw keydown event for the entire grid.
32541 * @param {Roo.EventObject} e
32549 * Fires when a cell is clicked
32550 * @param {Grid} this
32551 * @param {Number} rowIndex
32552 * @param {Number} columnIndex
32553 * @param {Roo.EventObject} e
32555 "cellclick" : true,
32557 * @event celldblclick
32558 * Fires when a cell is double clicked
32559 * @param {Grid} this
32560 * @param {Number} rowIndex
32561 * @param {Number} columnIndex
32562 * @param {Roo.EventObject} e
32564 "celldblclick" : true,
32567 * Fires when a row is clicked
32568 * @param {Grid} this
32569 * @param {Number} rowIndex
32570 * @param {Roo.EventObject} e
32574 * @event rowdblclick
32575 * Fires when a row is double clicked
32576 * @param {Grid} this
32577 * @param {Number} rowIndex
32578 * @param {Roo.EventObject} e
32580 "rowdblclick" : true,
32582 * @event headerclick
32583 * Fires when a header is clicked
32584 * @param {Grid} this
32585 * @param {Number} columnIndex
32586 * @param {Roo.EventObject} e
32588 "headerclick" : true,
32590 * @event headerdblclick
32591 * Fires when a header cell is double clicked
32592 * @param {Grid} this
32593 * @param {Number} columnIndex
32594 * @param {Roo.EventObject} e
32596 "headerdblclick" : true,
32598 * @event rowcontextmenu
32599 * Fires when a row is right clicked
32600 * @param {Grid} this
32601 * @param {Number} rowIndex
32602 * @param {Roo.EventObject} e
32604 "rowcontextmenu" : true,
32606 * @event cellcontextmenu
32607 * Fires when a cell is right clicked
32608 * @param {Grid} this
32609 * @param {Number} rowIndex
32610 * @param {Number} cellIndex
32611 * @param {Roo.EventObject} e
32613 "cellcontextmenu" : true,
32615 * @event headercontextmenu
32616 * Fires when a header is right clicked
32617 * @param {Grid} this
32618 * @param {Number} columnIndex
32619 * @param {Roo.EventObject} e
32621 "headercontextmenu" : true,
32623 * @event bodyscroll
32624 * Fires when the body element is scrolled
32625 * @param {Number} scrollLeft
32626 * @param {Number} scrollTop
32628 "bodyscroll" : true,
32630 * @event columnresize
32631 * Fires when the user resizes a column
32632 * @param {Number} columnIndex
32633 * @param {Number} newSize
32635 "columnresize" : true,
32637 * @event columnmove
32638 * Fires when the user moves a column
32639 * @param {Number} oldIndex
32640 * @param {Number} newIndex
32642 "columnmove" : true,
32645 * Fires when row(s) start being dragged
32646 * @param {Grid} this
32647 * @param {Roo.GridDD} dd The drag drop object
32648 * @param {event} e The raw browser event
32650 "startdrag" : true,
32653 * Fires when a drag operation is complete
32654 * @param {Grid} this
32655 * @param {Roo.GridDD} dd The drag drop object
32656 * @param {event} e The raw browser event
32661 * Fires when dragged row(s) are dropped on a valid DD target
32662 * @param {Grid} this
32663 * @param {Roo.GridDD} dd The drag drop object
32664 * @param {String} targetId The target drag drop object
32665 * @param {event} e The raw browser event
32670 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32671 * @param {Grid} this
32672 * @param {Roo.GridDD} dd The drag drop object
32673 * @param {String} targetId The target drag drop object
32674 * @param {event} e The raw browser event
32679 * Fires when the dragged row(s) first cross another DD target while being dragged
32680 * @param {Grid} this
32681 * @param {Roo.GridDD} dd The drag drop object
32682 * @param {String} targetId The target drag drop object
32683 * @param {event} e The raw browser event
32685 "dragenter" : true,
32688 * Fires when the dragged row(s) leave another DD target while being dragged
32689 * @param {Grid} this
32690 * @param {Roo.GridDD} dd The drag drop object
32691 * @param {String} targetId The target drag drop object
32692 * @param {event} e The raw browser event
32697 * Fires when a row is rendered, so you can change add a style to it.
32698 * @param {GridView} gridview The grid view
32699 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32705 * Fires when the grid is rendered
32706 * @param {Grid} grid
32711 Roo.grid.Grid.superclass.constructor.call(this);
32713 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32716 * @cfg {String} ddGroup - drag drop group.
32720 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32722 minColumnWidth : 25,
32725 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32726 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32727 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32729 autoSizeColumns : false,
32732 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32734 autoSizeHeaders : true,
32737 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32739 monitorWindowResize : true,
32742 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32743 * rows measured to get a columns size. Default is 0 (all rows).
32745 maxRowsToMeasure : 0,
32748 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32750 trackMouseOver : true,
32753 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32757 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32759 enableDragDrop : false,
32762 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32764 enableColumnMove : true,
32767 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32769 enableColumnHide : true,
32772 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32774 enableRowHeightSync : false,
32777 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32782 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32784 autoHeight : false,
32787 * @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.
32789 autoExpandColumn : false,
32792 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32795 autoExpandMin : 50,
32798 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32800 autoExpandMax : 1000,
32803 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32808 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32812 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32822 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32823 * of a fixed width. Default is false.
32826 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32829 * Called once after all setup has been completed and the grid is ready to be rendered.
32830 * @return {Roo.grid.Grid} this
32832 render : function()
32834 var c = this.container;
32835 // try to detect autoHeight/width mode
32836 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32837 this.autoHeight = true;
32839 var view = this.getView();
32842 c.on("click", this.onClick, this);
32843 c.on("dblclick", this.onDblClick, this);
32844 c.on("contextmenu", this.onContextMenu, this);
32845 c.on("keydown", this.onKeyDown, this);
32847 c.on("touchstart", this.onTouchStart, this);
32850 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32852 this.getSelectionModel().init(this);
32857 this.loadMask = new Roo.LoadMask(this.container,
32858 Roo.apply({store:this.dataSource}, this.loadMask));
32862 if (this.toolbar && this.toolbar.xtype) {
32863 this.toolbar.container = this.getView().getHeaderPanel(true);
32864 this.toolbar = new Roo.Toolbar(this.toolbar);
32866 if (this.footer && this.footer.xtype) {
32867 this.footer.dataSource = this.getDataSource();
32868 this.footer.container = this.getView().getFooterPanel(true);
32869 this.footer = Roo.factory(this.footer, Roo);
32871 if (this.dropTarget && this.dropTarget.xtype) {
32872 delete this.dropTarget.xtype;
32873 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32877 this.rendered = true;
32878 this.fireEvent('render', this);
32883 * Reconfigures the grid to use a different Store and Column Model.
32884 * The View will be bound to the new objects and refreshed.
32885 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32886 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32888 reconfigure : function(dataSource, colModel){
32890 this.loadMask.destroy();
32891 this.loadMask = new Roo.LoadMask(this.container,
32892 Roo.apply({store:dataSource}, this.loadMask));
32894 this.view.bind(dataSource, colModel);
32895 this.dataSource = dataSource;
32896 this.colModel = colModel;
32897 this.view.refresh(true);
32901 onKeyDown : function(e){
32902 this.fireEvent("keydown", e);
32906 * Destroy this grid.
32907 * @param {Boolean} removeEl True to remove the element
32909 destroy : function(removeEl, keepListeners){
32911 this.loadMask.destroy();
32913 var c = this.container;
32914 c.removeAllListeners();
32915 this.view.destroy();
32916 this.colModel.purgeListeners();
32917 if(!keepListeners){
32918 this.purgeListeners();
32921 if(removeEl === true){
32927 processEvent : function(name, e){
32928 // does this fire select???
32929 //Roo.log('grid:processEvent ' + name);
32931 if (name != 'touchstart' ) {
32932 this.fireEvent(name, e);
32935 var t = e.getTarget();
32937 var header = v.findHeaderIndex(t);
32938 if(header !== false){
32939 var ename = name == 'touchstart' ? 'click' : name;
32941 this.fireEvent("header" + ename, this, header, e);
32943 var row = v.findRowIndex(t);
32944 var cell = v.findCellIndex(t);
32945 if (name == 'touchstart') {
32946 // first touch is always a click.
32947 // hopefull this happens after selection is updated.?
32950 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32951 var cs = this.selModel.getSelectedCell();
32952 if (row == cs[0] && cell == cs[1]){
32956 if (typeof(this.selModel.getSelections) != 'undefined') {
32957 var cs = this.selModel.getSelections();
32958 var ds = this.dataSource;
32959 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32970 this.fireEvent("row" + name, this, row, e);
32971 if(cell !== false){
32972 this.fireEvent("cell" + name, this, row, cell, e);
32979 onClick : function(e){
32980 this.processEvent("click", e);
32983 onTouchStart : function(e){
32984 this.processEvent("touchstart", e);
32988 onContextMenu : function(e, t){
32989 this.processEvent("contextmenu", e);
32993 onDblClick : function(e){
32994 this.processEvent("dblclick", e);
32998 walkCells : function(row, col, step, fn, scope){
32999 var cm = this.colModel, clen = cm.getColumnCount();
33000 var ds = this.dataSource, rlen = ds.getCount(), first = true;
33012 if(fn.call(scope || this, row, col, cm) === true){
33030 if(fn.call(scope || this, row, col, cm) === true){
33042 getSelections : function(){
33043 return this.selModel.getSelections();
33047 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33048 * but if manual update is required this method will initiate it.
33050 autoSize : function(){
33052 this.view.layout();
33053 if(this.view.adjustForScroll){
33054 this.view.adjustForScroll();
33060 * Returns the grid's underlying element.
33061 * @return {Element} The element
33063 getGridEl : function(){
33064 return this.container;
33067 // private for compatibility, overridden by editor grid
33068 stopEditing : function(){},
33071 * Returns the grid's SelectionModel.
33072 * @return {SelectionModel}
33074 getSelectionModel : function(){
33075 if(!this.selModel){
33076 this.selModel = new Roo.grid.RowSelectionModel();
33078 return this.selModel;
33082 * Returns the grid's DataSource.
33083 * @return {DataSource}
33085 getDataSource : function(){
33086 return this.dataSource;
33090 * Returns the grid's ColumnModel.
33091 * @return {ColumnModel}
33093 getColumnModel : function(){
33094 return this.colModel;
33098 * Returns the grid's GridView object.
33099 * @return {GridView}
33101 getView : function(){
33103 this.view = new Roo.grid.GridView(this.viewConfig);
33108 * Called to get grid's drag proxy text, by default returns this.ddText.
33111 getDragDropText : function(){
33112 var count = this.selModel.getCount();
33113 return String.format(this.ddText, count, count == 1 ? '' : 's');
33117 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33118 * %0 is replaced with the number of selected rows.
33121 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33123 * Ext JS Library 1.1.1
33124 * Copyright(c) 2006-2007, Ext JS, LLC.
33126 * Originally Released Under LGPL - original licence link has changed is not relivant.
33129 * <script type="text/javascript">
33132 Roo.grid.AbstractGridView = function(){
33136 "beforerowremoved" : true,
33137 "beforerowsinserted" : true,
33138 "beforerefresh" : true,
33139 "rowremoved" : true,
33140 "rowsinserted" : true,
33141 "rowupdated" : true,
33144 Roo.grid.AbstractGridView.superclass.constructor.call(this);
33147 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33148 rowClass : "x-grid-row",
33149 cellClass : "x-grid-cell",
33150 tdClass : "x-grid-td",
33151 hdClass : "x-grid-hd",
33152 splitClass : "x-grid-hd-split",
33154 init: function(grid){
33156 var cid = this.grid.getGridEl().id;
33157 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33158 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33159 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33160 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33163 getColumnRenderers : function(){
33164 var renderers = [];
33165 var cm = this.grid.colModel;
33166 var colCount = cm.getColumnCount();
33167 for(var i = 0; i < colCount; i++){
33168 renderers[i] = cm.getRenderer(i);
33173 getColumnIds : function(){
33175 var cm = this.grid.colModel;
33176 var colCount = cm.getColumnCount();
33177 for(var i = 0; i < colCount; i++){
33178 ids[i] = cm.getColumnId(i);
33183 getDataIndexes : function(){
33184 if(!this.indexMap){
33185 this.indexMap = this.buildIndexMap();
33187 return this.indexMap.colToData;
33190 getColumnIndexByDataIndex : function(dataIndex){
33191 if(!this.indexMap){
33192 this.indexMap = this.buildIndexMap();
33194 return this.indexMap.dataToCol[dataIndex];
33198 * Set a css style for a column dynamically.
33199 * @param {Number} colIndex The index of the column
33200 * @param {String} name The css property name
33201 * @param {String} value The css value
33203 setCSSStyle : function(colIndex, name, value){
33204 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33205 Roo.util.CSS.updateRule(selector, name, value);
33208 generateRules : function(cm){
33209 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33210 Roo.util.CSS.removeStyleSheet(rulesId);
33211 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33212 var cid = cm.getColumnId(i);
33213 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33214 this.tdSelector, cid, " {\n}\n",
33215 this.hdSelector, cid, " {\n}\n",
33216 this.splitSelector, cid, " {\n}\n");
33218 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33222 * Ext JS Library 1.1.1
33223 * Copyright(c) 2006-2007, Ext JS, LLC.
33225 * Originally Released Under LGPL - original licence link has changed is not relivant.
33228 * <script type="text/javascript">
33232 // This is a support class used internally by the Grid components
33233 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33235 this.view = grid.getView();
33236 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33237 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33239 this.setHandleElId(Roo.id(hd));
33240 this.setOuterHandleElId(Roo.id(hd2));
33242 this.scroll = false;
33244 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33246 getDragData : function(e){
33247 var t = Roo.lib.Event.getTarget(e);
33248 var h = this.view.findHeaderCell(t);
33250 return {ddel: h.firstChild, header:h};
33255 onInitDrag : function(e){
33256 this.view.headersDisabled = true;
33257 var clone = this.dragData.ddel.cloneNode(true);
33258 clone.id = Roo.id();
33259 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33260 this.proxy.update(clone);
33264 afterValidDrop : function(){
33266 setTimeout(function(){
33267 v.headersDisabled = false;
33271 afterInvalidDrop : function(){
33273 setTimeout(function(){
33274 v.headersDisabled = false;
33280 * Ext JS Library 1.1.1
33281 * Copyright(c) 2006-2007, Ext JS, LLC.
33283 * Originally Released Under LGPL - original licence link has changed is not relivant.
33286 * <script type="text/javascript">
33289 // This is a support class used internally by the Grid components
33290 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33292 this.view = grid.getView();
33293 // split the proxies so they don't interfere with mouse events
33294 this.proxyTop = Roo.DomHelper.append(document.body, {
33295 cls:"col-move-top", html:" "
33297 this.proxyBottom = Roo.DomHelper.append(document.body, {
33298 cls:"col-move-bottom", html:" "
33300 this.proxyTop.hide = this.proxyBottom.hide = function(){
33301 this.setLeftTop(-100,-100);
33302 this.setStyle("visibility", "hidden");
33304 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33305 // temporarily disabled
33306 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33307 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33309 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33310 proxyOffsets : [-4, -9],
33311 fly: Roo.Element.fly,
33313 getTargetFromEvent : function(e){
33314 var t = Roo.lib.Event.getTarget(e);
33315 var cindex = this.view.findCellIndex(t);
33316 if(cindex !== false){
33317 return this.view.getHeaderCell(cindex);
33322 nextVisible : function(h){
33323 var v = this.view, cm = this.grid.colModel;
33326 if(!cm.isHidden(v.getCellIndex(h))){
33334 prevVisible : function(h){
33335 var v = this.view, cm = this.grid.colModel;
33338 if(!cm.isHidden(v.getCellIndex(h))){
33346 positionIndicator : function(h, n, e){
33347 var x = Roo.lib.Event.getPageX(e);
33348 var r = Roo.lib.Dom.getRegion(n.firstChild);
33349 var px, pt, py = r.top + this.proxyOffsets[1];
33350 if((r.right - x) <= (r.right-r.left)/2){
33351 px = r.right+this.view.borderWidth;
33357 var oldIndex = this.view.getCellIndex(h);
33358 var newIndex = this.view.getCellIndex(n);
33360 if(this.grid.colModel.isFixed(newIndex)){
33364 var locked = this.grid.colModel.isLocked(newIndex);
33369 if(oldIndex < newIndex){
33372 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33375 px += this.proxyOffsets[0];
33376 this.proxyTop.setLeftTop(px, py);
33377 this.proxyTop.show();
33378 if(!this.bottomOffset){
33379 this.bottomOffset = this.view.mainHd.getHeight();
33381 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33382 this.proxyBottom.show();
33386 onNodeEnter : function(n, dd, e, data){
33387 if(data.header != n){
33388 this.positionIndicator(data.header, n, e);
33392 onNodeOver : function(n, dd, e, data){
33393 var result = false;
33394 if(data.header != n){
33395 result = this.positionIndicator(data.header, n, e);
33398 this.proxyTop.hide();
33399 this.proxyBottom.hide();
33401 return result ? this.dropAllowed : this.dropNotAllowed;
33404 onNodeOut : function(n, dd, e, data){
33405 this.proxyTop.hide();
33406 this.proxyBottom.hide();
33409 onNodeDrop : function(n, dd, e, data){
33410 var h = data.header;
33412 var cm = this.grid.colModel;
33413 var x = Roo.lib.Event.getPageX(e);
33414 var r = Roo.lib.Dom.getRegion(n.firstChild);
33415 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33416 var oldIndex = this.view.getCellIndex(h);
33417 var newIndex = this.view.getCellIndex(n);
33418 var locked = cm.isLocked(newIndex);
33422 if(oldIndex < newIndex){
33425 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33428 cm.setLocked(oldIndex, locked, true);
33429 cm.moveColumn(oldIndex, newIndex);
33430 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33438 * Ext JS Library 1.1.1
33439 * Copyright(c) 2006-2007, Ext JS, LLC.
33441 * Originally Released Under LGPL - original licence link has changed is not relivant.
33444 * <script type="text/javascript">
33448 * @class Roo.grid.GridView
33449 * @extends Roo.util.Observable
33452 * @param {Object} config
33454 Roo.grid.GridView = function(config){
33455 Roo.grid.GridView.superclass.constructor.call(this);
33458 Roo.apply(this, config);
33461 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33463 unselectable : 'unselectable="on"',
33464 unselectableCls : 'x-unselectable',
33467 rowClass : "x-grid-row",
33469 cellClass : "x-grid-col",
33471 tdClass : "x-grid-td",
33473 hdClass : "x-grid-hd",
33475 splitClass : "x-grid-split",
33477 sortClasses : ["sort-asc", "sort-desc"],
33479 enableMoveAnim : false,
33483 dh : Roo.DomHelper,
33485 fly : Roo.Element.fly,
33487 css : Roo.util.CSS,
33493 scrollIncrement : 22,
33495 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33497 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33499 bind : function(ds, cm){
33501 this.ds.un("load", this.onLoad, this);
33502 this.ds.un("datachanged", this.onDataChange, this);
33503 this.ds.un("add", this.onAdd, this);
33504 this.ds.un("remove", this.onRemove, this);
33505 this.ds.un("update", this.onUpdate, this);
33506 this.ds.un("clear", this.onClear, this);
33509 ds.on("load", this.onLoad, this);
33510 ds.on("datachanged", this.onDataChange, this);
33511 ds.on("add", this.onAdd, this);
33512 ds.on("remove", this.onRemove, this);
33513 ds.on("update", this.onUpdate, this);
33514 ds.on("clear", this.onClear, this);
33519 this.cm.un("widthchange", this.onColWidthChange, this);
33520 this.cm.un("headerchange", this.onHeaderChange, this);
33521 this.cm.un("hiddenchange", this.onHiddenChange, this);
33522 this.cm.un("columnmoved", this.onColumnMove, this);
33523 this.cm.un("columnlockchange", this.onColumnLock, this);
33526 this.generateRules(cm);
33527 cm.on("widthchange", this.onColWidthChange, this);
33528 cm.on("headerchange", this.onHeaderChange, this);
33529 cm.on("hiddenchange", this.onHiddenChange, this);
33530 cm.on("columnmoved", this.onColumnMove, this);
33531 cm.on("columnlockchange", this.onColumnLock, this);
33536 init: function(grid){
33537 Roo.grid.GridView.superclass.init.call(this, grid);
33539 this.bind(grid.dataSource, grid.colModel);
33541 grid.on("headerclick", this.handleHeaderClick, this);
33543 if(grid.trackMouseOver){
33544 grid.on("mouseover", this.onRowOver, this);
33545 grid.on("mouseout", this.onRowOut, this);
33547 grid.cancelTextSelection = function(){};
33548 this.gridId = grid.id;
33550 var tpls = this.templates || {};
33553 tpls.master = new Roo.Template(
33554 '<div class="x-grid" hidefocus="true">',
33555 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33556 '<div class="x-grid-topbar"></div>',
33557 '<div class="x-grid-scroller"><div></div></div>',
33558 '<div class="x-grid-locked">',
33559 '<div class="x-grid-header">{lockedHeader}</div>',
33560 '<div class="x-grid-body">{lockedBody}</div>',
33562 '<div class="x-grid-viewport">',
33563 '<div class="x-grid-header">{header}</div>',
33564 '<div class="x-grid-body">{body}</div>',
33566 '<div class="x-grid-bottombar"></div>',
33568 '<div class="x-grid-resize-proxy"> </div>',
33571 tpls.master.disableformats = true;
33575 tpls.header = new Roo.Template(
33576 '<table border="0" cellspacing="0" cellpadding="0">',
33577 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33580 tpls.header.disableformats = true;
33582 tpls.header.compile();
33585 tpls.hcell = new Roo.Template(
33586 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33587 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33590 tpls.hcell.disableFormats = true;
33592 tpls.hcell.compile();
33595 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33596 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33597 tpls.hsplit.disableFormats = true;
33599 tpls.hsplit.compile();
33602 tpls.body = new Roo.Template(
33603 '<table border="0" cellspacing="0" cellpadding="0">',
33604 "<tbody>{rows}</tbody>",
33607 tpls.body.disableFormats = true;
33609 tpls.body.compile();
33612 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33613 tpls.row.disableFormats = true;
33615 tpls.row.compile();
33618 tpls.cell = new Roo.Template(
33619 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33620 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33621 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33624 tpls.cell.disableFormats = true;
33626 tpls.cell.compile();
33628 this.templates = tpls;
33631 // remap these for backwards compat
33632 onColWidthChange : function(){
33633 this.updateColumns.apply(this, arguments);
33635 onHeaderChange : function(){
33636 this.updateHeaders.apply(this, arguments);
33638 onHiddenChange : function(){
33639 this.handleHiddenChange.apply(this, arguments);
33641 onColumnMove : function(){
33642 this.handleColumnMove.apply(this, arguments);
33644 onColumnLock : function(){
33645 this.handleLockChange.apply(this, arguments);
33648 onDataChange : function(){
33650 this.updateHeaderSortState();
33653 onClear : function(){
33657 onUpdate : function(ds, record){
33658 this.refreshRow(record);
33661 refreshRow : function(record){
33662 var ds = this.ds, index;
33663 if(typeof record == 'number'){
33665 record = ds.getAt(index);
33667 index = ds.indexOf(record);
33669 this.insertRows(ds, index, index, true);
33670 this.onRemove(ds, record, index+1, true);
33671 this.syncRowHeights(index, index);
33673 this.fireEvent("rowupdated", this, index, record);
33676 onAdd : function(ds, records, index){
33677 this.insertRows(ds, index, index + (records.length-1));
33680 onRemove : function(ds, record, index, isUpdate){
33681 if(isUpdate !== true){
33682 this.fireEvent("beforerowremoved", this, index, record);
33684 var bt = this.getBodyTable(), lt = this.getLockedTable();
33685 if(bt.rows[index]){
33686 bt.firstChild.removeChild(bt.rows[index]);
33688 if(lt.rows[index]){
33689 lt.firstChild.removeChild(lt.rows[index]);
33691 if(isUpdate !== true){
33692 this.stripeRows(index);
33693 this.syncRowHeights(index, index);
33695 this.fireEvent("rowremoved", this, index, record);
33699 onLoad : function(){
33700 this.scrollToTop();
33704 * Scrolls the grid to the top
33706 scrollToTop : function(){
33708 this.scroller.dom.scrollTop = 0;
33714 * Gets a panel in the header of the grid that can be used for toolbars etc.
33715 * After modifying the contents of this panel a call to grid.autoSize() may be
33716 * required to register any changes in size.
33717 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33718 * @return Roo.Element
33720 getHeaderPanel : function(doShow){
33722 this.headerPanel.show();
33724 return this.headerPanel;
33728 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33729 * After modifying the contents of this panel a call to grid.autoSize() may be
33730 * required to register any changes in size.
33731 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33732 * @return Roo.Element
33734 getFooterPanel : function(doShow){
33736 this.footerPanel.show();
33738 return this.footerPanel;
33741 initElements : function(){
33742 var E = Roo.Element;
33743 var el = this.grid.getGridEl().dom.firstChild;
33744 var cs = el.childNodes;
33746 this.el = new E(el);
33748 this.focusEl = new E(el.firstChild);
33749 this.focusEl.swallowEvent("click", true);
33751 this.headerPanel = new E(cs[1]);
33752 this.headerPanel.enableDisplayMode("block");
33754 this.scroller = new E(cs[2]);
33755 this.scrollSizer = new E(this.scroller.dom.firstChild);
33757 this.lockedWrap = new E(cs[3]);
33758 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33759 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33761 this.mainWrap = new E(cs[4]);
33762 this.mainHd = new E(this.mainWrap.dom.firstChild);
33763 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33765 this.footerPanel = new E(cs[5]);
33766 this.footerPanel.enableDisplayMode("block");
33768 this.resizeProxy = new E(cs[6]);
33770 this.headerSelector = String.format(
33771 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33772 this.lockedHd.id, this.mainHd.id
33775 this.splitterSelector = String.format(
33776 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33777 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33780 idToCssName : function(s)
33782 return s.replace(/[^a-z0-9]+/ig, '-');
33785 getHeaderCell : function(index){
33786 return Roo.DomQuery.select(this.headerSelector)[index];
33789 getHeaderCellMeasure : function(index){
33790 return this.getHeaderCell(index).firstChild;
33793 getHeaderCellText : function(index){
33794 return this.getHeaderCell(index).firstChild.firstChild;
33797 getLockedTable : function(){
33798 return this.lockedBody.dom.firstChild;
33801 getBodyTable : function(){
33802 return this.mainBody.dom.firstChild;
33805 getLockedRow : function(index){
33806 return this.getLockedTable().rows[index];
33809 getRow : function(index){
33810 return this.getBodyTable().rows[index];
33813 getRowComposite : function(index){
33815 this.rowEl = new Roo.CompositeElementLite();
33817 var els = [], lrow, mrow;
33818 if(lrow = this.getLockedRow(index)){
33821 if(mrow = this.getRow(index)){
33824 this.rowEl.elements = els;
33828 * Gets the 'td' of the cell
33830 * @param {Integer} rowIndex row to select
33831 * @param {Integer} colIndex column to select
33835 getCell : function(rowIndex, colIndex){
33836 var locked = this.cm.getLockedCount();
33838 if(colIndex < locked){
33839 source = this.lockedBody.dom.firstChild;
33841 source = this.mainBody.dom.firstChild;
33842 colIndex -= locked;
33844 return source.rows[rowIndex].childNodes[colIndex];
33847 getCellText : function(rowIndex, colIndex){
33848 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33851 getCellBox : function(cell){
33852 var b = this.fly(cell).getBox();
33853 if(Roo.isOpera){ // opera fails to report the Y
33854 b.y = cell.offsetTop + this.mainBody.getY();
33859 getCellIndex : function(cell){
33860 var id = String(cell.className).match(this.cellRE);
33862 return parseInt(id[1], 10);
33867 findHeaderIndex : function(n){
33868 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33869 return r ? this.getCellIndex(r) : false;
33872 findHeaderCell : function(n){
33873 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33874 return r ? r : false;
33877 findRowIndex : function(n){
33881 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33882 return r ? r.rowIndex : false;
33885 findCellIndex : function(node){
33886 var stop = this.el.dom;
33887 while(node && node != stop){
33888 if(this.findRE.test(node.className)){
33889 return this.getCellIndex(node);
33891 node = node.parentNode;
33896 getColumnId : function(index){
33897 return this.cm.getColumnId(index);
33900 getSplitters : function()
33902 if(this.splitterSelector){
33903 return Roo.DomQuery.select(this.splitterSelector);
33909 getSplitter : function(index){
33910 return this.getSplitters()[index];
33913 onRowOver : function(e, t){
33915 if((row = this.findRowIndex(t)) !== false){
33916 this.getRowComposite(row).addClass("x-grid-row-over");
33920 onRowOut : function(e, t){
33922 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33923 this.getRowComposite(row).removeClass("x-grid-row-over");
33927 renderHeaders : function(){
33929 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33930 var cb = [], lb = [], sb = [], lsb = [], p = {};
33931 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33932 p.cellId = "x-grid-hd-0-" + i;
33933 p.splitId = "x-grid-csplit-0-" + i;
33934 p.id = cm.getColumnId(i);
33935 p.value = cm.getColumnHeader(i) || "";
33936 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33937 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33938 if(!cm.isLocked(i)){
33939 cb[cb.length] = ct.apply(p);
33940 sb[sb.length] = st.apply(p);
33942 lb[lb.length] = ct.apply(p);
33943 lsb[lsb.length] = st.apply(p);
33946 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33947 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33950 updateHeaders : function(){
33951 var html = this.renderHeaders();
33952 this.lockedHd.update(html[0]);
33953 this.mainHd.update(html[1]);
33957 * Focuses the specified row.
33958 * @param {Number} row The row index
33960 focusRow : function(row)
33962 //Roo.log('GridView.focusRow');
33963 var x = this.scroller.dom.scrollLeft;
33964 this.focusCell(row, 0, false);
33965 this.scroller.dom.scrollLeft = x;
33969 * Focuses the specified cell.
33970 * @param {Number} row The row index
33971 * @param {Number} col The column index
33972 * @param {Boolean} hscroll false to disable horizontal scrolling
33974 focusCell : function(row, col, hscroll)
33976 //Roo.log('GridView.focusCell');
33977 var el = this.ensureVisible(row, col, hscroll);
33978 this.focusEl.alignTo(el, "tl-tl");
33980 this.focusEl.focus();
33982 this.focusEl.focus.defer(1, this.focusEl);
33987 * Scrolls the specified cell into view
33988 * @param {Number} row The row index
33989 * @param {Number} col The column index
33990 * @param {Boolean} hscroll false to disable horizontal scrolling
33992 ensureVisible : function(row, col, hscroll)
33994 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33995 //return null; //disable for testing.
33996 if(typeof row != "number"){
33997 row = row.rowIndex;
33999 if(row < 0 && row >= this.ds.getCount()){
34002 col = (col !== undefined ? col : 0);
34003 var cm = this.grid.colModel;
34004 while(cm.isHidden(col)){
34008 var el = this.getCell(row, col);
34012 var c = this.scroller.dom;
34014 var ctop = parseInt(el.offsetTop, 10);
34015 var cleft = parseInt(el.offsetLeft, 10);
34016 var cbot = ctop + el.offsetHeight;
34017 var cright = cleft + el.offsetWidth;
34019 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34020 var stop = parseInt(c.scrollTop, 10);
34021 var sleft = parseInt(c.scrollLeft, 10);
34022 var sbot = stop + ch;
34023 var sright = sleft + c.clientWidth;
34025 Roo.log('GridView.ensureVisible:' +
34027 ' c.clientHeight:' + c.clientHeight +
34028 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34036 c.scrollTop = ctop;
34037 //Roo.log("set scrolltop to ctop DISABLE?");
34038 }else if(cbot > sbot){
34039 //Roo.log("set scrolltop to cbot-ch");
34040 c.scrollTop = cbot-ch;
34043 if(hscroll !== false){
34045 c.scrollLeft = cleft;
34046 }else if(cright > sright){
34047 c.scrollLeft = cright-c.clientWidth;
34054 updateColumns : function(){
34055 this.grid.stopEditing();
34056 var cm = this.grid.colModel, colIds = this.getColumnIds();
34057 //var totalWidth = cm.getTotalWidth();
34059 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34060 //if(cm.isHidden(i)) continue;
34061 var w = cm.getColumnWidth(i);
34062 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34063 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34065 this.updateSplitters();
34068 generateRules : function(cm){
34069 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34070 Roo.util.CSS.removeStyleSheet(rulesId);
34071 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34072 var cid = cm.getColumnId(i);
34074 if(cm.config[i].align){
34075 align = 'text-align:'+cm.config[i].align+';';
34078 if(cm.isHidden(i)){
34079 hidden = 'display:none;';
34081 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34083 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34084 this.hdSelector, cid, " {\n", align, width, "}\n",
34085 this.tdSelector, cid, " {\n",hidden,"\n}\n",
34086 this.splitSelector, cid, " {\n", hidden , "\n}\n");
34088 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34091 updateSplitters : function(){
34092 var cm = this.cm, s = this.getSplitters();
34093 if(s){ // splitters not created yet
34094 var pos = 0, locked = true;
34095 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34096 if(cm.isHidden(i)) {
34099 var w = cm.getColumnWidth(i); // make sure it's a number
34100 if(!cm.isLocked(i) && locked){
34105 s[i].style.left = (pos-this.splitOffset) + "px";
34110 handleHiddenChange : function(colModel, colIndex, hidden){
34112 this.hideColumn(colIndex);
34114 this.unhideColumn(colIndex);
34118 hideColumn : function(colIndex){
34119 var cid = this.getColumnId(colIndex);
34120 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34121 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34123 this.updateHeaders();
34125 this.updateSplitters();
34129 unhideColumn : function(colIndex){
34130 var cid = this.getColumnId(colIndex);
34131 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34132 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34135 this.updateHeaders();
34137 this.updateSplitters();
34141 insertRows : function(dm, firstRow, lastRow, isUpdate){
34142 if(firstRow == 0 && lastRow == dm.getCount()-1){
34146 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34148 var s = this.getScrollState();
34149 var markup = this.renderRows(firstRow, lastRow);
34150 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34151 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34152 this.restoreScroll(s);
34154 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34155 this.syncRowHeights(firstRow, lastRow);
34156 this.stripeRows(firstRow);
34162 bufferRows : function(markup, target, index){
34163 var before = null, trows = target.rows, tbody = target.tBodies[0];
34164 if(index < trows.length){
34165 before = trows[index];
34167 var b = document.createElement("div");
34168 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34169 var rows = b.firstChild.rows;
34170 for(var i = 0, len = rows.length; i < len; i++){
34172 tbody.insertBefore(rows[0], before);
34174 tbody.appendChild(rows[0]);
34181 deleteRows : function(dm, firstRow, lastRow){
34182 if(dm.getRowCount()<1){
34183 this.fireEvent("beforerefresh", this);
34184 this.mainBody.update("");
34185 this.lockedBody.update("");
34186 this.fireEvent("refresh", this);
34188 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34189 var bt = this.getBodyTable();
34190 var tbody = bt.firstChild;
34191 var rows = bt.rows;
34192 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34193 tbody.removeChild(rows[firstRow]);
34195 this.stripeRows(firstRow);
34196 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34200 updateRows : function(dataSource, firstRow, lastRow){
34201 var s = this.getScrollState();
34203 this.restoreScroll(s);
34206 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34210 this.updateHeaderSortState();
34213 getScrollState : function(){
34215 var sb = this.scroller.dom;
34216 return {left: sb.scrollLeft, top: sb.scrollTop};
34219 stripeRows : function(startRow){
34220 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34223 startRow = startRow || 0;
34224 var rows = this.getBodyTable().rows;
34225 var lrows = this.getLockedTable().rows;
34226 var cls = ' x-grid-row-alt ';
34227 for(var i = startRow, len = rows.length; i < len; i++){
34228 var row = rows[i], lrow = lrows[i];
34229 var isAlt = ((i+1) % 2 == 0);
34230 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34231 if(isAlt == hasAlt){
34235 row.className += " x-grid-row-alt";
34237 row.className = row.className.replace("x-grid-row-alt", "");
34240 lrow.className = row.className;
34245 restoreScroll : function(state){
34246 //Roo.log('GridView.restoreScroll');
34247 var sb = this.scroller.dom;
34248 sb.scrollLeft = state.left;
34249 sb.scrollTop = state.top;
34253 syncScroll : function(){
34254 //Roo.log('GridView.syncScroll');
34255 var sb = this.scroller.dom;
34256 var sh = this.mainHd.dom;
34257 var bs = this.mainBody.dom;
34258 var lv = this.lockedBody.dom;
34259 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34260 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34263 handleScroll : function(e){
34265 var sb = this.scroller.dom;
34266 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34270 handleWheel : function(e){
34271 var d = e.getWheelDelta();
34272 this.scroller.dom.scrollTop -= d*22;
34273 // set this here to prevent jumpy scrolling on large tables
34274 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34278 renderRows : function(startRow, endRow){
34279 // pull in all the crap needed to render rows
34280 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34281 var colCount = cm.getColumnCount();
34283 if(ds.getCount() < 1){
34287 // build a map for all the columns
34289 for(var i = 0; i < colCount; i++){
34290 var name = cm.getDataIndex(i);
34292 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34293 renderer : cm.getRenderer(i),
34294 id : cm.getColumnId(i),
34295 locked : cm.isLocked(i),
34296 has_editor : cm.isCellEditable(i)
34300 startRow = startRow || 0;
34301 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34303 // records to render
34304 var rs = ds.getRange(startRow, endRow);
34306 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34309 // As much as I hate to duplicate code, this was branched because FireFox really hates
34310 // [].join("") on strings. The performance difference was substantial enough to
34311 // branch this function
34312 doRender : Roo.isGecko ?
34313 function(cs, rs, ds, startRow, colCount, stripe){
34314 var ts = this.templates, ct = ts.cell, rt = ts.row;
34316 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34318 var hasListener = this.grid.hasListener('rowclass');
34320 for(var j = 0, len = rs.length; j < len; j++){
34321 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34322 for(var i = 0; i < colCount; i++){
34324 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34326 p.css = p.attr = "";
34327 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34328 if(p.value == undefined || p.value === "") {
34329 p.value = " ";
34332 p.css += ' x-grid-editable-cell';
34334 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34335 p.css += ' x-grid-dirty-cell';
34337 var markup = ct.apply(p);
34345 if(stripe && ((rowIndex+1) % 2 == 0)){
34346 alt.push("x-grid-row-alt")
34349 alt.push( " x-grid-dirty-row");
34352 if(this.getRowClass){
34353 alt.push(this.getRowClass(r, rowIndex));
34359 rowIndex : rowIndex,
34362 this.grid.fireEvent('rowclass', this, rowcfg);
34363 alt.push(rowcfg.rowClass);
34365 rp.alt = alt.join(" ");
34366 lbuf+= rt.apply(rp);
34368 buf+= rt.apply(rp);
34370 return [lbuf, buf];
34372 function(cs, rs, ds, startRow, colCount, stripe){
34373 var ts = this.templates, ct = ts.cell, rt = ts.row;
34375 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34376 var hasListener = this.grid.hasListener('rowclass');
34379 for(var j = 0, len = rs.length; j < len; j++){
34380 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34381 for(var i = 0; i < colCount; i++){
34383 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34385 p.css = p.attr = "";
34386 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34387 if(p.value == undefined || p.value === "") {
34388 p.value = " ";
34392 p.css += ' x-grid-editable-cell';
34394 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34395 p.css += ' x-grid-dirty-cell'
34398 var markup = ct.apply(p);
34400 cb[cb.length] = markup;
34402 lcb[lcb.length] = markup;
34406 if(stripe && ((rowIndex+1) % 2 == 0)){
34407 alt.push( "x-grid-row-alt");
34410 alt.push(" x-grid-dirty-row");
34413 if(this.getRowClass){
34414 alt.push( this.getRowClass(r, rowIndex));
34420 rowIndex : rowIndex,
34423 this.grid.fireEvent('rowclass', this, rowcfg);
34424 alt.push(rowcfg.rowClass);
34427 rp.alt = alt.join(" ");
34428 rp.cells = lcb.join("");
34429 lbuf[lbuf.length] = rt.apply(rp);
34430 rp.cells = cb.join("");
34431 buf[buf.length] = rt.apply(rp);
34433 return [lbuf.join(""), buf.join("")];
34436 renderBody : function(){
34437 var markup = this.renderRows();
34438 var bt = this.templates.body;
34439 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34443 * Refreshes the grid
34444 * @param {Boolean} headersToo
34446 refresh : function(headersToo){
34447 this.fireEvent("beforerefresh", this);
34448 this.grid.stopEditing();
34449 var result = this.renderBody();
34450 this.lockedBody.update(result[0]);
34451 this.mainBody.update(result[1]);
34452 if(headersToo === true){
34453 this.updateHeaders();
34454 this.updateColumns();
34455 this.updateSplitters();
34456 this.updateHeaderSortState();
34458 this.syncRowHeights();
34460 this.fireEvent("refresh", this);
34463 handleColumnMove : function(cm, oldIndex, newIndex){
34464 this.indexMap = null;
34465 var s = this.getScrollState();
34466 this.refresh(true);
34467 this.restoreScroll(s);
34468 this.afterMove(newIndex);
34471 afterMove : function(colIndex){
34472 if(this.enableMoveAnim && Roo.enableFx){
34473 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34475 // if multisort - fix sortOrder, and reload..
34476 if (this.grid.dataSource.multiSort) {
34477 // the we can call sort again..
34478 var dm = this.grid.dataSource;
34479 var cm = this.grid.colModel;
34481 for(var i = 0; i < cm.config.length; i++ ) {
34483 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34484 continue; // dont' bother, it's not in sort list or being set.
34487 so.push(cm.config[i].dataIndex);
34490 dm.load(dm.lastOptions);
34497 updateCell : function(dm, rowIndex, dataIndex){
34498 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34499 if(typeof colIndex == "undefined"){ // not present in grid
34502 var cm = this.grid.colModel;
34503 var cell = this.getCell(rowIndex, colIndex);
34504 var cellText = this.getCellText(rowIndex, colIndex);
34507 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34508 id : cm.getColumnId(colIndex),
34509 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34511 var renderer = cm.getRenderer(colIndex);
34512 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34513 if(typeof val == "undefined" || val === "") {
34516 cellText.innerHTML = val;
34517 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34518 this.syncRowHeights(rowIndex, rowIndex);
34521 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34523 if(this.grid.autoSizeHeaders){
34524 var h = this.getHeaderCellMeasure(colIndex);
34525 maxWidth = Math.max(maxWidth, h.scrollWidth);
34528 if(this.cm.isLocked(colIndex)){
34529 tb = this.getLockedTable();
34532 tb = this.getBodyTable();
34533 index = colIndex - this.cm.getLockedCount();
34536 var rows = tb.rows;
34537 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34538 for(var i = 0; i < stopIndex; i++){
34539 var cell = rows[i].childNodes[index].firstChild;
34540 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34543 return maxWidth + /*margin for error in IE*/ 5;
34546 * Autofit a column to its content.
34547 * @param {Number} colIndex
34548 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34550 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34551 if(this.cm.isHidden(colIndex)){
34552 return; // can't calc a hidden column
34555 var cid = this.cm.getColumnId(colIndex);
34556 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34557 if(this.grid.autoSizeHeaders){
34558 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34561 var newWidth = this.calcColumnWidth(colIndex);
34562 this.cm.setColumnWidth(colIndex,
34563 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34564 if(!suppressEvent){
34565 this.grid.fireEvent("columnresize", colIndex, newWidth);
34570 * Autofits all columns to their content and then expands to fit any extra space in the grid
34572 autoSizeColumns : function(){
34573 var cm = this.grid.colModel;
34574 var colCount = cm.getColumnCount();
34575 for(var i = 0; i < colCount; i++){
34576 this.autoSizeColumn(i, true, true);
34578 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34581 this.updateColumns();
34587 * Autofits all columns to the grid's width proportionate with their current size
34588 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34590 fitColumns : function(reserveScrollSpace){
34591 var cm = this.grid.colModel;
34592 var colCount = cm.getColumnCount();
34596 for (i = 0; i < colCount; i++){
34597 if(!cm.isHidden(i) && !cm.isFixed(i)){
34598 w = cm.getColumnWidth(i);
34604 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34605 if(reserveScrollSpace){
34608 var frac = (avail - cm.getTotalWidth())/width;
34609 while (cols.length){
34612 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34614 this.updateColumns();
34618 onRowSelect : function(rowIndex){
34619 var row = this.getRowComposite(rowIndex);
34620 row.addClass("x-grid-row-selected");
34623 onRowDeselect : function(rowIndex){
34624 var row = this.getRowComposite(rowIndex);
34625 row.removeClass("x-grid-row-selected");
34628 onCellSelect : function(row, col){
34629 var cell = this.getCell(row, col);
34631 Roo.fly(cell).addClass("x-grid-cell-selected");
34635 onCellDeselect : function(row, col){
34636 var cell = this.getCell(row, col);
34638 Roo.fly(cell).removeClass("x-grid-cell-selected");
34642 updateHeaderSortState : function(){
34644 // sort state can be single { field: xxx, direction : yyy}
34645 // or { xxx=>ASC , yyy : DESC ..... }
34648 if (!this.ds.multiSort) {
34649 var state = this.ds.getSortState();
34653 mstate[state.field] = state.direction;
34654 // FIXME... - this is not used here.. but might be elsewhere..
34655 this.sortState = state;
34658 mstate = this.ds.sortToggle;
34660 //remove existing sort classes..
34662 var sc = this.sortClasses;
34663 var hds = this.el.select(this.headerSelector).removeClass(sc);
34665 for(var f in mstate) {
34667 var sortColumn = this.cm.findColumnIndex(f);
34669 if(sortColumn != -1){
34670 var sortDir = mstate[f];
34671 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34680 handleHeaderClick : function(g, index,e){
34682 Roo.log("header click");
34685 // touch events on header are handled by context
34686 this.handleHdCtx(g,index,e);
34691 if(this.headersDisabled){
34694 var dm = g.dataSource, cm = g.colModel;
34695 if(!cm.isSortable(index)){
34700 if (dm.multiSort) {
34701 // update the sortOrder
34703 for(var i = 0; i < cm.config.length; i++ ) {
34705 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34706 continue; // dont' bother, it's not in sort list or being set.
34709 so.push(cm.config[i].dataIndex);
34715 dm.sort(cm.getDataIndex(index));
34719 destroy : function(){
34721 this.colMenu.removeAll();
34722 Roo.menu.MenuMgr.unregister(this.colMenu);
34723 this.colMenu.getEl().remove();
34724 delete this.colMenu;
34727 this.hmenu.removeAll();
34728 Roo.menu.MenuMgr.unregister(this.hmenu);
34729 this.hmenu.getEl().remove();
34732 if(this.grid.enableColumnMove){
34733 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34735 for(var dd in dds){
34736 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34737 var elid = dds[dd].dragElId;
34739 Roo.get(elid).remove();
34740 } else if(dds[dd].config.isTarget){
34741 dds[dd].proxyTop.remove();
34742 dds[dd].proxyBottom.remove();
34745 if(Roo.dd.DDM.locationCache[dd]){
34746 delete Roo.dd.DDM.locationCache[dd];
34749 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34752 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34753 this.bind(null, null);
34754 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34757 handleLockChange : function(){
34758 this.refresh(true);
34761 onDenyColumnLock : function(){
34765 onDenyColumnHide : function(){
34769 handleHdMenuClick : function(item){
34770 var index = this.hdCtxIndex;
34771 var cm = this.cm, ds = this.ds;
34774 ds.sort(cm.getDataIndex(index), "ASC");
34777 ds.sort(cm.getDataIndex(index), "DESC");
34780 var lc = cm.getLockedCount();
34781 if(cm.getColumnCount(true) <= lc+1){
34782 this.onDenyColumnLock();
34786 cm.setLocked(index, true, true);
34787 cm.moveColumn(index, lc);
34788 this.grid.fireEvent("columnmove", index, lc);
34790 cm.setLocked(index, true);
34794 var lc = cm.getLockedCount();
34795 if((lc-1) != index){
34796 cm.setLocked(index, false, true);
34797 cm.moveColumn(index, lc-1);
34798 this.grid.fireEvent("columnmove", index, lc-1);
34800 cm.setLocked(index, false);
34803 case 'wider': // used to expand cols on touch..
34805 var cw = cm.getColumnWidth(index);
34806 cw += (item.id == 'wider' ? 1 : -1) * 50;
34807 cw = Math.max(0, cw);
34808 cw = Math.min(cw,4000);
34809 cm.setColumnWidth(index, cw);
34813 index = cm.getIndexById(item.id.substr(4));
34815 if(item.checked && cm.getColumnCount(true) <= 1){
34816 this.onDenyColumnHide();
34819 cm.setHidden(index, item.checked);
34825 beforeColMenuShow : function(){
34826 var cm = this.cm, colCount = cm.getColumnCount();
34827 this.colMenu.removeAll();
34828 for(var i = 0; i < colCount; i++){
34829 this.colMenu.add(new Roo.menu.CheckItem({
34830 id: "col-"+cm.getColumnId(i),
34831 text: cm.getColumnHeader(i),
34832 checked: !cm.isHidden(i),
34838 handleHdCtx : function(g, index, e){
34840 var hd = this.getHeaderCell(index);
34841 this.hdCtxIndex = index;
34842 var ms = this.hmenu.items, cm = this.cm;
34843 ms.get("asc").setDisabled(!cm.isSortable(index));
34844 ms.get("desc").setDisabled(!cm.isSortable(index));
34845 if(this.grid.enableColLock !== false){
34846 ms.get("lock").setDisabled(cm.isLocked(index));
34847 ms.get("unlock").setDisabled(!cm.isLocked(index));
34849 this.hmenu.show(hd, "tl-bl");
34852 handleHdOver : function(e){
34853 var hd = this.findHeaderCell(e.getTarget());
34854 if(hd && !this.headersDisabled){
34855 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34856 this.fly(hd).addClass("x-grid-hd-over");
34861 handleHdOut : function(e){
34862 var hd = this.findHeaderCell(e.getTarget());
34864 this.fly(hd).removeClass("x-grid-hd-over");
34868 handleSplitDblClick : function(e, t){
34869 var i = this.getCellIndex(t);
34870 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34871 this.autoSizeColumn(i, true);
34876 render : function(){
34879 var colCount = cm.getColumnCount();
34881 if(this.grid.monitorWindowResize === true){
34882 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34884 var header = this.renderHeaders();
34885 var body = this.templates.body.apply({rows:""});
34886 var html = this.templates.master.apply({
34889 lockedHeader: header[0],
34893 //this.updateColumns();
34895 this.grid.getGridEl().dom.innerHTML = html;
34897 this.initElements();
34899 // a kludge to fix the random scolling effect in webkit
34900 this.el.on("scroll", function() {
34901 this.el.dom.scrollTop=0; // hopefully not recursive..
34904 this.scroller.on("scroll", this.handleScroll, this);
34905 this.lockedBody.on("mousewheel", this.handleWheel, this);
34906 this.mainBody.on("mousewheel", this.handleWheel, this);
34908 this.mainHd.on("mouseover", this.handleHdOver, this);
34909 this.mainHd.on("mouseout", this.handleHdOut, this);
34910 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34911 {delegate: "."+this.splitClass});
34913 this.lockedHd.on("mouseover", this.handleHdOver, this);
34914 this.lockedHd.on("mouseout", this.handleHdOut, this);
34915 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34916 {delegate: "."+this.splitClass});
34918 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34919 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34922 this.updateSplitters();
34924 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34925 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34926 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34929 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34930 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34932 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34933 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34935 if(this.grid.enableColLock !== false){
34936 this.hmenu.add('-',
34937 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34938 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34942 this.hmenu.add('-',
34943 {id:"wider", text: this.columnsWiderText},
34944 {id:"narrow", text: this.columnsNarrowText }
34950 if(this.grid.enableColumnHide !== false){
34952 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34953 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34954 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34956 this.hmenu.add('-',
34957 {id:"columns", text: this.columnsText, menu: this.colMenu}
34960 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34962 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34965 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34966 this.dd = new Roo.grid.GridDragZone(this.grid, {
34967 ddGroup : this.grid.ddGroup || 'GridDD'
34973 for(var i = 0; i < colCount; i++){
34974 if(cm.isHidden(i)){
34975 this.hideColumn(i);
34977 if(cm.config[i].align){
34978 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34979 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34983 this.updateHeaderSortState();
34985 this.beforeInitialResize();
34988 // two part rendering gives faster view to the user
34989 this.renderPhase2.defer(1, this);
34992 renderPhase2 : function(){
34993 // render the rows now
34995 if(this.grid.autoSizeColumns){
34996 this.autoSizeColumns();
35000 beforeInitialResize : function(){
35004 onColumnSplitterMoved : function(i, w){
35005 this.userResized = true;
35006 var cm = this.grid.colModel;
35007 cm.setColumnWidth(i, w, true);
35008 var cid = cm.getColumnId(i);
35009 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35010 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35011 this.updateSplitters();
35013 this.grid.fireEvent("columnresize", i, w);
35016 syncRowHeights : function(startIndex, endIndex){
35017 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35018 startIndex = startIndex || 0;
35019 var mrows = this.getBodyTable().rows;
35020 var lrows = this.getLockedTable().rows;
35021 var len = mrows.length-1;
35022 endIndex = Math.min(endIndex || len, len);
35023 for(var i = startIndex; i <= endIndex; i++){
35024 var m = mrows[i], l = lrows[i];
35025 var h = Math.max(m.offsetHeight, l.offsetHeight);
35026 m.style.height = l.style.height = h + "px";
35031 layout : function(initialRender, is2ndPass){
35033 var auto = g.autoHeight;
35034 var scrollOffset = 16;
35035 var c = g.getGridEl(), cm = this.cm,
35036 expandCol = g.autoExpandColumn,
35038 //c.beginMeasure();
35040 if(!c.dom.offsetWidth){ // display:none?
35042 this.lockedWrap.show();
35043 this.mainWrap.show();
35048 var hasLock = this.cm.isLocked(0);
35050 var tbh = this.headerPanel.getHeight();
35051 var bbh = this.footerPanel.getHeight();
35054 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35055 var newHeight = ch + c.getBorderWidth("tb");
35057 newHeight = Math.min(g.maxHeight, newHeight);
35059 c.setHeight(newHeight);
35063 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35066 var s = this.scroller;
35068 var csize = c.getSize(true);
35070 this.el.setSize(csize.width, csize.height);
35072 this.headerPanel.setWidth(csize.width);
35073 this.footerPanel.setWidth(csize.width);
35075 var hdHeight = this.mainHd.getHeight();
35076 var vw = csize.width;
35077 var vh = csize.height - (tbh + bbh);
35081 var bt = this.getBodyTable();
35083 if(cm.getLockedCount() == cm.config.length){
35084 bt = this.getLockedTable();
35087 var ltWidth = hasLock ?
35088 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35090 var scrollHeight = bt.offsetHeight;
35091 var scrollWidth = ltWidth + bt.offsetWidth;
35092 var vscroll = false, hscroll = false;
35094 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35096 var lw = this.lockedWrap, mw = this.mainWrap;
35097 var lb = this.lockedBody, mb = this.mainBody;
35099 setTimeout(function(){
35100 var t = s.dom.offsetTop;
35101 var w = s.dom.clientWidth,
35102 h = s.dom.clientHeight;
35105 lw.setSize(ltWidth, h);
35107 mw.setLeftTop(ltWidth, t);
35108 mw.setSize(w-ltWidth, h);
35110 lb.setHeight(h-hdHeight);
35111 mb.setHeight(h-hdHeight);
35113 if(is2ndPass !== true && !gv.userResized && expandCol){
35114 // high speed resize without full column calculation
35116 var ci = cm.getIndexById(expandCol);
35118 ci = cm.findColumnIndex(expandCol);
35120 ci = Math.max(0, ci); // make sure it's got at least the first col.
35121 var expandId = cm.getColumnId(ci);
35122 var tw = cm.getTotalWidth(false);
35123 var currentWidth = cm.getColumnWidth(ci);
35124 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35125 if(currentWidth != cw){
35126 cm.setColumnWidth(ci, cw, true);
35127 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35128 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35129 gv.updateSplitters();
35130 gv.layout(false, true);
35142 onWindowResize : function(){
35143 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35149 appendFooter : function(parentEl){
35153 sortAscText : "Sort Ascending",
35154 sortDescText : "Sort Descending",
35155 lockText : "Lock Column",
35156 unlockText : "Unlock Column",
35157 columnsText : "Columns",
35159 columnsWiderText : "Wider",
35160 columnsNarrowText : "Thinner"
35164 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35165 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35166 this.proxy.el.addClass('x-grid3-col-dd');
35169 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35170 handleMouseDown : function(e){
35174 callHandleMouseDown : function(e){
35175 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35180 * Ext JS Library 1.1.1
35181 * Copyright(c) 2006-2007, Ext JS, LLC.
35183 * Originally Released Under LGPL - original licence link has changed is not relivant.
35186 * <script type="text/javascript">
35190 // This is a support class used internally by the Grid components
35191 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35193 this.view = grid.getView();
35194 this.proxy = this.view.resizeProxy;
35195 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35196 "gridSplitters" + this.grid.getGridEl().id, {
35197 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35199 this.setHandleElId(Roo.id(hd));
35200 this.setOuterHandleElId(Roo.id(hd2));
35201 this.scroll = false;
35203 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35204 fly: Roo.Element.fly,
35206 b4StartDrag : function(x, y){
35207 this.view.headersDisabled = true;
35208 this.proxy.setHeight(this.view.mainWrap.getHeight());
35209 var w = this.cm.getColumnWidth(this.cellIndex);
35210 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35211 this.resetConstraints();
35212 this.setXConstraint(minw, 1000);
35213 this.setYConstraint(0, 0);
35214 this.minX = x - minw;
35215 this.maxX = x + 1000;
35217 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35221 handleMouseDown : function(e){
35222 ev = Roo.EventObject.setEvent(e);
35223 var t = this.fly(ev.getTarget());
35224 if(t.hasClass("x-grid-split")){
35225 this.cellIndex = this.view.getCellIndex(t.dom);
35226 this.split = t.dom;
35227 this.cm = this.grid.colModel;
35228 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35229 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35234 endDrag : function(e){
35235 this.view.headersDisabled = false;
35236 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35237 var diff = endX - this.startPos;
35238 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35241 autoOffset : function(){
35242 this.setDelta(0,0);
35246 * Ext JS Library 1.1.1
35247 * Copyright(c) 2006-2007, Ext JS, LLC.
35249 * Originally Released Under LGPL - original licence link has changed is not relivant.
35252 * <script type="text/javascript">
35256 // This is a support class used internally by the Grid components
35257 Roo.grid.GridDragZone = function(grid, config){
35258 this.view = grid.getView();
35259 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35260 if(this.view.lockedBody){
35261 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35262 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35264 this.scroll = false;
35266 this.ddel = document.createElement('div');
35267 this.ddel.className = 'x-grid-dd-wrap';
35270 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35271 ddGroup : "GridDD",
35273 getDragData : function(e){
35274 var t = Roo.lib.Event.getTarget(e);
35275 var rowIndex = this.view.findRowIndex(t);
35276 var sm = this.grid.selModel;
35278 //Roo.log(rowIndex);
35280 if (sm.getSelectedCell) {
35281 // cell selection..
35282 if (!sm.getSelectedCell()) {
35285 if (rowIndex != sm.getSelectedCell()[0]) {
35291 if(rowIndex !== false){
35296 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35298 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35301 if (e.hasModifier()){
35302 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35305 Roo.log("getDragData");
35310 rowIndex: rowIndex,
35311 selections:sm.getSelections ? sm.getSelections() : (
35312 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35319 onInitDrag : function(e){
35320 var data = this.dragData;
35321 this.ddel.innerHTML = this.grid.getDragDropText();
35322 this.proxy.update(this.ddel);
35323 // fire start drag?
35326 afterRepair : function(){
35327 this.dragging = false;
35330 getRepairXY : function(e, data){
35334 onEndDrag : function(data, e){
35338 onValidDrop : function(dd, e, id){
35343 beforeInvalidDrop : function(e, id){
35348 * Ext JS Library 1.1.1
35349 * Copyright(c) 2006-2007, Ext JS, LLC.
35351 * Originally Released Under LGPL - original licence link has changed is not relivant.
35354 * <script type="text/javascript">
35359 * @class Roo.grid.ColumnModel
35360 * @extends Roo.util.Observable
35361 * This is the default implementation of a ColumnModel used by the Grid. It defines
35362 * the columns in the grid.
35365 var colModel = new Roo.grid.ColumnModel([
35366 {header: "Ticker", width: 60, sortable: true, locked: true},
35367 {header: "Company Name", width: 150, sortable: true},
35368 {header: "Market Cap.", width: 100, sortable: true},
35369 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35370 {header: "Employees", width: 100, sortable: true, resizable: false}
35375 * The config options listed for this class are options which may appear in each
35376 * individual column definition.
35377 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35379 * @param {Object} config An Array of column config objects. See this class's
35380 * config objects for details.
35382 Roo.grid.ColumnModel = function(config){
35384 * The config passed into the constructor
35386 this.config = config;
35389 // if no id, create one
35390 // if the column does not have a dataIndex mapping,
35391 // map it to the order it is in the config
35392 for(var i = 0, len = config.length; i < len; i++){
35394 if(typeof c.dataIndex == "undefined"){
35397 if(typeof c.renderer == "string"){
35398 c.renderer = Roo.util.Format[c.renderer];
35400 if(typeof c.id == "undefined"){
35403 if(c.editor && c.editor.xtype){
35404 c.editor = Roo.factory(c.editor, Roo.grid);
35406 if(c.editor && c.editor.isFormField){
35407 c.editor = new Roo.grid.GridEditor(c.editor);
35409 this.lookup[c.id] = c;
35413 * The width of columns which have no width specified (defaults to 100)
35416 this.defaultWidth = 100;
35419 * Default sortable of columns which have no sortable specified (defaults to false)
35422 this.defaultSortable = false;
35426 * @event widthchange
35427 * Fires when the width of a column changes.
35428 * @param {ColumnModel} this
35429 * @param {Number} columnIndex The column index
35430 * @param {Number} newWidth The new width
35432 "widthchange": true,
35434 * @event headerchange
35435 * Fires when the text of a header changes.
35436 * @param {ColumnModel} this
35437 * @param {Number} columnIndex The column index
35438 * @param {Number} newText The new header text
35440 "headerchange": true,
35442 * @event hiddenchange
35443 * Fires when a column is hidden or "unhidden".
35444 * @param {ColumnModel} this
35445 * @param {Number} columnIndex The column index
35446 * @param {Boolean} hidden true if hidden, false otherwise
35448 "hiddenchange": true,
35450 * @event columnmoved
35451 * Fires when a column is moved.
35452 * @param {ColumnModel} this
35453 * @param {Number} oldIndex
35454 * @param {Number} newIndex
35456 "columnmoved" : true,
35458 * @event columlockchange
35459 * Fires when a column's locked state is changed
35460 * @param {ColumnModel} this
35461 * @param {Number} colIndex
35462 * @param {Boolean} locked true if locked
35464 "columnlockchange" : true
35466 Roo.grid.ColumnModel.superclass.constructor.call(this);
35468 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35470 * @cfg {String} header The header text to display in the Grid view.
35473 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35474 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35475 * specified, the column's index is used as an index into the Record's data Array.
35478 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35479 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35482 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35483 * Defaults to the value of the {@link #defaultSortable} property.
35484 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35487 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35490 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35493 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35496 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35499 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35500 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35501 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35502 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35505 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35508 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35511 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35514 * @cfg {String} cursor (Optional)
35517 * @cfg {String} tooltip (Optional)
35520 * @cfg {Number} xs (Optional)
35523 * @cfg {Number} sm (Optional)
35526 * @cfg {Number} md (Optional)
35529 * @cfg {Number} lg (Optional)
35532 * Returns the id of the column at the specified index.
35533 * @param {Number} index The column index
35534 * @return {String} the id
35536 getColumnId : function(index){
35537 return this.config[index].id;
35541 * Returns the column for a specified id.
35542 * @param {String} id The column id
35543 * @return {Object} the column
35545 getColumnById : function(id){
35546 return this.lookup[id];
35551 * Returns the column for a specified dataIndex.
35552 * @param {String} dataIndex The column dataIndex
35553 * @return {Object|Boolean} the column or false if not found
35555 getColumnByDataIndex: function(dataIndex){
35556 var index = this.findColumnIndex(dataIndex);
35557 return index > -1 ? this.config[index] : false;
35561 * Returns the index for a specified column id.
35562 * @param {String} id The column id
35563 * @return {Number} the index, or -1 if not found
35565 getIndexById : function(id){
35566 for(var i = 0, len = this.config.length; i < len; i++){
35567 if(this.config[i].id == id){
35575 * Returns the index for a specified column dataIndex.
35576 * @param {String} dataIndex The column dataIndex
35577 * @return {Number} the index, or -1 if not found
35580 findColumnIndex : function(dataIndex){
35581 for(var i = 0, len = this.config.length; i < len; i++){
35582 if(this.config[i].dataIndex == dataIndex){
35590 moveColumn : function(oldIndex, newIndex){
35591 var c = this.config[oldIndex];
35592 this.config.splice(oldIndex, 1);
35593 this.config.splice(newIndex, 0, c);
35594 this.dataMap = null;
35595 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35598 isLocked : function(colIndex){
35599 return this.config[colIndex].locked === true;
35602 setLocked : function(colIndex, value, suppressEvent){
35603 if(this.isLocked(colIndex) == value){
35606 this.config[colIndex].locked = value;
35607 if(!suppressEvent){
35608 this.fireEvent("columnlockchange", this, colIndex, value);
35612 getTotalLockedWidth : function(){
35613 var totalWidth = 0;
35614 for(var i = 0; i < this.config.length; i++){
35615 if(this.isLocked(i) && !this.isHidden(i)){
35616 this.totalWidth += this.getColumnWidth(i);
35622 getLockedCount : function(){
35623 for(var i = 0, len = this.config.length; i < len; i++){
35624 if(!this.isLocked(i)){
35629 return this.config.length;
35633 * Returns the number of columns.
35636 getColumnCount : function(visibleOnly){
35637 if(visibleOnly === true){
35639 for(var i = 0, len = this.config.length; i < len; i++){
35640 if(!this.isHidden(i)){
35646 return this.config.length;
35650 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35651 * @param {Function} fn
35652 * @param {Object} scope (optional)
35653 * @return {Array} result
35655 getColumnsBy : function(fn, scope){
35657 for(var i = 0, len = this.config.length; i < len; i++){
35658 var c = this.config[i];
35659 if(fn.call(scope||this, c, i) === true){
35667 * Returns true if the specified column is sortable.
35668 * @param {Number} col The column index
35669 * @return {Boolean}
35671 isSortable : function(col){
35672 if(typeof this.config[col].sortable == "undefined"){
35673 return this.defaultSortable;
35675 return this.config[col].sortable;
35679 * Returns the rendering (formatting) function defined for the column.
35680 * @param {Number} col The column index.
35681 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35683 getRenderer : function(col){
35684 if(!this.config[col].renderer){
35685 return Roo.grid.ColumnModel.defaultRenderer;
35687 return this.config[col].renderer;
35691 * Sets the rendering (formatting) function for a column.
35692 * @param {Number} col The column index
35693 * @param {Function} fn The function to use to process the cell's raw data
35694 * to return HTML markup for the grid view. The render function is called with
35695 * the following parameters:<ul>
35696 * <li>Data value.</li>
35697 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35698 * <li>css A CSS style string to apply to the table cell.</li>
35699 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35700 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35701 * <li>Row index</li>
35702 * <li>Column index</li>
35703 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35705 setRenderer : function(col, fn){
35706 this.config[col].renderer = fn;
35710 * Returns the width for the specified column.
35711 * @param {Number} col The column index
35714 getColumnWidth : function(col){
35715 return this.config[col].width * 1 || this.defaultWidth;
35719 * Sets the width for a column.
35720 * @param {Number} col The column index
35721 * @param {Number} width The new width
35723 setColumnWidth : function(col, width, suppressEvent){
35724 this.config[col].width = width;
35725 this.totalWidth = null;
35726 if(!suppressEvent){
35727 this.fireEvent("widthchange", this, col, width);
35732 * Returns the total width of all columns.
35733 * @param {Boolean} includeHidden True to include hidden column widths
35736 getTotalWidth : function(includeHidden){
35737 if(!this.totalWidth){
35738 this.totalWidth = 0;
35739 for(var i = 0, len = this.config.length; i < len; i++){
35740 if(includeHidden || !this.isHidden(i)){
35741 this.totalWidth += this.getColumnWidth(i);
35745 return this.totalWidth;
35749 * Returns the header for the specified column.
35750 * @param {Number} col The column index
35753 getColumnHeader : function(col){
35754 return this.config[col].header;
35758 * Sets the header for a column.
35759 * @param {Number} col The column index
35760 * @param {String} header The new header
35762 setColumnHeader : function(col, header){
35763 this.config[col].header = header;
35764 this.fireEvent("headerchange", this, col, header);
35768 * Returns the tooltip for the specified column.
35769 * @param {Number} col The column index
35772 getColumnTooltip : function(col){
35773 return this.config[col].tooltip;
35776 * Sets the tooltip for a column.
35777 * @param {Number} col The column index
35778 * @param {String} tooltip The new tooltip
35780 setColumnTooltip : function(col, tooltip){
35781 this.config[col].tooltip = tooltip;
35785 * Returns the dataIndex for the specified column.
35786 * @param {Number} col The column index
35789 getDataIndex : function(col){
35790 return this.config[col].dataIndex;
35794 * Sets the dataIndex for a column.
35795 * @param {Number} col The column index
35796 * @param {Number} dataIndex The new dataIndex
35798 setDataIndex : function(col, dataIndex){
35799 this.config[col].dataIndex = dataIndex;
35805 * Returns true if the cell is editable.
35806 * @param {Number} colIndex The column index
35807 * @param {Number} rowIndex The row index - this is nto actually used..?
35808 * @return {Boolean}
35810 isCellEditable : function(colIndex, rowIndex){
35811 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35815 * Returns the editor defined for the cell/column.
35816 * return false or null to disable editing.
35817 * @param {Number} colIndex The column index
35818 * @param {Number} rowIndex The row index
35821 getCellEditor : function(colIndex, rowIndex){
35822 return this.config[colIndex].editor;
35826 * Sets if a column is editable.
35827 * @param {Number} col The column index
35828 * @param {Boolean} editable True if the column is editable
35830 setEditable : function(col, editable){
35831 this.config[col].editable = editable;
35836 * Returns true if the column is hidden.
35837 * @param {Number} colIndex The column index
35838 * @return {Boolean}
35840 isHidden : function(colIndex){
35841 return this.config[colIndex].hidden;
35846 * Returns true if the column width cannot be changed
35848 isFixed : function(colIndex){
35849 return this.config[colIndex].fixed;
35853 * Returns true if the column can be resized
35854 * @return {Boolean}
35856 isResizable : function(colIndex){
35857 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35860 * Sets if a column is hidden.
35861 * @param {Number} colIndex The column index
35862 * @param {Boolean} hidden True if the column is hidden
35864 setHidden : function(colIndex, hidden){
35865 this.config[colIndex].hidden = hidden;
35866 this.totalWidth = null;
35867 this.fireEvent("hiddenchange", this, colIndex, hidden);
35871 * Sets the editor for a column.
35872 * @param {Number} col The column index
35873 * @param {Object} editor The editor object
35875 setEditor : function(col, editor){
35876 this.config[col].editor = editor;
35880 Roo.grid.ColumnModel.defaultRenderer = function(value)
35882 if(typeof value == "object") {
35885 if(typeof value == "string" && value.length < 1){
35889 return String.format("{0}", value);
35892 // Alias for backwards compatibility
35893 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35896 * Ext JS Library 1.1.1
35897 * Copyright(c) 2006-2007, Ext JS, LLC.
35899 * Originally Released Under LGPL - original licence link has changed is not relivant.
35902 * <script type="text/javascript">
35906 * @class Roo.grid.AbstractSelectionModel
35907 * @extends Roo.util.Observable
35908 * Abstract base class for grid SelectionModels. It provides the interface that should be
35909 * implemented by descendant classes. This class should not be directly instantiated.
35912 Roo.grid.AbstractSelectionModel = function(){
35913 this.locked = false;
35914 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35917 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35918 /** @ignore Called by the grid automatically. Do not call directly. */
35919 init : function(grid){
35925 * Locks the selections.
35928 this.locked = true;
35932 * Unlocks the selections.
35934 unlock : function(){
35935 this.locked = false;
35939 * Returns true if the selections are locked.
35940 * @return {Boolean}
35942 isLocked : function(){
35943 return this.locked;
35947 * Ext JS Library 1.1.1
35948 * Copyright(c) 2006-2007, Ext JS, LLC.
35950 * Originally Released Under LGPL - original licence link has changed is not relivant.
35953 * <script type="text/javascript">
35956 * @extends Roo.grid.AbstractSelectionModel
35957 * @class Roo.grid.RowSelectionModel
35958 * The default SelectionModel used by {@link Roo.grid.Grid}.
35959 * It supports multiple selections and keyboard selection/navigation.
35961 * @param {Object} config
35963 Roo.grid.RowSelectionModel = function(config){
35964 Roo.apply(this, config);
35965 this.selections = new Roo.util.MixedCollection(false, function(o){
35970 this.lastActive = false;
35974 * @event selectionchange
35975 * Fires when the selection changes
35976 * @param {SelectionModel} this
35978 "selectionchange" : true,
35980 * @event afterselectionchange
35981 * Fires after the selection changes (eg. by key press or clicking)
35982 * @param {SelectionModel} this
35984 "afterselectionchange" : true,
35986 * @event beforerowselect
35987 * Fires when a row is selected being selected, return false to cancel.
35988 * @param {SelectionModel} this
35989 * @param {Number} rowIndex The selected index
35990 * @param {Boolean} keepExisting False if other selections will be cleared
35992 "beforerowselect" : true,
35995 * Fires when a row is selected.
35996 * @param {SelectionModel} this
35997 * @param {Number} rowIndex The selected index
35998 * @param {Roo.data.Record} r The record
36000 "rowselect" : true,
36002 * @event rowdeselect
36003 * Fires when a row is deselected.
36004 * @param {SelectionModel} this
36005 * @param {Number} rowIndex The selected index
36007 "rowdeselect" : true
36009 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36010 this.locked = false;
36013 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
36015 * @cfg {Boolean} singleSelect
36016 * True to allow selection of only one row at a time (defaults to false)
36018 singleSelect : false,
36021 initEvents : function(){
36023 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36024 this.grid.on("mousedown", this.handleMouseDown, this);
36025 }else{ // allow click to work like normal
36026 this.grid.on("rowclick", this.handleDragableRowClick, this);
36029 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36030 "up" : function(e){
36032 this.selectPrevious(e.shiftKey);
36033 }else if(this.last !== false && this.lastActive !== false){
36034 var last = this.last;
36035 this.selectRange(this.last, this.lastActive-1);
36036 this.grid.getView().focusRow(this.lastActive);
36037 if(last !== false){
36041 this.selectFirstRow();
36043 this.fireEvent("afterselectionchange", this);
36045 "down" : function(e){
36047 this.selectNext(e.shiftKey);
36048 }else if(this.last !== false && this.lastActive !== false){
36049 var last = this.last;
36050 this.selectRange(this.last, this.lastActive+1);
36051 this.grid.getView().focusRow(this.lastActive);
36052 if(last !== false){
36056 this.selectFirstRow();
36058 this.fireEvent("afterselectionchange", this);
36063 var view = this.grid.view;
36064 view.on("refresh", this.onRefresh, this);
36065 view.on("rowupdated", this.onRowUpdated, this);
36066 view.on("rowremoved", this.onRemove, this);
36070 onRefresh : function(){
36071 var ds = this.grid.dataSource, i, v = this.grid.view;
36072 var s = this.selections;
36073 s.each(function(r){
36074 if((i = ds.indexOfId(r.id)) != -1){
36076 s.add(ds.getAt(i)); // updating the selection relate data
36084 onRemove : function(v, index, r){
36085 this.selections.remove(r);
36089 onRowUpdated : function(v, index, r){
36090 if(this.isSelected(r)){
36091 v.onRowSelect(index);
36097 * @param {Array} records The records to select
36098 * @param {Boolean} keepExisting (optional) True to keep existing selections
36100 selectRecords : function(records, keepExisting){
36102 this.clearSelections();
36104 var ds = this.grid.dataSource;
36105 for(var i = 0, len = records.length; i < len; i++){
36106 this.selectRow(ds.indexOf(records[i]), true);
36111 * Gets the number of selected rows.
36114 getCount : function(){
36115 return this.selections.length;
36119 * Selects the first row in the grid.
36121 selectFirstRow : function(){
36126 * Select the last row.
36127 * @param {Boolean} keepExisting (optional) True to keep existing selections
36129 selectLastRow : function(keepExisting){
36130 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36134 * Selects the row immediately following the last selected row.
36135 * @param {Boolean} keepExisting (optional) True to keep existing selections
36137 selectNext : function(keepExisting){
36138 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36139 this.selectRow(this.last+1, keepExisting);
36140 this.grid.getView().focusRow(this.last);
36145 * Selects the row that precedes the last selected row.
36146 * @param {Boolean} keepExisting (optional) True to keep existing selections
36148 selectPrevious : function(keepExisting){
36150 this.selectRow(this.last-1, keepExisting);
36151 this.grid.getView().focusRow(this.last);
36156 * Returns the selected records
36157 * @return {Array} Array of selected records
36159 getSelections : function(){
36160 return [].concat(this.selections.items);
36164 * Returns the first selected record.
36167 getSelected : function(){
36168 return this.selections.itemAt(0);
36173 * Clears all selections.
36175 clearSelections : function(fast){
36180 var ds = this.grid.dataSource;
36181 var s = this.selections;
36182 s.each(function(r){
36183 this.deselectRow(ds.indexOfId(r.id));
36187 this.selections.clear();
36194 * Selects all rows.
36196 selectAll : function(){
36200 this.selections.clear();
36201 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36202 this.selectRow(i, true);
36207 * Returns True if there is a selection.
36208 * @return {Boolean}
36210 hasSelection : function(){
36211 return this.selections.length > 0;
36215 * Returns True if the specified row is selected.
36216 * @param {Number/Record} record The record or index of the record to check
36217 * @return {Boolean}
36219 isSelected : function(index){
36220 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36221 return (r && this.selections.key(r.id) ? true : false);
36225 * Returns True if the specified record id is selected.
36226 * @param {String} id The id of record to check
36227 * @return {Boolean}
36229 isIdSelected : function(id){
36230 return (this.selections.key(id) ? true : false);
36234 handleMouseDown : function(e, t){
36235 var view = this.grid.getView(), rowIndex;
36236 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36239 if(e.shiftKey && this.last !== false){
36240 var last = this.last;
36241 this.selectRange(last, rowIndex, e.ctrlKey);
36242 this.last = last; // reset the last
36243 view.focusRow(rowIndex);
36245 var isSelected = this.isSelected(rowIndex);
36246 if(e.button !== 0 && isSelected){
36247 view.focusRow(rowIndex);
36248 }else if(e.ctrlKey && isSelected){
36249 this.deselectRow(rowIndex);
36250 }else if(!isSelected){
36251 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36252 view.focusRow(rowIndex);
36255 this.fireEvent("afterselectionchange", this);
36258 handleDragableRowClick : function(grid, rowIndex, e)
36260 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36261 this.selectRow(rowIndex, false);
36262 grid.view.focusRow(rowIndex);
36263 this.fireEvent("afterselectionchange", this);
36268 * Selects multiple rows.
36269 * @param {Array} rows Array of the indexes of the row to select
36270 * @param {Boolean} keepExisting (optional) True to keep existing selections
36272 selectRows : function(rows, keepExisting){
36274 this.clearSelections();
36276 for(var i = 0, len = rows.length; i < len; i++){
36277 this.selectRow(rows[i], true);
36282 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36283 * @param {Number} startRow The index of the first row in the range
36284 * @param {Number} endRow The index of the last row in the range
36285 * @param {Boolean} keepExisting (optional) True to retain existing selections
36287 selectRange : function(startRow, endRow, keepExisting){
36292 this.clearSelections();
36294 if(startRow <= endRow){
36295 for(var i = startRow; i <= endRow; i++){
36296 this.selectRow(i, true);
36299 for(var i = startRow; i >= endRow; i--){
36300 this.selectRow(i, true);
36306 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36307 * @param {Number} startRow The index of the first row in the range
36308 * @param {Number} endRow The index of the last row in the range
36310 deselectRange : function(startRow, endRow, preventViewNotify){
36314 for(var i = startRow; i <= endRow; i++){
36315 this.deselectRow(i, preventViewNotify);
36321 * @param {Number} row The index of the row to select
36322 * @param {Boolean} keepExisting (optional) True to keep existing selections
36324 selectRow : function(index, keepExisting, preventViewNotify){
36325 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36328 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36329 if(!keepExisting || this.singleSelect){
36330 this.clearSelections();
36332 var r = this.grid.dataSource.getAt(index);
36333 this.selections.add(r);
36334 this.last = this.lastActive = index;
36335 if(!preventViewNotify){
36336 this.grid.getView().onRowSelect(index);
36338 this.fireEvent("rowselect", this, index, r);
36339 this.fireEvent("selectionchange", this);
36345 * @param {Number} row The index of the row to deselect
36347 deselectRow : function(index, preventViewNotify){
36351 if(this.last == index){
36354 if(this.lastActive == index){
36355 this.lastActive = false;
36357 var r = this.grid.dataSource.getAt(index);
36358 this.selections.remove(r);
36359 if(!preventViewNotify){
36360 this.grid.getView().onRowDeselect(index);
36362 this.fireEvent("rowdeselect", this, index);
36363 this.fireEvent("selectionchange", this);
36367 restoreLast : function(){
36369 this.last = this._last;
36374 acceptsNav : function(row, col, cm){
36375 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36379 onEditorKey : function(field, e){
36380 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36385 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36387 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36389 }else if(k == e.ENTER && !e.ctrlKey){
36393 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36395 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36397 }else if(k == e.ESC){
36401 g.startEditing(newCell[0], newCell[1]);
36406 * Ext JS Library 1.1.1
36407 * Copyright(c) 2006-2007, Ext JS, LLC.
36409 * Originally Released Under LGPL - original licence link has changed is not relivant.
36412 * <script type="text/javascript">
36415 * @class Roo.grid.CellSelectionModel
36416 * @extends Roo.grid.AbstractSelectionModel
36417 * This class provides the basic implementation for cell selection in a grid.
36419 * @param {Object} config The object containing the configuration of this model.
36420 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36422 Roo.grid.CellSelectionModel = function(config){
36423 Roo.apply(this, config);
36425 this.selection = null;
36429 * @event beforerowselect
36430 * Fires before a cell is selected.
36431 * @param {SelectionModel} this
36432 * @param {Number} rowIndex The selected row index
36433 * @param {Number} colIndex The selected cell index
36435 "beforecellselect" : true,
36437 * @event cellselect
36438 * Fires when a cell is selected.
36439 * @param {SelectionModel} this
36440 * @param {Number} rowIndex The selected row index
36441 * @param {Number} colIndex The selected cell index
36443 "cellselect" : true,
36445 * @event selectionchange
36446 * Fires when the active selection changes.
36447 * @param {SelectionModel} this
36448 * @param {Object} selection null for no selection or an object (o) with two properties
36450 <li>o.record: the record object for the row the selection is in</li>
36451 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36454 "selectionchange" : true,
36457 * Fires when the tab (or enter) was pressed on the last editable cell
36458 * You can use this to trigger add new row.
36459 * @param {SelectionModel} this
36463 * @event beforeeditnext
36464 * Fires before the next editable sell is made active
36465 * You can use this to skip to another cell or fire the tabend
36466 * if you set cell to false
36467 * @param {Object} eventdata object : { cell : [ row, col ] }
36469 "beforeeditnext" : true
36471 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36474 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36476 enter_is_tab: false,
36479 initEvents : function(){
36480 this.grid.on("mousedown", this.handleMouseDown, this);
36481 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36482 var view = this.grid.view;
36483 view.on("refresh", this.onViewChange, this);
36484 view.on("rowupdated", this.onRowUpdated, this);
36485 view.on("beforerowremoved", this.clearSelections, this);
36486 view.on("beforerowsinserted", this.clearSelections, this);
36487 if(this.grid.isEditor){
36488 this.grid.on("beforeedit", this.beforeEdit, this);
36493 beforeEdit : function(e){
36494 this.select(e.row, e.column, false, true, e.record);
36498 onRowUpdated : function(v, index, r){
36499 if(this.selection && this.selection.record == r){
36500 v.onCellSelect(index, this.selection.cell[1]);
36505 onViewChange : function(){
36506 this.clearSelections(true);
36510 * Returns the currently selected cell,.
36511 * @return {Array} The selected cell (row, column) or null if none selected.
36513 getSelectedCell : function(){
36514 return this.selection ? this.selection.cell : null;
36518 * Clears all selections.
36519 * @param {Boolean} true to prevent the gridview from being notified about the change.
36521 clearSelections : function(preventNotify){
36522 var s = this.selection;
36524 if(preventNotify !== true){
36525 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36527 this.selection = null;
36528 this.fireEvent("selectionchange", this, null);
36533 * Returns true if there is a selection.
36534 * @return {Boolean}
36536 hasSelection : function(){
36537 return this.selection ? true : false;
36541 handleMouseDown : function(e, t){
36542 var v = this.grid.getView();
36543 if(this.isLocked()){
36546 var row = v.findRowIndex(t);
36547 var cell = v.findCellIndex(t);
36548 if(row !== false && cell !== false){
36549 this.select(row, cell);
36555 * @param {Number} rowIndex
36556 * @param {Number} collIndex
36558 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36559 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36560 this.clearSelections();
36561 r = r || this.grid.dataSource.getAt(rowIndex);
36564 cell : [rowIndex, colIndex]
36566 if(!preventViewNotify){
36567 var v = this.grid.getView();
36568 v.onCellSelect(rowIndex, colIndex);
36569 if(preventFocus !== true){
36570 v.focusCell(rowIndex, colIndex);
36573 this.fireEvent("cellselect", this, rowIndex, colIndex);
36574 this.fireEvent("selectionchange", this, this.selection);
36579 isSelectable : function(rowIndex, colIndex, cm){
36580 return !cm.isHidden(colIndex);
36584 handleKeyDown : function(e){
36585 //Roo.log('Cell Sel Model handleKeyDown');
36586 if(!e.isNavKeyPress()){
36589 var g = this.grid, s = this.selection;
36592 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36594 this.select(cell[0], cell[1]);
36599 var walk = function(row, col, step){
36600 return g.walkCells(row, col, step, sm.isSelectable, sm);
36602 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36609 // handled by onEditorKey
36610 if (g.isEditor && g.editing) {
36614 newCell = walk(r, c-1, -1);
36616 newCell = walk(r, c+1, 1);
36621 newCell = walk(r+1, c, 1);
36625 newCell = walk(r-1, c, -1);
36629 newCell = walk(r, c+1, 1);
36633 newCell = walk(r, c-1, -1);
36638 if(g.isEditor && !g.editing){
36639 g.startEditing(r, c);
36648 this.select(newCell[0], newCell[1]);
36654 acceptsNav : function(row, col, cm){
36655 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36659 * @param {Number} field (not used) - as it's normally used as a listener
36660 * @param {Number} e - event - fake it by using
36662 * var e = Roo.EventObjectImpl.prototype;
36663 * e.keyCode = e.TAB
36667 onEditorKey : function(field, e){
36669 var k = e.getKey(),
36672 ed = g.activeEditor,
36674 ///Roo.log('onEditorKey' + k);
36677 if (this.enter_is_tab && k == e.ENTER) {
36683 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36685 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36691 } else if(k == e.ENTER && !e.ctrlKey){
36694 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36696 } else if(k == e.ESC){
36701 var ecall = { cell : newCell, forward : forward };
36702 this.fireEvent('beforeeditnext', ecall );
36703 newCell = ecall.cell;
36704 forward = ecall.forward;
36708 //Roo.log('next cell after edit');
36709 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36710 } else if (forward) {
36711 // tabbed past last
36712 this.fireEvent.defer(100, this, ['tabend',this]);
36717 * Ext JS Library 1.1.1
36718 * Copyright(c) 2006-2007, Ext JS, LLC.
36720 * Originally Released Under LGPL - original licence link has changed is not relivant.
36723 * <script type="text/javascript">
36727 * @class Roo.grid.EditorGrid
36728 * @extends Roo.grid.Grid
36729 * Class for creating and editable grid.
36730 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36731 * The container MUST have some type of size defined for the grid to fill. The container will be
36732 * automatically set to position relative if it isn't already.
36733 * @param {Object} dataSource The data model to bind to
36734 * @param {Object} colModel The column model with info about this grid's columns
36736 Roo.grid.EditorGrid = function(container, config){
36737 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36738 this.getGridEl().addClass("xedit-grid");
36740 if(!this.selModel){
36741 this.selModel = new Roo.grid.CellSelectionModel();
36744 this.activeEditor = null;
36748 * @event beforeedit
36749 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36750 * <ul style="padding:5px;padding-left:16px;">
36751 * <li>grid - This grid</li>
36752 * <li>record - The record being edited</li>
36753 * <li>field - The field name being edited</li>
36754 * <li>value - The value for the field being edited.</li>
36755 * <li>row - The grid row index</li>
36756 * <li>column - The grid column index</li>
36757 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36759 * @param {Object} e An edit event (see above for description)
36761 "beforeedit" : true,
36764 * Fires after a cell is edited. <br />
36765 * <ul style="padding:5px;padding-left:16px;">
36766 * <li>grid - This grid</li>
36767 * <li>record - The record being edited</li>
36768 * <li>field - The field name being edited</li>
36769 * <li>value - The value being set</li>
36770 * <li>originalValue - The original value for the field, before the edit.</li>
36771 * <li>row - The grid row index</li>
36772 * <li>column - The grid column index</li>
36774 * @param {Object} e An edit event (see above for description)
36776 "afteredit" : true,
36778 * @event validateedit
36779 * Fires after a cell is edited, but before the value is set in the record.
36780 * You can use this to modify the value being set in the field, Return false
36781 * to cancel the change. The edit event object has the following properties <br />
36782 * <ul style="padding:5px;padding-left:16px;">
36783 * <li>editor - This editor</li>
36784 * <li>grid - This grid</li>
36785 * <li>record - The record being edited</li>
36786 * <li>field - The field name being edited</li>
36787 * <li>value - The value being set</li>
36788 * <li>originalValue - The original value for the field, before the edit.</li>
36789 * <li>row - The grid row index</li>
36790 * <li>column - The grid column index</li>
36791 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36793 * @param {Object} e An edit event (see above for description)
36795 "validateedit" : true
36797 this.on("bodyscroll", this.stopEditing, this);
36798 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36801 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36803 * @cfg {Number} clicksToEdit
36804 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36811 trackMouseOver: false, // causes very odd FF errors
36813 onCellDblClick : function(g, row, col){
36814 this.startEditing(row, col);
36817 onEditComplete : function(ed, value, startValue){
36818 this.editing = false;
36819 this.activeEditor = null;
36820 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36822 var field = this.colModel.getDataIndex(ed.col);
36827 originalValue: startValue,
36834 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36837 if(String(value) !== String(startValue)){
36839 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36840 r.set(field, e.value);
36841 // if we are dealing with a combo box..
36842 // then we also set the 'name' colum to be the displayField
36843 if (ed.field.displayField && ed.field.name) {
36844 r.set(ed.field.name, ed.field.el.dom.value);
36847 delete e.cancel; //?? why!!!
36848 this.fireEvent("afteredit", e);
36851 this.fireEvent("afteredit", e); // always fire it!
36853 this.view.focusCell(ed.row, ed.col);
36857 * Starts editing the specified for the specified row/column
36858 * @param {Number} rowIndex
36859 * @param {Number} colIndex
36861 startEditing : function(row, col){
36862 this.stopEditing();
36863 if(this.colModel.isCellEditable(col, row)){
36864 this.view.ensureVisible(row, col, true);
36866 var r = this.dataSource.getAt(row);
36867 var field = this.colModel.getDataIndex(col);
36868 var cell = Roo.get(this.view.getCell(row,col));
36873 value: r.data[field],
36878 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36879 this.editing = true;
36880 var ed = this.colModel.getCellEditor(col, row);
36886 ed.render(ed.parentEl || document.body);
36892 (function(){ // complex but required for focus issues in safari, ie and opera
36896 ed.on("complete", this.onEditComplete, this, {single: true});
36897 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36898 this.activeEditor = ed;
36899 var v = r.data[field];
36900 ed.startEdit(this.view.getCell(row, col), v);
36901 // combo's with 'displayField and name set
36902 if (ed.field.displayField && ed.field.name) {
36903 ed.field.el.dom.value = r.data[ed.field.name];
36907 }).defer(50, this);
36913 * Stops any active editing
36915 stopEditing : function(){
36916 if(this.activeEditor){
36917 this.activeEditor.completeEdit();
36919 this.activeEditor = null;
36923 * Called to get grid's drag proxy text, by default returns this.ddText.
36926 getDragDropText : function(){
36927 var count = this.selModel.getSelectedCell() ? 1 : 0;
36928 return String.format(this.ddText, count, count == 1 ? '' : 's');
36933 * Ext JS Library 1.1.1
36934 * Copyright(c) 2006-2007, Ext JS, LLC.
36936 * Originally Released Under LGPL - original licence link has changed is not relivant.
36939 * <script type="text/javascript">
36942 // private - not really -- you end up using it !
36943 // This is a support class used internally by the Grid components
36946 * @class Roo.grid.GridEditor
36947 * @extends Roo.Editor
36948 * Class for creating and editable grid elements.
36949 * @param {Object} config any settings (must include field)
36951 Roo.grid.GridEditor = function(field, config){
36952 if (!config && field.field) {
36954 field = Roo.factory(config.field, Roo.form);
36956 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36957 field.monitorTab = false;
36960 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36963 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36966 alignment: "tl-tl",
36969 cls: "x-small-editor x-grid-editor",
36974 * Ext JS Library 1.1.1
36975 * Copyright(c) 2006-2007, Ext JS, LLC.
36977 * Originally Released Under LGPL - original licence link has changed is not relivant.
36980 * <script type="text/javascript">
36985 Roo.grid.PropertyRecord = Roo.data.Record.create([
36986 {name:'name',type:'string'}, 'value'
36990 Roo.grid.PropertyStore = function(grid, source){
36992 this.store = new Roo.data.Store({
36993 recordType : Roo.grid.PropertyRecord
36995 this.store.on('update', this.onUpdate, this);
36997 this.setSource(source);
36999 Roo.grid.PropertyStore.superclass.constructor.call(this);
37004 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37005 setSource : function(o){
37007 this.store.removeAll();
37010 if(this.isEditableValue(o[k])){
37011 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37014 this.store.loadRecords({records: data}, {}, true);
37017 onUpdate : function(ds, record, type){
37018 if(type == Roo.data.Record.EDIT){
37019 var v = record.data['value'];
37020 var oldValue = record.modified['value'];
37021 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37022 this.source[record.id] = v;
37024 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37031 getProperty : function(row){
37032 return this.store.getAt(row);
37035 isEditableValue: function(val){
37036 if(val && val instanceof Date){
37038 }else if(typeof val == 'object' || typeof val == 'function'){
37044 setValue : function(prop, value){
37045 this.source[prop] = value;
37046 this.store.getById(prop).set('value', value);
37049 getSource : function(){
37050 return this.source;
37054 Roo.grid.PropertyColumnModel = function(grid, store){
37057 g.PropertyColumnModel.superclass.constructor.call(this, [
37058 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37059 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37061 this.store = store;
37062 this.bselect = Roo.DomHelper.append(document.body, {
37063 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37064 {tag: 'option', value: 'true', html: 'true'},
37065 {tag: 'option', value: 'false', html: 'false'}
37068 Roo.id(this.bselect);
37071 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37072 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37073 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37074 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37075 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37077 this.renderCellDelegate = this.renderCell.createDelegate(this);
37078 this.renderPropDelegate = this.renderProp.createDelegate(this);
37081 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37085 valueText : 'Value',
37087 dateFormat : 'm/j/Y',
37090 renderDate : function(dateVal){
37091 return dateVal.dateFormat(this.dateFormat);
37094 renderBool : function(bVal){
37095 return bVal ? 'true' : 'false';
37098 isCellEditable : function(colIndex, rowIndex){
37099 return colIndex == 1;
37102 getRenderer : function(col){
37104 this.renderCellDelegate : this.renderPropDelegate;
37107 renderProp : function(v){
37108 return this.getPropertyName(v);
37111 renderCell : function(val){
37113 if(val instanceof Date){
37114 rv = this.renderDate(val);
37115 }else if(typeof val == 'boolean'){
37116 rv = this.renderBool(val);
37118 return Roo.util.Format.htmlEncode(rv);
37121 getPropertyName : function(name){
37122 var pn = this.grid.propertyNames;
37123 return pn && pn[name] ? pn[name] : name;
37126 getCellEditor : function(colIndex, rowIndex){
37127 var p = this.store.getProperty(rowIndex);
37128 var n = p.data['name'], val = p.data['value'];
37130 if(typeof(this.grid.customEditors[n]) == 'string'){
37131 return this.editors[this.grid.customEditors[n]];
37133 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37134 return this.grid.customEditors[n];
37136 if(val instanceof Date){
37137 return this.editors['date'];
37138 }else if(typeof val == 'number'){
37139 return this.editors['number'];
37140 }else if(typeof val == 'boolean'){
37141 return this.editors['boolean'];
37143 return this.editors['string'];
37149 * @class Roo.grid.PropertyGrid
37150 * @extends Roo.grid.EditorGrid
37151 * This class represents the interface of a component based property grid control.
37152 * <br><br>Usage:<pre><code>
37153 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37161 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37162 * The container MUST have some type of size defined for the grid to fill. The container will be
37163 * automatically set to position relative if it isn't already.
37164 * @param {Object} config A config object that sets properties on this grid.
37166 Roo.grid.PropertyGrid = function(container, config){
37167 config = config || {};
37168 var store = new Roo.grid.PropertyStore(this);
37169 this.store = store;
37170 var cm = new Roo.grid.PropertyColumnModel(this, store);
37171 store.store.sort('name', 'ASC');
37172 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37175 enableColLock:false,
37176 enableColumnMove:false,
37178 trackMouseOver: false,
37181 this.getGridEl().addClass('x-props-grid');
37182 this.lastEditRow = null;
37183 this.on('columnresize', this.onColumnResize, this);
37186 * @event beforepropertychange
37187 * Fires before a property changes (return false to stop?)
37188 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37189 * @param {String} id Record Id
37190 * @param {String} newval New Value
37191 * @param {String} oldval Old Value
37193 "beforepropertychange": true,
37195 * @event propertychange
37196 * Fires after a property changes
37197 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37198 * @param {String} id Record Id
37199 * @param {String} newval New Value
37200 * @param {String} oldval Old Value
37202 "propertychange": true
37204 this.customEditors = this.customEditors || {};
37206 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37209 * @cfg {Object} customEditors map of colnames=> custom editors.
37210 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37211 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37212 * false disables editing of the field.
37216 * @cfg {Object} propertyNames map of property Names to their displayed value
37219 render : function(){
37220 Roo.grid.PropertyGrid.superclass.render.call(this);
37221 this.autoSize.defer(100, this);
37224 autoSize : function(){
37225 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37227 this.view.fitColumns();
37231 onColumnResize : function(){
37232 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37236 * Sets the data for the Grid
37237 * accepts a Key => Value object of all the elements avaiable.
37238 * @param {Object} data to appear in grid.
37240 setSource : function(source){
37241 this.store.setSource(source);
37245 * Gets all the data from the grid.
37246 * @return {Object} data data stored in grid
37248 getSource : function(){
37249 return this.store.getSource();
37258 * @class Roo.grid.Calendar
37259 * @extends Roo.util.Grid
37260 * This class extends the Grid to provide a calendar widget
37261 * <br><br>Usage:<pre><code>
37262 var grid = new Roo.grid.Calendar("my-container-id", {
37265 selModel: mySelectionModel,
37266 autoSizeColumns: true,
37267 monitorWindowResize: false,
37268 trackMouseOver: true
37269 eventstore : real data store..
37275 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37276 * The container MUST have some type of size defined for the grid to fill. The container will be
37277 * automatically set to position relative if it isn't already.
37278 * @param {Object} config A config object that sets properties on this grid.
37280 Roo.grid.Calendar = function(container, config){
37281 // initialize the container
37282 this.container = Roo.get(container);
37283 this.container.update("");
37284 this.container.setStyle("overflow", "hidden");
37285 this.container.addClass('x-grid-container');
37287 this.id = this.container.id;
37289 Roo.apply(this, config);
37290 // check and correct shorthanded configs
37294 for (var r = 0;r < 6;r++) {
37297 for (var c =0;c < 7;c++) {
37301 if (this.eventStore) {
37302 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37303 this.eventStore.on('load',this.onLoad, this);
37304 this.eventStore.on('beforeload',this.clearEvents, this);
37308 this.dataSource = new Roo.data.Store({
37309 proxy: new Roo.data.MemoryProxy(rows),
37310 reader: new Roo.data.ArrayReader({}, [
37311 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37314 this.dataSource.load();
37315 this.ds = this.dataSource;
37316 this.ds.xmodule = this.xmodule || false;
37319 var cellRender = function(v,x,r)
37321 return String.format(
37322 '<div class="fc-day fc-widget-content"><div>' +
37323 '<div class="fc-event-container"></div>' +
37324 '<div class="fc-day-number">{0}</div>'+
37326 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37327 '</div></div>', v);
37332 this.colModel = new Roo.grid.ColumnModel( [
37334 xtype: 'ColumnModel',
37336 dataIndex : 'weekday0',
37338 renderer : cellRender
37341 xtype: 'ColumnModel',
37343 dataIndex : 'weekday1',
37345 renderer : cellRender
37348 xtype: 'ColumnModel',
37350 dataIndex : 'weekday2',
37351 header : 'Tuesday',
37352 renderer : cellRender
37355 xtype: 'ColumnModel',
37357 dataIndex : 'weekday3',
37358 header : 'Wednesday',
37359 renderer : cellRender
37362 xtype: 'ColumnModel',
37364 dataIndex : 'weekday4',
37365 header : 'Thursday',
37366 renderer : cellRender
37369 xtype: 'ColumnModel',
37371 dataIndex : 'weekday5',
37373 renderer : cellRender
37376 xtype: 'ColumnModel',
37378 dataIndex : 'weekday6',
37379 header : 'Saturday',
37380 renderer : cellRender
37383 this.cm = this.colModel;
37384 this.cm.xmodule = this.xmodule || false;
37388 //this.selModel = new Roo.grid.CellSelectionModel();
37389 //this.sm = this.selModel;
37390 //this.selModel.init(this);
37394 this.container.setWidth(this.width);
37398 this.container.setHeight(this.height);
37405 * The raw click event for the entire grid.
37406 * @param {Roo.EventObject} e
37411 * The raw dblclick event for the entire grid.
37412 * @param {Roo.EventObject} e
37416 * @event contextmenu
37417 * The raw contextmenu event for the entire grid.
37418 * @param {Roo.EventObject} e
37420 "contextmenu" : true,
37423 * The raw mousedown event for the entire grid.
37424 * @param {Roo.EventObject} e
37426 "mousedown" : true,
37429 * The raw mouseup event for the entire grid.
37430 * @param {Roo.EventObject} e
37435 * The raw mouseover event for the entire grid.
37436 * @param {Roo.EventObject} e
37438 "mouseover" : true,
37441 * The raw mouseout event for the entire grid.
37442 * @param {Roo.EventObject} e
37447 * The raw keypress event for the entire grid.
37448 * @param {Roo.EventObject} e
37453 * The raw keydown event for the entire grid.
37454 * @param {Roo.EventObject} e
37462 * Fires when a cell is clicked
37463 * @param {Grid} this
37464 * @param {Number} rowIndex
37465 * @param {Number} columnIndex
37466 * @param {Roo.EventObject} e
37468 "cellclick" : true,
37470 * @event celldblclick
37471 * Fires when a cell is double clicked
37472 * @param {Grid} this
37473 * @param {Number} rowIndex
37474 * @param {Number} columnIndex
37475 * @param {Roo.EventObject} e
37477 "celldblclick" : true,
37480 * Fires when a row is clicked
37481 * @param {Grid} this
37482 * @param {Number} rowIndex
37483 * @param {Roo.EventObject} e
37487 * @event rowdblclick
37488 * Fires when a row is double clicked
37489 * @param {Grid} this
37490 * @param {Number} rowIndex
37491 * @param {Roo.EventObject} e
37493 "rowdblclick" : true,
37495 * @event headerclick
37496 * Fires when a header is clicked
37497 * @param {Grid} this
37498 * @param {Number} columnIndex
37499 * @param {Roo.EventObject} e
37501 "headerclick" : true,
37503 * @event headerdblclick
37504 * Fires when a header cell is double clicked
37505 * @param {Grid} this
37506 * @param {Number} columnIndex
37507 * @param {Roo.EventObject} e
37509 "headerdblclick" : true,
37511 * @event rowcontextmenu
37512 * Fires when a row is right clicked
37513 * @param {Grid} this
37514 * @param {Number} rowIndex
37515 * @param {Roo.EventObject} e
37517 "rowcontextmenu" : true,
37519 * @event cellcontextmenu
37520 * Fires when a cell is right clicked
37521 * @param {Grid} this
37522 * @param {Number} rowIndex
37523 * @param {Number} cellIndex
37524 * @param {Roo.EventObject} e
37526 "cellcontextmenu" : true,
37528 * @event headercontextmenu
37529 * Fires when a header is right clicked
37530 * @param {Grid} this
37531 * @param {Number} columnIndex
37532 * @param {Roo.EventObject} e
37534 "headercontextmenu" : true,
37536 * @event bodyscroll
37537 * Fires when the body element is scrolled
37538 * @param {Number} scrollLeft
37539 * @param {Number} scrollTop
37541 "bodyscroll" : true,
37543 * @event columnresize
37544 * Fires when the user resizes a column
37545 * @param {Number} columnIndex
37546 * @param {Number} newSize
37548 "columnresize" : true,
37550 * @event columnmove
37551 * Fires when the user moves a column
37552 * @param {Number} oldIndex
37553 * @param {Number} newIndex
37555 "columnmove" : true,
37558 * Fires when row(s) start being dragged
37559 * @param {Grid} this
37560 * @param {Roo.GridDD} dd The drag drop object
37561 * @param {event} e The raw browser event
37563 "startdrag" : true,
37566 * Fires when a drag operation is complete
37567 * @param {Grid} this
37568 * @param {Roo.GridDD} dd The drag drop object
37569 * @param {event} e The raw browser event
37574 * Fires when dragged row(s) are dropped on a valid DD target
37575 * @param {Grid} this
37576 * @param {Roo.GridDD} dd The drag drop object
37577 * @param {String} targetId The target drag drop object
37578 * @param {event} e The raw browser event
37583 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37584 * @param {Grid} this
37585 * @param {Roo.GridDD} dd The drag drop object
37586 * @param {String} targetId The target drag drop object
37587 * @param {event} e The raw browser event
37592 * Fires when the dragged row(s) first cross another DD target while being dragged
37593 * @param {Grid} this
37594 * @param {Roo.GridDD} dd The drag drop object
37595 * @param {String} targetId The target drag drop object
37596 * @param {event} e The raw browser event
37598 "dragenter" : true,
37601 * Fires when the dragged row(s) leave another DD target while being dragged
37602 * @param {Grid} this
37603 * @param {Roo.GridDD} dd The drag drop object
37604 * @param {String} targetId The target drag drop object
37605 * @param {event} e The raw browser event
37610 * Fires when a row is rendered, so you can change add a style to it.
37611 * @param {GridView} gridview The grid view
37612 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37618 * Fires when the grid is rendered
37619 * @param {Grid} grid
37624 * Fires when a date is selected
37625 * @param {DatePicker} this
37626 * @param {Date} date The selected date
37630 * @event monthchange
37631 * Fires when the displayed month changes
37632 * @param {DatePicker} this
37633 * @param {Date} date The selected month
37635 'monthchange': true,
37637 * @event evententer
37638 * Fires when mouse over an event
37639 * @param {Calendar} this
37640 * @param {event} Event
37642 'evententer': true,
37644 * @event eventleave
37645 * Fires when the mouse leaves an
37646 * @param {Calendar} this
37649 'eventleave': true,
37651 * @event eventclick
37652 * Fires when the mouse click an
37653 * @param {Calendar} this
37656 'eventclick': true,
37658 * @event eventrender
37659 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37660 * @param {Calendar} this
37661 * @param {data} data to be modified
37663 'eventrender': true
37667 Roo.grid.Grid.superclass.constructor.call(this);
37668 this.on('render', function() {
37669 this.view.el.addClass('x-grid-cal');
37671 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37675 if (!Roo.grid.Calendar.style) {
37676 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37679 '.x-grid-cal .x-grid-col' : {
37680 height: 'auto !important',
37681 'vertical-align': 'top'
37683 '.x-grid-cal .fc-event-hori' : {
37694 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37696 * @cfg {Store} eventStore The store that loads events.
37701 activeDate : false,
37704 monitorWindowResize : false,
37707 resizeColumns : function() {
37708 var col = (this.view.el.getWidth() / 7) - 3;
37709 // loop through cols, and setWidth
37710 for(var i =0 ; i < 7 ; i++){
37711 this.cm.setColumnWidth(i, col);
37714 setDate :function(date) {
37716 Roo.log('setDate?');
37718 this.resizeColumns();
37719 var vd = this.activeDate;
37720 this.activeDate = date;
37721 // if(vd && this.el){
37722 // var t = date.getTime();
37723 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37724 // Roo.log('using add remove');
37726 // this.fireEvent('monthchange', this, date);
37728 // this.cells.removeClass("fc-state-highlight");
37729 // this.cells.each(function(c){
37730 // if(c.dateValue == t){
37731 // c.addClass("fc-state-highlight");
37732 // setTimeout(function(){
37733 // try{c.dom.firstChild.focus();}catch(e){}
37743 var days = date.getDaysInMonth();
37745 var firstOfMonth = date.getFirstDateOfMonth();
37746 var startingPos = firstOfMonth.getDay()-this.startDay;
37748 if(startingPos < this.startDay){
37752 var pm = date.add(Date.MONTH, -1);
37753 var prevStart = pm.getDaysInMonth()-startingPos;
37757 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37759 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37760 //this.cells.addClassOnOver('fc-state-hover');
37762 var cells = this.cells.elements;
37763 var textEls = this.textNodes;
37765 //Roo.each(cells, function(cell){
37766 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37769 days += startingPos;
37771 // convert everything to numbers so it's fast
37772 var day = 86400000;
37773 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37776 //Roo.log(prevStart);
37778 var today = new Date().clearTime().getTime();
37779 var sel = date.clearTime().getTime();
37780 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37781 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37782 var ddMatch = this.disabledDatesRE;
37783 var ddText = this.disabledDatesText;
37784 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37785 var ddaysText = this.disabledDaysText;
37786 var format = this.format;
37788 var setCellClass = function(cal, cell){
37790 //Roo.log('set Cell Class');
37792 var t = d.getTime();
37797 cell.dateValue = t;
37799 cell.className += " fc-today";
37800 cell.className += " fc-state-highlight";
37801 cell.title = cal.todayText;
37804 // disable highlight in other month..
37805 cell.className += " fc-state-highlight";
37810 //cell.className = " fc-state-disabled";
37811 cell.title = cal.minText;
37815 //cell.className = " fc-state-disabled";
37816 cell.title = cal.maxText;
37820 if(ddays.indexOf(d.getDay()) != -1){
37821 // cell.title = ddaysText;
37822 // cell.className = " fc-state-disabled";
37825 if(ddMatch && format){
37826 var fvalue = d.dateFormat(format);
37827 if(ddMatch.test(fvalue)){
37828 cell.title = ddText.replace("%0", fvalue);
37829 cell.className = " fc-state-disabled";
37833 if (!cell.initialClassName) {
37834 cell.initialClassName = cell.dom.className;
37837 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37842 for(; i < startingPos; i++) {
37843 cells[i].dayName = (++prevStart);
37844 Roo.log(textEls[i]);
37845 d.setDate(d.getDate()+1);
37847 //cells[i].className = "fc-past fc-other-month";
37848 setCellClass(this, cells[i]);
37853 for(; i < days; i++){
37854 intDay = i - startingPos + 1;
37855 cells[i].dayName = (intDay);
37856 d.setDate(d.getDate()+1);
37858 cells[i].className = ''; // "x-date-active";
37859 setCellClass(this, cells[i]);
37863 for(; i < 42; i++) {
37864 //textEls[i].innerHTML = (++extraDays);
37866 d.setDate(d.getDate()+1);
37867 cells[i].dayName = (++extraDays);
37868 cells[i].className = "fc-future fc-other-month";
37869 setCellClass(this, cells[i]);
37872 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37874 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37876 // this will cause all the cells to mis
37879 for (var r = 0;r < 6;r++) {
37880 for (var c =0;c < 7;c++) {
37881 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37885 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37886 for(i=0;i<cells.length;i++) {
37888 this.cells.elements[i].dayName = cells[i].dayName ;
37889 this.cells.elements[i].className = cells[i].className;
37890 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37891 this.cells.elements[i].title = cells[i].title ;
37892 this.cells.elements[i].dateValue = cells[i].dateValue ;
37898 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37899 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37901 ////if(totalRows != 6){
37902 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37903 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37906 this.fireEvent('monthchange', this, date);
37911 * Returns the grid's SelectionModel.
37912 * @return {SelectionModel}
37914 getSelectionModel : function(){
37915 if(!this.selModel){
37916 this.selModel = new Roo.grid.CellSelectionModel();
37918 return this.selModel;
37922 this.eventStore.load()
37928 findCell : function(dt) {
37929 dt = dt.clearTime().getTime();
37931 this.cells.each(function(c){
37932 //Roo.log("check " +c.dateValue + '?=' + dt);
37933 if(c.dateValue == dt){
37943 findCells : function(rec) {
37944 var s = rec.data.start_dt.clone().clearTime().getTime();
37946 var e= rec.data.end_dt.clone().clearTime().getTime();
37949 this.cells.each(function(c){
37950 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37952 if(c.dateValue > e){
37955 if(c.dateValue < s){
37964 findBestRow: function(cells)
37968 for (var i =0 ; i < cells.length;i++) {
37969 ret = Math.max(cells[i].rows || 0,ret);
37976 addItem : function(rec)
37978 // look for vertical location slot in
37979 var cells = this.findCells(rec);
37981 rec.row = this.findBestRow(cells);
37983 // work out the location.
37987 for(var i =0; i < cells.length; i++) {
37995 if (crow.start.getY() == cells[i].getY()) {
37997 crow.end = cells[i];
38013 for (var i = 0; i < cells.length;i++) {
38014 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38021 clearEvents: function() {
38023 if (!this.eventStore.getCount()) {
38026 // reset number of rows in cells.
38027 Roo.each(this.cells.elements, function(c){
38031 this.eventStore.each(function(e) {
38032 this.clearEvent(e);
38037 clearEvent : function(ev)
38040 Roo.each(ev.els, function(el) {
38041 el.un('mouseenter' ,this.onEventEnter, this);
38042 el.un('mouseleave' ,this.onEventLeave, this);
38050 renderEvent : function(ev,ctr) {
38052 ctr = this.view.el.select('.fc-event-container',true).first();
38056 this.clearEvent(ev);
38062 var cells = ev.cells;
38063 var rows = ev.rows;
38064 this.fireEvent('eventrender', this, ev);
38066 for(var i =0; i < rows.length; i++) {
38070 cls += ' fc-event-start';
38072 if ((i+1) == rows.length) {
38073 cls += ' fc-event-end';
38076 //Roo.log(ev.data);
38077 // how many rows should it span..
38078 var cg = this.eventTmpl.append(ctr,Roo.apply({
38081 }, ev.data) , true);
38084 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38085 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38086 cg.on('click', this.onEventClick, this, ev);
38090 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38091 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38094 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38095 cg.setWidth(ebox.right - sbox.x -2);
38099 renderEvents: function()
38101 // first make sure there is enough space..
38103 if (!this.eventTmpl) {
38104 this.eventTmpl = new Roo.Template(
38105 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38106 '<div class="fc-event-inner">' +
38107 '<span class="fc-event-time">{time}</span>' +
38108 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38110 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38118 this.cells.each(function(c) {
38119 //Roo.log(c.select('.fc-day-content div',true).first());
38120 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38123 var ctr = this.view.el.select('.fc-event-container',true).first();
38126 this.eventStore.each(function(ev){
38128 this.renderEvent(ev);
38132 this.view.layout();
38136 onEventEnter: function (e, el,event,d) {
38137 this.fireEvent('evententer', this, el, event);
38140 onEventLeave: function (e, el,event,d) {
38141 this.fireEvent('eventleave', this, el, event);
38144 onEventClick: function (e, el,event,d) {
38145 this.fireEvent('eventclick', this, el, event);
38148 onMonthChange: function () {
38152 onLoad: function () {
38154 //Roo.log('calendar onload');
38156 if(this.eventStore.getCount() > 0){
38160 this.eventStore.each(function(d){
38165 if (typeof(add.end_dt) == 'undefined') {
38166 Roo.log("Missing End time in calendar data: ");
38170 if (typeof(add.start_dt) == 'undefined') {
38171 Roo.log("Missing Start time in calendar data: ");
38175 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38176 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38177 add.id = add.id || d.id;
38178 add.title = add.title || '??';
38186 this.renderEvents();
38196 render : function ()
38200 if (!this.view.el.hasClass('course-timesheet')) {
38201 this.view.el.addClass('course-timesheet');
38203 if (this.tsStyle) {
38208 Roo.log(_this.grid.view.el.getWidth());
38211 this.tsStyle = Roo.util.CSS.createStyleSheet({
38212 '.course-timesheet .x-grid-row' : {
38215 '.x-grid-row td' : {
38216 'vertical-align' : 0
38218 '.course-edit-link' : {
38220 'text-overflow' : 'ellipsis',
38221 'overflow' : 'hidden',
38222 'white-space' : 'nowrap',
38223 'cursor' : 'pointer'
38228 '.de-act-sup-link' : {
38229 'color' : 'purple',
38230 'text-decoration' : 'line-through'
38234 'text-decoration' : 'line-through'
38236 '.course-timesheet .course-highlight' : {
38237 'border-top-style': 'dashed !important',
38238 'border-bottom-bottom': 'dashed !important'
38240 '.course-timesheet .course-item' : {
38241 'font-family' : 'tahoma, arial, helvetica',
38242 'font-size' : '11px',
38243 'overflow' : 'hidden',
38244 'padding-left' : '10px',
38245 'padding-right' : '10px',
38246 'padding-top' : '10px'
38254 monitorWindowResize : false,
38255 cellrenderer : function(v,x,r)
38260 xtype: 'CellSelectionModel',
38267 beforeload : function (_self, options)
38269 options.params = options.params || {};
38270 options.params._month = _this.monthField.getValue();
38271 options.params.limit = 9999;
38272 options.params['sort'] = 'when_dt';
38273 options.params['dir'] = 'ASC';
38274 this.proxy.loadResponse = this.loadResponse;
38276 //this.addColumns();
38278 load : function (_self, records, options)
38280 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38281 // if you click on the translation.. you can edit it...
38282 var el = Roo.get(this);
38283 var id = el.dom.getAttribute('data-id');
38284 var d = el.dom.getAttribute('data-date');
38285 var t = el.dom.getAttribute('data-time');
38286 //var id = this.child('span').dom.textContent;
38289 Pman.Dialog.CourseCalendar.show({
38293 productitem_active : id ? 1 : 0
38295 _this.grid.ds.load({});
38300 _this.panel.fireEvent('resize', [ '', '' ]);
38303 loadResponse : function(o, success, response){
38304 // this is overridden on before load..
38306 Roo.log("our code?");
38307 //Roo.log(success);
38308 //Roo.log(response)
38309 delete this.activeRequest;
38311 this.fireEvent("loadexception", this, o, response);
38312 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38317 result = o.reader.read(response);
38319 Roo.log("load exception?");
38320 this.fireEvent("loadexception", this, o, response, e);
38321 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38324 Roo.log("ready...");
38325 // loop through result.records;
38326 // and set this.tdate[date] = [] << array of records..
38328 Roo.each(result.records, function(r){
38330 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38331 _this.tdata[r.data.when_dt.format('j')] = [];
38333 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38336 //Roo.log(_this.tdata);
38338 result.records = [];
38339 result.totalRecords = 6;
38341 // let's generate some duumy records for the rows.
38342 //var st = _this.dateField.getValue();
38344 // work out monday..
38345 //st = st.add(Date.DAY, -1 * st.format('w'));
38347 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38349 var firstOfMonth = date.getFirstDayOfMonth();
38350 var days = date.getDaysInMonth();
38352 var firstAdded = false;
38353 for (var i = 0; i < result.totalRecords ; i++) {
38354 //var d= st.add(Date.DAY, i);
38357 for(var w = 0 ; w < 7 ; w++){
38358 if(!firstAdded && firstOfMonth != w){
38365 var dd = (d > 0 && d < 10) ? "0"+d : d;
38366 row['weekday'+w] = String.format(
38367 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38368 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38370 date.format('Y-m-')+dd
38373 if(typeof(_this.tdata[d]) != 'undefined'){
38374 Roo.each(_this.tdata[d], function(r){
38378 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38379 if(r.parent_id*1>0){
38380 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38383 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38384 deactive = 'de-act-link';
38387 row['weekday'+w] += String.format(
38388 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38390 r.product_id_name, //1
38391 r.when_dt.format('h:ia'), //2
38401 // only do this if something added..
38403 result.records.push(_this.grid.dataSource.reader.newRow(row));
38407 // push it twice. (second one with an hour..
38411 this.fireEvent("load", this, o, o.request.arg);
38412 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38414 sortInfo : {field: 'when_dt', direction : 'ASC' },
38416 xtype: 'HttpProxy',
38419 url : baseURL + '/Roo/Shop_course.php'
38422 xtype: 'JsonReader',
38439 'name': 'parent_id',
38443 'name': 'product_id',
38447 'name': 'productitem_id',
38465 click : function (_self, e)
38467 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38468 sd.setMonth(sd.getMonth()-1);
38469 _this.monthField.setValue(sd.format('Y-m-d'));
38470 _this.grid.ds.load({});
38476 xtype: 'Separator',
38480 xtype: 'MonthField',
38483 render : function (_self)
38485 _this.monthField = _self;
38486 // _this.monthField.set today
38488 select : function (combo, date)
38490 _this.grid.ds.load({});
38493 value : (function() { return new Date(); })()
38496 xtype: 'Separator',
38502 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38512 click : function (_self, e)
38514 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38515 sd.setMonth(sd.getMonth()+1);
38516 _this.monthField.setValue(sd.format('Y-m-d'));
38517 _this.grid.ds.load({});
38530 * Ext JS Library 1.1.1
38531 * Copyright(c) 2006-2007, Ext JS, LLC.
38533 * Originally Released Under LGPL - original licence link has changed is not relivant.
38536 * <script type="text/javascript">
38540 * @class Roo.LoadMask
38541 * A simple utility class for generically masking elements while loading data. If the element being masked has
38542 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38543 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38544 * element's UpdateManager load indicator and will be destroyed after the initial load.
38546 * Create a new LoadMask
38547 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38548 * @param {Object} config The config object
38550 Roo.LoadMask = function(el, config){
38551 this.el = Roo.get(el);
38552 Roo.apply(this, config);
38554 this.store.on('beforeload', this.onBeforeLoad, this);
38555 this.store.on('load', this.onLoad, this);
38556 this.store.on('loadexception', this.onLoadException, this);
38557 this.removeMask = false;
38559 var um = this.el.getUpdateManager();
38560 um.showLoadIndicator = false; // disable the default indicator
38561 um.on('beforeupdate', this.onBeforeLoad, this);
38562 um.on('update', this.onLoad, this);
38563 um.on('failure', this.onLoad, this);
38564 this.removeMask = true;
38568 Roo.LoadMask.prototype = {
38570 * @cfg {Boolean} removeMask
38571 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38572 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38575 * @cfg {String} msg
38576 * The text to display in a centered loading message box (defaults to 'Loading...')
38578 msg : 'Loading...',
38580 * @cfg {String} msgCls
38581 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38583 msgCls : 'x-mask-loading',
38586 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38592 * Disables the mask to prevent it from being displayed
38594 disable : function(){
38595 this.disabled = true;
38599 * Enables the mask so that it can be displayed
38601 enable : function(){
38602 this.disabled = false;
38605 onLoadException : function()
38607 Roo.log(arguments);
38609 if (typeof(arguments[3]) != 'undefined') {
38610 Roo.MessageBox.alert("Error loading",arguments[3]);
38614 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38615 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38622 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38625 onLoad : function()
38627 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38631 onBeforeLoad : function(){
38632 if(!this.disabled){
38633 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38638 destroy : function(){
38640 this.store.un('beforeload', this.onBeforeLoad, this);
38641 this.store.un('load', this.onLoad, this);
38642 this.store.un('loadexception', this.onLoadException, this);
38644 var um = this.el.getUpdateManager();
38645 um.un('beforeupdate', this.onBeforeLoad, this);
38646 um.un('update', this.onLoad, this);
38647 um.un('failure', this.onLoad, this);
38652 * Ext JS Library 1.1.1
38653 * Copyright(c) 2006-2007, Ext JS, LLC.
38655 * Originally Released Under LGPL - original licence link has changed is not relivant.
38658 * <script type="text/javascript">
38663 * @class Roo.XTemplate
38664 * @extends Roo.Template
38665 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38667 var t = new Roo.XTemplate(
38668 '<select name="{name}">',
38669 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38673 // then append, applying the master template values
38676 * Supported features:
38681 {a_variable} - output encoded.
38682 {a_variable.format:("Y-m-d")} - call a method on the variable
38683 {a_variable:raw} - unencoded output
38684 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38685 {a_variable:this.method_on_template(...)} - call a method on the template object.
38690 <tpl for="a_variable or condition.."></tpl>
38691 <tpl if="a_variable or condition"></tpl>
38692 <tpl exec="some javascript"></tpl>
38693 <tpl name="named_template"></tpl> (experimental)
38695 <tpl for="."></tpl> - just iterate the property..
38696 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38700 Roo.XTemplate = function()
38702 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38709 Roo.extend(Roo.XTemplate, Roo.Template, {
38712 * The various sub templates
38717 * basic tag replacing syntax
38720 * // you can fake an object call by doing this
38724 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38727 * compile the template
38729 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38732 compile: function()
38736 s = ['<tpl>', s, '</tpl>'].join('');
38738 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38739 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38740 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38741 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38742 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38747 while(true == !!(m = s.match(re))){
38748 var forMatch = m[0].match(nameRe),
38749 ifMatch = m[0].match(ifRe),
38750 execMatch = m[0].match(execRe),
38751 namedMatch = m[0].match(namedRe),
38756 name = forMatch && forMatch[1] ? forMatch[1] : '';
38759 // if - puts fn into test..
38760 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38762 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38767 // exec - calls a function... returns empty if true is returned.
38768 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38770 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38778 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38779 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38780 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38783 var uid = namedMatch ? namedMatch[1] : id;
38787 id: namedMatch ? namedMatch[1] : id,
38794 s = s.replace(m[0], '');
38796 s = s.replace(m[0], '{xtpl'+ id + '}');
38801 for(var i = tpls.length-1; i >= 0; --i){
38802 this.compileTpl(tpls[i]);
38803 this.tpls[tpls[i].id] = tpls[i];
38805 this.master = tpls[tpls.length-1];
38809 * same as applyTemplate, except it's done to one of the subTemplates
38810 * when using named templates, you can do:
38812 * var str = pl.applySubTemplate('your-name', values);
38815 * @param {Number} id of the template
38816 * @param {Object} values to apply to template
38817 * @param {Object} parent (normaly the instance of this object)
38819 applySubTemplate : function(id, values, parent)
38823 var t = this.tpls[id];
38827 if(t.test && !t.test.call(this, values, parent)){
38831 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38832 Roo.log(e.toString());
38838 if(t.exec && t.exec.call(this, values, parent)){
38842 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38843 Roo.log(e.toString());
38848 var vs = t.target ? t.target.call(this, values, parent) : values;
38849 parent = t.target ? values : parent;
38850 if(t.target && vs instanceof Array){
38852 for(var i = 0, len = vs.length; i < len; i++){
38853 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38855 return buf.join('');
38857 return t.compiled.call(this, vs, parent);
38859 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38860 Roo.log(e.toString());
38861 Roo.log(t.compiled);
38866 compileTpl : function(tpl)
38868 var fm = Roo.util.Format;
38869 var useF = this.disableFormats !== true;
38870 var sep = Roo.isGecko ? "+" : ",";
38871 var undef = function(str) {
38872 Roo.log("Property not found :" + str);
38876 var fn = function(m, name, format, args)
38878 //Roo.log(arguments);
38879 args = args ? args.replace(/\\'/g,"'") : args;
38880 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38881 if (typeof(format) == 'undefined') {
38882 format= 'htmlEncode';
38884 if (format == 'raw' ) {
38888 if(name.substr(0, 4) == 'xtpl'){
38889 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38892 // build an array of options to determine if value is undefined..
38894 // basically get 'xxxx.yyyy' then do
38895 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38896 // (function () { Roo.log("Property not found"); return ''; })() :
38901 Roo.each(name.split('.'), function(st) {
38902 lookfor += (lookfor.length ? '.': '') + st;
38903 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38906 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38909 if(format && useF){
38911 args = args ? ',' + args : "";
38913 if(format.substr(0, 5) != "this."){
38914 format = "fm." + format + '(';
38916 format = 'this.call("'+ format.substr(5) + '", ';
38920 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38924 // called with xxyx.yuu:(test,test)
38926 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38928 // raw.. - :raw modifier..
38929 return "'"+ sep + udef_st + name + ")"+sep+"'";
38933 // branched to use + in gecko and [].join() in others
38935 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38936 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38939 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38940 body.push(tpl.body.replace(/(\r\n|\n)/g,
38941 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38942 body.push("'].join('');};};");
38943 body = body.join('');
38946 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38948 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38954 applyTemplate : function(values){
38955 return this.master.compiled.call(this, values, {});
38956 //var s = this.subs;
38959 apply : function(){
38960 return this.applyTemplate.apply(this, arguments);
38965 Roo.XTemplate.from = function(el){
38966 el = Roo.getDom(el);
38967 return new Roo.XTemplate(el.value || el.innerHTML);