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, {
2053 * This method is only used by a DataProxy which has retrieved data from a remote server.
2054 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2055 * to contain a method called 'responseXML' that returns an XML document object.
2056 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2057 * a cache of Roo.data.Records.
2059 read : function(response){
2060 var doc = response.responseXML;
2062 throw {message: "XmlReader.read: XML Document not available"};
2064 return this.readRecords(doc);
2068 * Create a data block containing Roo.data.Records from an XML document.
2069 * @param {Object} doc A parsed XML document.
2070 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2071 * a cache of Roo.data.Records.
2073 readRecords : function(doc){
2075 * After any data loads/reads, the raw XML Document is available for further custom processing.
2079 var root = doc.documentElement || doc;
2080 var q = Roo.DomQuery;
2081 var recordType = this.recordType, fields = recordType.prototype.fields;
2082 var sid = this.meta.id;
2083 var totalRecords = 0, success = true;
2084 if(this.meta.totalRecords){
2085 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2088 if(this.meta.success){
2089 var sv = q.selectValue(this.meta.success, root, true);
2090 success = sv !== false && sv !== 'false';
2093 var ns = q.select(this.meta.record, root);
2094 for(var i = 0, len = ns.length; i < len; i++) {
2097 var id = sid ? q.selectValue(sid, n) : undefined;
2098 for(var j = 0, jlen = fields.length; j < jlen; j++){
2099 var f = fields.items[j];
2100 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2104 var record = new recordType(values, id);
2106 records[records.length] = record;
2112 totalRecords : totalRecords || records.length
2117 * Ext JS Library 1.1.1
2118 * Copyright(c) 2006-2007, Ext JS, LLC.
2120 * Originally Released Under LGPL - original licence link has changed is not relivant.
2123 * <script type="text/javascript">
2127 * @class Roo.data.ArrayReader
2128 * @extends Roo.data.DataReader
2129 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2130 * Each element of that Array represents a row of data fields. The
2131 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2132 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2136 var RecordDef = Roo.data.Record.create([
2137 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2138 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2140 var myReader = new Roo.data.ArrayReader({
2141 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2145 * This would consume an Array like this:
2147 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2151 * Create a new JsonReader
2152 * @param {Object} meta Metadata configuration options.
2153 * @param {Object|Array} recordType Either an Array of field definition objects
2155 * @cfg {Array} fields Array of field definition objects
2156 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2157 * as specified to {@link Roo.data.Record#create},
2158 * or an {@link Roo.data.Record} object
2161 * created using {@link Roo.data.Record#create}.
2163 Roo.data.ArrayReader = function(meta, recordType)
2165 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2168 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2170 * Create a data block containing Roo.data.Records from an XML document.
2171 * @param {Object} o An Array of row objects which represents the dataset.
2172 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2173 * a cache of Roo.data.Records.
2175 readRecords : function(o)
2177 var sid = this.meta ? this.meta.id : null;
2178 var recordType = this.recordType, fields = recordType.prototype.fields;
2181 for(var i = 0; i < root.length; i++){
2184 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2185 for(var j = 0, jlen = fields.length; j < jlen; j++){
2186 var f = fields.items[j];
2187 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2188 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2192 var record = new recordType(values, id);
2194 records[records.length] = record;
2198 totalRecords : records.length
2203 * Ext JS Library 1.1.1
2204 * Copyright(c) 2006-2007, Ext JS, LLC.
2206 * Originally Released Under LGPL - original licence link has changed is not relivant.
2209 * <script type="text/javascript">
2214 * @class Roo.data.Tree
2215 * @extends Roo.util.Observable
2216 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2217 * in the tree have most standard DOM functionality.
2219 * @param {Node} root (optional) The root node
2221 Roo.data.Tree = function(root){
2224 * The root node for this tree
2229 this.setRootNode(root);
2234 * Fires when a new child node is appended to a node in this tree.
2235 * @param {Tree} tree The owner tree
2236 * @param {Node} parent The parent node
2237 * @param {Node} node The newly appended node
2238 * @param {Number} index The index of the newly appended node
2243 * Fires when a child node is removed from a node in this tree.
2244 * @param {Tree} tree The owner tree
2245 * @param {Node} parent The parent node
2246 * @param {Node} node The child node removed
2251 * Fires when a node is moved to a new location in the tree
2252 * @param {Tree} tree The owner tree
2253 * @param {Node} node The node moved
2254 * @param {Node} oldParent The old parent of this node
2255 * @param {Node} newParent The new parent of this node
2256 * @param {Number} index The index it was moved to
2261 * Fires when a new child node is inserted in a node in this tree.
2262 * @param {Tree} tree The owner tree
2263 * @param {Node} parent The parent node
2264 * @param {Node} node The child node inserted
2265 * @param {Node} refNode The child node the node was inserted before
2269 * @event beforeappend
2270 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2271 * @param {Tree} tree The owner tree
2272 * @param {Node} parent The parent node
2273 * @param {Node} node The child node to be appended
2275 "beforeappend" : true,
2277 * @event beforeremove
2278 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2279 * @param {Tree} tree The owner tree
2280 * @param {Node} parent The parent node
2281 * @param {Node} node The child node to be removed
2283 "beforeremove" : true,
2286 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2287 * @param {Tree} tree The owner tree
2288 * @param {Node} node The node being moved
2289 * @param {Node} oldParent The parent of the node
2290 * @param {Node} newParent The new parent the node is moving to
2291 * @param {Number} index The index it is being moved to
2293 "beforemove" : true,
2295 * @event beforeinsert
2296 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2297 * @param {Tree} tree The owner tree
2298 * @param {Node} parent The parent node
2299 * @param {Node} node The child node to be inserted
2300 * @param {Node} refNode The child node the node is being inserted before
2302 "beforeinsert" : true
2305 Roo.data.Tree.superclass.constructor.call(this);
2308 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2311 proxyNodeEvent : function(){
2312 return this.fireEvent.apply(this, arguments);
2316 * Returns the root node for this tree.
2319 getRootNode : function(){
2324 * Sets the root node for this tree.
2325 * @param {Node} node
2328 setRootNode : function(node){
2330 node.ownerTree = this;
2332 this.registerNode(node);
2337 * Gets a node in this tree by its id.
2338 * @param {String} id
2341 getNodeById : function(id){
2342 return this.nodeHash[id];
2345 registerNode : function(node){
2346 this.nodeHash[node.id] = node;
2349 unregisterNode : function(node){
2350 delete this.nodeHash[node.id];
2353 toString : function(){
2354 return "[Tree"+(this.id?" "+this.id:"")+"]";
2359 * @class Roo.data.Node
2360 * @extends Roo.util.Observable
2361 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2362 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2364 * @param {Object} attributes The attributes/config for the node
2366 Roo.data.Node = function(attributes){
2368 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2371 this.attributes = attributes || {};
2372 this.leaf = this.attributes.leaf;
2374 * The node id. @type String
2376 this.id = this.attributes.id;
2378 this.id = Roo.id(null, "ynode-");
2379 this.attributes.id = this.id;
2384 * All child nodes of this node. @type Array
2386 this.childNodes = [];
2387 if(!this.childNodes.indexOf){ // indexOf is a must
2388 this.childNodes.indexOf = function(o){
2389 for(var i = 0, len = this.length; i < len; i++){
2398 * The parent node for this node. @type Node
2400 this.parentNode = null;
2402 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2404 this.firstChild = null;
2406 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2408 this.lastChild = null;
2410 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2412 this.previousSibling = null;
2414 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2416 this.nextSibling = null;
2421 * Fires when a new child node is appended
2422 * @param {Tree} tree The owner tree
2423 * @param {Node} this This node
2424 * @param {Node} node The newly appended node
2425 * @param {Number} index The index of the newly appended node
2430 * Fires when a child node is removed
2431 * @param {Tree} tree The owner tree
2432 * @param {Node} this This node
2433 * @param {Node} node The removed node
2438 * Fires when this node is moved to a new location in the tree
2439 * @param {Tree} tree The owner tree
2440 * @param {Node} this This node
2441 * @param {Node} oldParent The old parent of this node
2442 * @param {Node} newParent The new parent of this node
2443 * @param {Number} index The index it was moved to
2448 * Fires when a new child node is inserted.
2449 * @param {Tree} tree The owner tree
2450 * @param {Node} this This node
2451 * @param {Node} node The child node inserted
2452 * @param {Node} refNode The child node the node was inserted before
2456 * @event beforeappend
2457 * Fires before a new child is appended, return false to cancel the append.
2458 * @param {Tree} tree The owner tree
2459 * @param {Node} this This node
2460 * @param {Node} node The child node to be appended
2462 "beforeappend" : true,
2464 * @event beforeremove
2465 * Fires before a child is removed, return false to cancel the remove.
2466 * @param {Tree} tree The owner tree
2467 * @param {Node} this This node
2468 * @param {Node} node The child node to be removed
2470 "beforeremove" : true,
2473 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2474 * @param {Tree} tree The owner tree
2475 * @param {Node} this This node
2476 * @param {Node} oldParent The parent of this node
2477 * @param {Node} newParent The new parent this node is moving to
2478 * @param {Number} index The index it is being moved to
2480 "beforemove" : true,
2482 * @event beforeinsert
2483 * Fires before a new child is inserted, return false to cancel the insert.
2484 * @param {Tree} tree The owner tree
2485 * @param {Node} this This node
2486 * @param {Node} node The child node to be inserted
2487 * @param {Node} refNode The child node the node is being inserted before
2489 "beforeinsert" : true
2491 this.listeners = this.attributes.listeners;
2492 Roo.data.Node.superclass.constructor.call(this);
2495 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2496 fireEvent : function(evtName){
2497 // first do standard event for this node
2498 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2501 // then bubble it up to the tree if the event wasn't cancelled
2502 var ot = this.getOwnerTree();
2504 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2512 * Returns true if this node is a leaf
2515 isLeaf : function(){
2516 return this.leaf === true;
2520 setFirstChild : function(node){
2521 this.firstChild = node;
2525 setLastChild : function(node){
2526 this.lastChild = node;
2531 * Returns true if this node is the last child of its parent
2534 isLast : function(){
2535 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2539 * Returns true if this node is the first child of its parent
2542 isFirst : function(){
2543 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2546 hasChildNodes : function(){
2547 return !this.isLeaf() && this.childNodes.length > 0;
2551 * Insert node(s) as the last child node of this node.
2552 * @param {Node/Array} node The node or Array of nodes to append
2553 * @return {Node} The appended node if single append, or null if an array was passed
2555 appendChild : function(node){
2557 if(node instanceof Array){
2559 }else if(arguments.length > 1){
2563 // if passed an array or multiple args do them one by one
2565 for(var i = 0, len = multi.length; i < len; i++) {
2566 this.appendChild(multi[i]);
2569 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2572 var index = this.childNodes.length;
2573 var oldParent = node.parentNode;
2574 // it's a move, make sure we move it cleanly
2576 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2579 oldParent.removeChild(node);
2582 index = this.childNodes.length;
2584 this.setFirstChild(node);
2586 this.childNodes.push(node);
2587 node.parentNode = this;
2588 var ps = this.childNodes[index-1];
2590 node.previousSibling = ps;
2591 ps.nextSibling = node;
2593 node.previousSibling = null;
2595 node.nextSibling = null;
2596 this.setLastChild(node);
2597 node.setOwnerTree(this.getOwnerTree());
2598 this.fireEvent("append", this.ownerTree, this, node, index);
2599 if(this.ownerTree) {
2600 this.ownerTree.fireEvent("appendnode", this, node, index);
2603 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2610 * Removes a child node from this node.
2611 * @param {Node} node The node to remove
2612 * @return {Node} The removed node
2614 removeChild : function(node){
2615 var index = this.childNodes.indexOf(node);
2619 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2623 // remove it from childNodes collection
2624 this.childNodes.splice(index, 1);
2627 if(node.previousSibling){
2628 node.previousSibling.nextSibling = node.nextSibling;
2630 if(node.nextSibling){
2631 node.nextSibling.previousSibling = node.previousSibling;
2634 // update child refs
2635 if(this.firstChild == node){
2636 this.setFirstChild(node.nextSibling);
2638 if(this.lastChild == node){
2639 this.setLastChild(node.previousSibling);
2642 node.setOwnerTree(null);
2643 // clear any references from the node
2644 node.parentNode = null;
2645 node.previousSibling = null;
2646 node.nextSibling = null;
2647 this.fireEvent("remove", this.ownerTree, this, node);
2652 * Inserts the first node before the second node in this nodes childNodes collection.
2653 * @param {Node} node The node to insert
2654 * @param {Node} refNode The node to insert before (if null the node is appended)
2655 * @return {Node} The inserted node
2657 insertBefore : function(node, refNode){
2658 if(!refNode){ // like standard Dom, refNode can be null for append
2659 return this.appendChild(node);
2662 if(node == refNode){
2666 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2669 var index = this.childNodes.indexOf(refNode);
2670 var oldParent = node.parentNode;
2671 var refIndex = index;
2673 // when moving internally, indexes will change after remove
2674 if(oldParent == this && this.childNodes.indexOf(node) < index){
2678 // it's a move, make sure we move it cleanly
2680 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2683 oldParent.removeChild(node);
2686 this.setFirstChild(node);
2688 this.childNodes.splice(refIndex, 0, node);
2689 node.parentNode = this;
2690 var ps = this.childNodes[refIndex-1];
2692 node.previousSibling = ps;
2693 ps.nextSibling = node;
2695 node.previousSibling = null;
2697 node.nextSibling = refNode;
2698 refNode.previousSibling = node;
2699 node.setOwnerTree(this.getOwnerTree());
2700 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2702 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2708 * Returns the child node at the specified index.
2709 * @param {Number} index
2712 item : function(index){
2713 return this.childNodes[index];
2717 * Replaces one child node in this node with another.
2718 * @param {Node} newChild The replacement node
2719 * @param {Node} oldChild The node to replace
2720 * @return {Node} The replaced node
2722 replaceChild : function(newChild, oldChild){
2723 this.insertBefore(newChild, oldChild);
2724 this.removeChild(oldChild);
2729 * Returns the index of a child node
2730 * @param {Node} node
2731 * @return {Number} The index of the node or -1 if it was not found
2733 indexOf : function(child){
2734 return this.childNodes.indexOf(child);
2738 * Returns the tree this node is in.
2741 getOwnerTree : function(){
2742 // if it doesn't have one, look for one
2743 if(!this.ownerTree){
2747 this.ownerTree = p.ownerTree;
2753 return this.ownerTree;
2757 * Returns depth of this node (the root node has a depth of 0)
2760 getDepth : function(){
2763 while(p.parentNode){
2771 setOwnerTree : function(tree){
2772 // if it's move, we need to update everyone
2773 if(tree != this.ownerTree){
2775 this.ownerTree.unregisterNode(this);
2777 this.ownerTree = tree;
2778 var cs = this.childNodes;
2779 for(var i = 0, len = cs.length; i < len; i++) {
2780 cs[i].setOwnerTree(tree);
2783 tree.registerNode(this);
2789 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2790 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2791 * @return {String} The path
2793 getPath : function(attr){
2794 attr = attr || "id";
2795 var p = this.parentNode;
2796 var b = [this.attributes[attr]];
2798 b.unshift(p.attributes[attr]);
2801 var sep = this.getOwnerTree().pathSeparator;
2802 return sep + b.join(sep);
2806 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2807 * function call will be the scope provided or the current node. The arguments to the function
2808 * will be the args provided or the current node. If the function returns false at any point,
2809 * the bubble is stopped.
2810 * @param {Function} fn The function to call
2811 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2812 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2814 bubble : function(fn, scope, args){
2817 if(fn.call(scope || p, args || p) === false){
2825 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2826 * function call will be the scope provided or the current node. The arguments to the function
2827 * will be the args provided or the current node. If the function returns false at any point,
2828 * the cascade is stopped on that branch.
2829 * @param {Function} fn The function to call
2830 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2831 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2833 cascade : function(fn, scope, args){
2834 if(fn.call(scope || this, args || this) !== false){
2835 var cs = this.childNodes;
2836 for(var i = 0, len = cs.length; i < len; i++) {
2837 cs[i].cascade(fn, scope, args);
2843 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2844 * function call will be the scope provided or the current node. The arguments to the function
2845 * will be the args provided or the current node. If the function returns false at any point,
2846 * the iteration stops.
2847 * @param {Function} fn The function to call
2848 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2849 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2851 eachChild : function(fn, scope, args){
2852 var cs = this.childNodes;
2853 for(var i = 0, len = cs.length; i < len; i++) {
2854 if(fn.call(scope || this, args || cs[i]) === false){
2861 * Finds the first child that has the attribute with the specified value.
2862 * @param {String} attribute The attribute name
2863 * @param {Mixed} value The value to search for
2864 * @return {Node} The found child or null if none was found
2866 findChild : function(attribute, value){
2867 var cs = this.childNodes;
2868 for(var i = 0, len = cs.length; i < len; i++) {
2869 if(cs[i].attributes[attribute] == value){
2877 * Finds the first child by a custom function. The child matches if the function passed
2879 * @param {Function} fn
2880 * @param {Object} scope (optional)
2881 * @return {Node} The found child or null if none was found
2883 findChildBy : function(fn, scope){
2884 var cs = this.childNodes;
2885 for(var i = 0, len = cs.length; i < len; i++) {
2886 if(fn.call(scope||cs[i], cs[i]) === true){
2894 * Sorts this nodes children using the supplied sort function
2895 * @param {Function} fn
2896 * @param {Object} scope (optional)
2898 sort : function(fn, scope){
2899 var cs = this.childNodes;
2900 var len = cs.length;
2902 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2904 for(var i = 0; i < len; i++){
2906 n.previousSibling = cs[i-1];
2907 n.nextSibling = cs[i+1];
2909 this.setFirstChild(n);
2912 this.setLastChild(n);
2919 * Returns true if this node is an ancestor (at any point) of the passed node.
2920 * @param {Node} node
2923 contains : function(node){
2924 return node.isAncestor(this);
2928 * Returns true if the passed node is an ancestor (at any point) of this node.
2929 * @param {Node} node
2932 isAncestor : function(node){
2933 var p = this.parentNode;
2943 toString : function(){
2944 return "[Node"+(this.id?" "+this.id:"")+"]";
2948 * Ext JS Library 1.1.1
2949 * Copyright(c) 2006-2007, Ext JS, LLC.
2951 * Originally Released Under LGPL - original licence link has changed is not relivant.
2954 * <script type="text/javascript">
2959 * @extends Roo.Element
2960 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2961 * automatic maintaining of shadow/shim positions.
2962 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2963 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2964 * you can pass a string with a CSS class name. False turns off the shadow.
2965 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2966 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2967 * @cfg {String} cls CSS class to add to the element
2968 * @cfg {Number} zindex Starting z-index (defaults to 11000)
2969 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2971 * @param {Object} config An object with config options.
2972 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2975 Roo.Layer = function(config, existingEl){
2976 config = config || {};
2977 var dh = Roo.DomHelper;
2978 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2980 this.dom = Roo.getDom(existingEl);
2983 var o = config.dh || {tag: "div", cls: "x-layer"};
2984 this.dom = dh.append(pel, o);
2987 this.addClass(config.cls);
2989 this.constrain = config.constrain !== false;
2990 this.visibilityMode = Roo.Element.VISIBILITY;
2992 this.id = this.dom.id = config.id;
2994 this.id = Roo.id(this.dom);
2996 this.zindex = config.zindex || this.getZIndex();
2997 this.position("absolute", this.zindex);
2999 this.shadowOffset = config.shadowOffset || 4;
3000 this.shadow = new Roo.Shadow({
3001 offset : this.shadowOffset,
3002 mode : config.shadow
3005 this.shadowOffset = 0;
3007 this.useShim = config.shim !== false && Roo.useShims;
3008 this.useDisplay = config.useDisplay;
3012 var supr = Roo.Element.prototype;
3014 // shims are shared among layer to keep from having 100 iframes
3017 Roo.extend(Roo.Layer, Roo.Element, {
3019 getZIndex : function(){
3020 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3023 getShim : function(){
3030 var shim = shims.shift();
3032 shim = this.createShim();
3033 shim.enableDisplayMode('block');
3034 shim.dom.style.display = 'none';
3035 shim.dom.style.visibility = 'visible';
3037 var pn = this.dom.parentNode;
3038 if(shim.dom.parentNode != pn){
3039 pn.insertBefore(shim.dom, this.dom);
3041 shim.setStyle('z-index', this.getZIndex()-2);
3046 hideShim : function(){
3048 this.shim.setDisplayed(false);
3049 shims.push(this.shim);
3054 disableShadow : function(){
3056 this.shadowDisabled = true;
3058 this.lastShadowOffset = this.shadowOffset;
3059 this.shadowOffset = 0;
3063 enableShadow : function(show){
3065 this.shadowDisabled = false;
3066 this.shadowOffset = this.lastShadowOffset;
3067 delete this.lastShadowOffset;
3075 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3076 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3077 sync : function(doShow){
3078 var sw = this.shadow;
3079 if(!this.updating && this.isVisible() && (sw || this.useShim)){
3080 var sh = this.getShim();
3082 var w = this.getWidth(),
3083 h = this.getHeight();
3085 var l = this.getLeft(true),
3086 t = this.getTop(true);
3088 if(sw && !this.shadowDisabled){
3089 if(doShow && !sw.isVisible()){
3092 sw.realign(l, t, w, h);
3098 // fit the shim behind the shadow, so it is shimmed too
3099 var a = sw.adjusts, s = sh.dom.style;
3100 s.left = (Math.min(l, l+a.l))+"px";
3101 s.top = (Math.min(t, t+a.t))+"px";
3102 s.width = (w+a.w)+"px";
3103 s.height = (h+a.h)+"px";
3110 sh.setLeftTop(l, t);
3117 destroy : function(){
3122 this.removeAllListeners();
3123 var pn = this.dom.parentNode;
3125 pn.removeChild(this.dom);
3127 Roo.Element.uncache(this.id);
3130 remove : function(){
3135 beginUpdate : function(){
3136 this.updating = true;
3140 endUpdate : function(){
3141 this.updating = false;
3146 hideUnders : function(negOffset){
3154 constrainXY : function(){
3156 var vw = Roo.lib.Dom.getViewWidth(),
3157 vh = Roo.lib.Dom.getViewHeight();
3158 var s = Roo.get(document).getScroll();
3160 var xy = this.getXY();
3161 var x = xy[0], y = xy[1];
3162 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3163 // only move it if it needs it
3165 // first validate right/bottom
3166 if((x + w) > vw+s.left){
3167 x = vw - w - this.shadowOffset;
3170 if((y + h) > vh+s.top){
3171 y = vh - h - this.shadowOffset;
3174 // then make sure top/left isn't negative
3185 var ay = this.avoidY;
3186 if(y <= ay && (y+h) >= ay){
3192 supr.setXY.call(this, xy);
3198 isVisible : function(){
3199 return this.visible;
3203 showAction : function(){
3204 this.visible = true; // track visibility to prevent getStyle calls
3205 if(this.useDisplay === true){
3206 this.setDisplayed("");
3207 }else if(this.lastXY){
3208 supr.setXY.call(this, this.lastXY);
3209 }else if(this.lastLT){
3210 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3215 hideAction : function(){
3216 this.visible = false;
3217 if(this.useDisplay === true){
3218 this.setDisplayed(false);
3220 this.setLeftTop(-10000,-10000);
3224 // overridden Element method
3225 setVisible : function(v, a, d, c, e){
3230 var cb = function(){
3235 }.createDelegate(this);
3236 supr.setVisible.call(this, true, true, d, cb, e);
3239 this.hideUnders(true);
3248 }.createDelegate(this);
3250 supr.setVisible.call(this, v, a, d, cb, e);
3259 storeXY : function(xy){
3264 storeLeftTop : function(left, top){
3266 this.lastLT = [left, top];
3270 beforeFx : function(){
3271 this.beforeAction();
3272 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3276 afterFx : function(){
3277 Roo.Layer.superclass.afterFx.apply(this, arguments);
3278 this.sync(this.isVisible());
3282 beforeAction : function(){
3283 if(!this.updating && this.shadow){
3288 // overridden Element method
3289 setLeft : function(left){
3290 this.storeLeftTop(left, this.getTop(true));
3291 supr.setLeft.apply(this, arguments);
3295 setTop : function(top){
3296 this.storeLeftTop(this.getLeft(true), top);
3297 supr.setTop.apply(this, arguments);
3301 setLeftTop : function(left, top){
3302 this.storeLeftTop(left, top);
3303 supr.setLeftTop.apply(this, arguments);
3307 setXY : function(xy, a, d, c, e){
3309 this.beforeAction();
3311 var cb = this.createCB(c);
3312 supr.setXY.call(this, xy, a, d, cb, e);
3319 createCB : function(c){
3330 // overridden Element method
3331 setX : function(x, a, d, c, e){
3332 this.setXY([x, this.getY()], a, d, c, e);
3335 // overridden Element method
3336 setY : function(y, a, d, c, e){
3337 this.setXY([this.getX(), y], a, d, c, e);
3340 // overridden Element method
3341 setSize : function(w, h, a, d, c, e){
3342 this.beforeAction();
3343 var cb = this.createCB(c);
3344 supr.setSize.call(this, w, h, a, d, cb, e);
3350 // overridden Element method
3351 setWidth : function(w, a, d, c, e){
3352 this.beforeAction();
3353 var cb = this.createCB(c);
3354 supr.setWidth.call(this, w, a, d, cb, e);
3360 // overridden Element method
3361 setHeight : function(h, a, d, c, e){
3362 this.beforeAction();
3363 var cb = this.createCB(c);
3364 supr.setHeight.call(this, h, a, d, cb, e);
3370 // overridden Element method
3371 setBounds : function(x, y, w, h, a, d, c, e){
3372 this.beforeAction();
3373 var cb = this.createCB(c);
3375 this.storeXY([x, y]);
3376 supr.setXY.call(this, [x, y]);
3377 supr.setSize.call(this, w, h, a, d, cb, e);
3380 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3386 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3387 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3388 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3389 * @param {Number} zindex The new z-index to set
3390 * @return {this} The Layer
3392 setZIndex : function(zindex){
3393 this.zindex = zindex;
3394 this.setStyle("z-index", zindex + 2);
3396 this.shadow.setZIndex(zindex + 1);
3399 this.shim.setStyle("z-index", zindex);
3405 * Ext JS Library 1.1.1
3406 * Copyright(c) 2006-2007, Ext JS, LLC.
3408 * Originally Released Under LGPL - original licence link has changed is not relivant.
3411 * <script type="text/javascript">
3417 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3418 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3419 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3421 * Create a new Shadow
3422 * @param {Object} config The config object
3424 Roo.Shadow = function(config){
3425 Roo.apply(this, config);
3426 if(typeof this.mode != "string"){
3427 this.mode = this.defaultMode;
3429 var o = this.offset, a = {h: 0};
3430 var rad = Math.floor(this.offset/2);
3431 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3437 a.l -= this.offset + rad;
3438 a.t -= this.offset + rad;
3449 a.l -= (this.offset - rad);
3450 a.t -= this.offset + rad;
3452 a.w -= (this.offset - rad)*2;
3463 a.l -= (this.offset - rad);
3464 a.t -= (this.offset - rad);
3466 a.w -= (this.offset + rad + 1);
3467 a.h -= (this.offset + rad);
3476 Roo.Shadow.prototype = {
3478 * @cfg {String} mode
3479 * The shadow display mode. Supports the following options:<br />
3480 * sides: Shadow displays on both sides and bottom only<br />
3481 * frame: Shadow displays equally on all four sides<br />
3482 * drop: Traditional bottom-right drop shadow (default)
3485 * @cfg {String} offset
3486 * The number of pixels to offset the shadow from the element (defaults to 4)
3491 defaultMode: "drop",
3494 * Displays the shadow under the target element
3495 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3497 show : function(target){
3498 target = Roo.get(target);
3500 this.el = Roo.Shadow.Pool.pull();
3501 if(this.el.dom.nextSibling != target.dom){
3502 this.el.insertBefore(target);
3505 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3507 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3510 target.getLeft(true),
3511 target.getTop(true),
3515 this.el.dom.style.display = "block";
3519 * Returns true if the shadow is visible, else false
3521 isVisible : function(){
3522 return this.el ? true : false;
3526 * Direct alignment when values are already available. Show must be called at least once before
3527 * calling this method to ensure it is initialized.
3528 * @param {Number} left The target element left position
3529 * @param {Number} top The target element top position
3530 * @param {Number} width The target element width
3531 * @param {Number} height The target element height
3533 realign : function(l, t, w, h){
3537 var a = this.adjusts, d = this.el.dom, s = d.style;
3539 s.left = (l+a.l)+"px";
3540 s.top = (t+a.t)+"px";
3541 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3543 if(s.width != sws || s.height != shs){
3547 var cn = d.childNodes;
3548 var sww = Math.max(0, (sw-12))+"px";
3549 cn[0].childNodes[1].style.width = sww;
3550 cn[1].childNodes[1].style.width = sww;
3551 cn[2].childNodes[1].style.width = sww;
3552 cn[1].style.height = Math.max(0, (sh-12))+"px";
3562 this.el.dom.style.display = "none";
3563 Roo.Shadow.Pool.push(this.el);
3569 * Adjust the z-index of this shadow
3570 * @param {Number} zindex The new z-index
3572 setZIndex : function(z){
3575 this.el.setStyle("z-index", z);
3580 // Private utility class that manages the internal Shadow cache
3581 Roo.Shadow.Pool = function(){
3583 var markup = Roo.isIE ?
3584 '<div class="x-ie-shadow"></div>' :
3585 '<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>';
3590 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3591 sh.autoBoxAdjust = false;
3596 push : function(sh){
3602 * Ext JS Library 1.1.1
3603 * Copyright(c) 2006-2007, Ext JS, LLC.
3605 * Originally Released Under LGPL - original licence link has changed is not relivant.
3608 * <script type="text/javascript">
3613 * @class Roo.SplitBar
3614 * @extends Roo.util.Observable
3615 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3619 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3620 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3621 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3622 split.minSize = 100;
3623 split.maxSize = 600;
3624 split.animate = true;
3625 split.on('moved', splitterMoved);
3628 * Create a new SplitBar
3629 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3630 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3631 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3632 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3633 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3634 position of the SplitBar).
3636 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3639 this.el = Roo.get(dragElement, true);
3640 this.el.dom.unselectable = "on";
3642 this.resizingEl = Roo.get(resizingElement, true);
3646 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3647 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3650 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3653 * The minimum size of the resizing element. (Defaults to 0)
3659 * The maximum size of the resizing element. (Defaults to 2000)
3662 this.maxSize = 2000;
3665 * Whether to animate the transition to the new size
3668 this.animate = false;
3671 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3674 this.useShim = false;
3681 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3683 this.proxy = Roo.get(existingProxy).dom;
3686 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3689 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3692 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3695 this.dragSpecs = {};
3698 * @private The adapter to use to positon and resize elements
3700 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3701 this.adapter.init(this);
3703 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3705 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3706 this.el.addClass("x-splitbar-h");
3709 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3710 this.el.addClass("x-splitbar-v");
3716 * Fires when the splitter is moved (alias for {@link #event-moved})
3717 * @param {Roo.SplitBar} this
3718 * @param {Number} newSize the new width or height
3723 * Fires when the splitter is moved
3724 * @param {Roo.SplitBar} this
3725 * @param {Number} newSize the new width or height
3729 * @event beforeresize
3730 * Fires before the splitter is dragged
3731 * @param {Roo.SplitBar} this
3733 "beforeresize" : true,
3735 "beforeapply" : true
3738 Roo.util.Observable.call(this);
3741 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3742 onStartProxyDrag : function(x, y){
3743 this.fireEvent("beforeresize", this);
3745 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3747 o.enableDisplayMode("block");
3748 // all splitbars share the same overlay
3749 Roo.SplitBar.prototype.overlay = o;
3751 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3752 this.overlay.show();
3753 Roo.get(this.proxy).setDisplayed("block");
3754 var size = this.adapter.getElementSize(this);
3755 this.activeMinSize = this.getMinimumSize();;
3756 this.activeMaxSize = this.getMaximumSize();;
3757 var c1 = size - this.activeMinSize;
3758 var c2 = Math.max(this.activeMaxSize - size, 0);
3759 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3760 this.dd.resetConstraints();
3761 this.dd.setXConstraint(
3762 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3763 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3765 this.dd.setYConstraint(0, 0);
3767 this.dd.resetConstraints();
3768 this.dd.setXConstraint(0, 0);
3769 this.dd.setYConstraint(
3770 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3771 this.placement == Roo.SplitBar.TOP ? c2 : c1
3774 this.dragSpecs.startSize = size;
3775 this.dragSpecs.startPoint = [x, y];
3776 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3780 * @private Called after the drag operation by the DDProxy
3782 onEndProxyDrag : function(e){
3783 Roo.get(this.proxy).setDisplayed(false);
3784 var endPoint = Roo.lib.Event.getXY(e);
3786 this.overlay.hide();
3789 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3790 newSize = this.dragSpecs.startSize +
3791 (this.placement == Roo.SplitBar.LEFT ?
3792 endPoint[0] - this.dragSpecs.startPoint[0] :
3793 this.dragSpecs.startPoint[0] - endPoint[0]
3796 newSize = this.dragSpecs.startSize +
3797 (this.placement == Roo.SplitBar.TOP ?
3798 endPoint[1] - this.dragSpecs.startPoint[1] :
3799 this.dragSpecs.startPoint[1] - endPoint[1]
3802 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3803 if(newSize != this.dragSpecs.startSize){
3804 if(this.fireEvent('beforeapply', this, newSize) !== false){
3805 this.adapter.setElementSize(this, newSize);
3806 this.fireEvent("moved", this, newSize);
3807 this.fireEvent("resize", this, newSize);
3813 * Get the adapter this SplitBar uses
3814 * @return The adapter object
3816 getAdapter : function(){
3817 return this.adapter;
3821 * Set the adapter this SplitBar uses
3822 * @param {Object} adapter A SplitBar adapter object
3824 setAdapter : function(adapter){
3825 this.adapter = adapter;
3826 this.adapter.init(this);
3830 * Gets the minimum size for the resizing element
3831 * @return {Number} The minimum size
3833 getMinimumSize : function(){
3834 return this.minSize;
3838 * Sets the minimum size for the resizing element
3839 * @param {Number} minSize The minimum size
3841 setMinimumSize : function(minSize){
3842 this.minSize = minSize;
3846 * Gets the maximum size for the resizing element
3847 * @return {Number} The maximum size
3849 getMaximumSize : function(){
3850 return this.maxSize;
3854 * Sets the maximum size for the resizing element
3855 * @param {Number} maxSize The maximum size
3857 setMaximumSize : function(maxSize){
3858 this.maxSize = maxSize;
3862 * Sets the initialize size for the resizing element
3863 * @param {Number} size The initial size
3865 setCurrentSize : function(size){
3866 var oldAnimate = this.animate;
3867 this.animate = false;
3868 this.adapter.setElementSize(this, size);
3869 this.animate = oldAnimate;
3873 * Destroy this splitbar.
3874 * @param {Boolean} removeEl True to remove the element
3876 destroy : function(removeEl){
3881 this.proxy.parentNode.removeChild(this.proxy);
3889 * @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.
3891 Roo.SplitBar.createProxy = function(dir){
3892 var proxy = new Roo.Element(document.createElement("div"));
3893 proxy.unselectable();
3894 var cls = 'x-splitbar-proxy';
3895 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3896 document.body.appendChild(proxy.dom);
3901 * @class Roo.SplitBar.BasicLayoutAdapter
3902 * Default Adapter. It assumes the splitter and resizing element are not positioned
3903 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3905 Roo.SplitBar.BasicLayoutAdapter = function(){
3908 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3909 // do nothing for now
3914 * Called before drag operations to get the current size of the resizing element.
3915 * @param {Roo.SplitBar} s The SplitBar using this adapter
3917 getElementSize : function(s){
3918 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3919 return s.resizingEl.getWidth();
3921 return s.resizingEl.getHeight();
3926 * Called after drag operations to set the size of the resizing element.
3927 * @param {Roo.SplitBar} s The SplitBar using this adapter
3928 * @param {Number} newSize The new size to set
3929 * @param {Function} onComplete A function to be invoked when resizing is complete
3931 setElementSize : function(s, newSize, onComplete){
3932 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3934 s.resizingEl.setWidth(newSize);
3936 onComplete(s, newSize);
3939 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3944 s.resizingEl.setHeight(newSize);
3946 onComplete(s, newSize);
3949 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3956 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3957 * @extends Roo.SplitBar.BasicLayoutAdapter
3958 * Adapter that moves the splitter element to align with the resized sizing element.
3959 * Used with an absolute positioned SplitBar.
3960 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3961 * document.body, make sure you assign an id to the body element.
3963 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3964 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3965 this.container = Roo.get(container);
3968 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3973 getElementSize : function(s){
3974 return this.basic.getElementSize(s);
3977 setElementSize : function(s, newSize, onComplete){
3978 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3981 moveSplitter : function(s){
3982 var yes = Roo.SplitBar;
3983 switch(s.placement){
3985 s.el.setX(s.resizingEl.getRight());
3988 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3991 s.el.setY(s.resizingEl.getBottom());
3994 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4001 * Orientation constant - Create a vertical SplitBar
4005 Roo.SplitBar.VERTICAL = 1;
4008 * Orientation constant - Create a horizontal SplitBar
4012 Roo.SplitBar.HORIZONTAL = 2;
4015 * Placement constant - The resizing element is to the left of the splitter element
4019 Roo.SplitBar.LEFT = 1;
4022 * Placement constant - The resizing element is to the right of the splitter element
4026 Roo.SplitBar.RIGHT = 2;
4029 * Placement constant - The resizing element is positioned above the splitter element
4033 Roo.SplitBar.TOP = 3;
4036 * Placement constant - The resizing element is positioned under splitter element
4040 Roo.SplitBar.BOTTOM = 4;
4043 * Ext JS Library 1.1.1
4044 * Copyright(c) 2006-2007, Ext JS, LLC.
4046 * Originally Released Under LGPL - original licence link has changed is not relivant.
4049 * <script type="text/javascript">
4054 * @extends Roo.util.Observable
4055 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
4056 * This class also supports single and multi selection modes. <br>
4057 * Create a data model bound view:
4059 var store = new Roo.data.Store(...);
4061 var view = new Roo.View({
4063 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
4066 selectedClass: "ydataview-selected",
4070 // listen for node click?
4071 view.on("click", function(vw, index, node, e){
4072 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4076 dataModel.load("foobar.xml");
4078 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4080 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4081 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4083 * Note: old style constructor is still suported (container, template, config)
4087 * @param {Object} config The config object
4090 Roo.View = function(config, depreciated_tpl, depreciated_config){
4092 this.parent = false;
4094 if (typeof(depreciated_tpl) == 'undefined') {
4095 // new way.. - universal constructor.
4096 Roo.apply(this, config);
4097 this.el = Roo.get(this.el);
4100 this.el = Roo.get(config);
4101 this.tpl = depreciated_tpl;
4102 Roo.apply(this, depreciated_config);
4104 this.wrapEl = this.el.wrap().wrap();
4105 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4108 if(typeof(this.tpl) == "string"){
4109 this.tpl = new Roo.Template(this.tpl);
4111 // support xtype ctors..
4112 this.tpl = new Roo.factory(this.tpl, Roo);
4121 * @event beforeclick
4122 * Fires before a click is processed. Returns false to cancel the default action.
4123 * @param {Roo.View} this
4124 * @param {Number} index The index of the target node
4125 * @param {HTMLElement} node The target node
4126 * @param {Roo.EventObject} e The raw event object
4128 "beforeclick" : true,
4131 * Fires when a template node is clicked.
4132 * @param {Roo.View} this
4133 * @param {Number} index The index of the target node
4134 * @param {HTMLElement} node The target node
4135 * @param {Roo.EventObject} e The raw event object
4140 * Fires when a template node is double clicked.
4141 * @param {Roo.View} this
4142 * @param {Number} index The index of the target node
4143 * @param {HTMLElement} node The target node
4144 * @param {Roo.EventObject} e The raw event object
4148 * @event contextmenu
4149 * Fires when a template node is right clicked.
4150 * @param {Roo.View} this
4151 * @param {Number} index The index of the target node
4152 * @param {HTMLElement} node The target node
4153 * @param {Roo.EventObject} e The raw event object
4155 "contextmenu" : true,
4157 * @event selectionchange
4158 * Fires when the selected nodes change.
4159 * @param {Roo.View} this
4160 * @param {Array} selections Array of the selected nodes
4162 "selectionchange" : true,
4165 * @event beforeselect
4166 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4167 * @param {Roo.View} this
4168 * @param {HTMLElement} node The node to be selected
4169 * @param {Array} selections Array of currently selected nodes
4171 "beforeselect" : true,
4173 * @event preparedata
4174 * Fires on every row to render, to allow you to change the data.
4175 * @param {Roo.View} this
4176 * @param {Object} data to be rendered (change this)
4178 "preparedata" : true
4186 "click": this.onClick,
4187 "dblclick": this.onDblClick,
4188 "contextmenu": this.onContextMenu,
4192 this.selections = [];
4194 this.cmp = new Roo.CompositeElementLite([]);
4196 this.store = Roo.factory(this.store, Roo.data);
4197 this.setStore(this.store, true);
4200 if ( this.footer && this.footer.xtype) {
4202 var fctr = this.wrapEl.appendChild(document.createElement("div"));
4204 this.footer.dataSource = this.store;
4205 this.footer.container = fctr;
4206 this.footer = Roo.factory(this.footer, Roo);
4207 fctr.insertFirst(this.el);
4209 // this is a bit insane - as the paging toolbar seems to detach the el..
4210 // dom.parentNode.parentNode.parentNode
4211 // they get detached?
4215 Roo.View.superclass.constructor.call(this);
4220 Roo.extend(Roo.View, Roo.util.Observable, {
4223 * @cfg {Roo.data.Store} store Data store to load data from.
4228 * @cfg {String|Roo.Element} el The container element.
4233 * @cfg {String|Roo.Template} tpl The template used by this View
4237 * @cfg {String} dataName the named area of the template to use as the data area
4238 * Works with domtemplates roo-name="name"
4242 * @cfg {String} selectedClass The css class to add to selected nodes
4244 selectedClass : "x-view-selected",
4246 * @cfg {String} emptyText The empty text to show when nothing is loaded.
4251 * @cfg {String} text to display on mask (default Loading)
4255 * @cfg {Boolean} multiSelect Allow multiple selection
4257 multiSelect : false,
4259 * @cfg {Boolean} singleSelect Allow single selection
4261 singleSelect: false,
4264 * @cfg {Boolean} toggleSelect - selecting
4266 toggleSelect : false,
4269 * @cfg {Boolean} tickable - selecting
4274 * Returns the element this view is bound to.
4275 * @return {Roo.Element}
4284 * Refreshes the view. - called by datachanged on the store. - do not call directly.
4286 refresh : function(){
4287 //Roo.log('refresh');
4290 // if we are using something like 'domtemplate', then
4291 // the what gets used is:
4292 // t.applySubtemplate(NAME, data, wrapping data..)
4293 // the outer template then get' applied with
4294 // the store 'extra data'
4295 // and the body get's added to the
4296 // roo-name="data" node?
4297 // <span class='roo-tpl-{name}'></span> ?????
4301 this.clearSelections();
4304 var records = this.store.getRange();
4305 if(records.length < 1) {
4307 // is this valid?? = should it render a template??
4309 this.el.update(this.emptyText);
4313 if (this.dataName) {
4314 this.el.update(t.apply(this.store.meta)); //????
4315 el = this.el.child('.roo-tpl-' + this.dataName);
4318 for(var i = 0, len = records.length; i < len; i++){
4319 var data = this.prepareData(records[i].data, i, records[i]);
4320 this.fireEvent("preparedata", this, data, i, records[i]);
4322 var d = Roo.apply({}, data);
4325 Roo.apply(d, {'roo-id' : Roo.id()});
4329 Roo.each(this.parent.item, function(item){
4330 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4333 Roo.apply(d, {'roo-data-checked' : 'checked'});
4337 html[html.length] = Roo.util.Format.trim(
4339 t.applySubtemplate(this.dataName, d, this.store.meta) :
4346 el.update(html.join(""));
4347 this.nodes = el.dom.childNodes;
4348 this.updateIndexes(0);
4353 * Function to override to reformat the data that is sent to
4354 * the template for each node.
4355 * DEPRICATED - use the preparedata event handler.
4356 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4357 * a JSON object for an UpdateManager bound view).
4359 prepareData : function(data, index, record)
4361 this.fireEvent("preparedata", this, data, index, record);
4365 onUpdate : function(ds, record){
4366 // Roo.log('on update');
4367 this.clearSelections();
4368 var index = this.store.indexOf(record);
4369 var n = this.nodes[index];
4370 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4371 n.parentNode.removeChild(n);
4372 this.updateIndexes(index, index);
4378 onAdd : function(ds, records, index)
4380 //Roo.log(['on Add', ds, records, index] );
4381 this.clearSelections();
4382 if(this.nodes.length == 0){
4386 var n = this.nodes[index];
4387 for(var i = 0, len = records.length; i < len; i++){
4388 var d = this.prepareData(records[i].data, i, records[i]);
4390 this.tpl.insertBefore(n, d);
4393 this.tpl.append(this.el, d);
4396 this.updateIndexes(index);
4399 onRemove : function(ds, record, index){
4400 // Roo.log('onRemove');
4401 this.clearSelections();
4402 var el = this.dataName ?
4403 this.el.child('.roo-tpl-' + this.dataName) :
4406 el.dom.removeChild(this.nodes[index]);
4407 this.updateIndexes(index);
4411 * Refresh an individual node.
4412 * @param {Number} index
4414 refreshNode : function(index){
4415 this.onUpdate(this.store, this.store.getAt(index));
4418 updateIndexes : function(startIndex, endIndex){
4419 var ns = this.nodes;
4420 startIndex = startIndex || 0;
4421 endIndex = endIndex || ns.length - 1;
4422 for(var i = startIndex; i <= endIndex; i++){
4423 ns[i].nodeIndex = i;
4428 * Changes the data store this view uses and refresh the view.
4429 * @param {Store} store
4431 setStore : function(store, initial){
4432 if(!initial && this.store){
4433 this.store.un("datachanged", this.refresh);
4434 this.store.un("add", this.onAdd);
4435 this.store.un("remove", this.onRemove);
4436 this.store.un("update", this.onUpdate);
4437 this.store.un("clear", this.refresh);
4438 this.store.un("beforeload", this.onBeforeLoad);
4439 this.store.un("load", this.onLoad);
4440 this.store.un("loadexception", this.onLoad);
4444 store.on("datachanged", this.refresh, this);
4445 store.on("add", this.onAdd, this);
4446 store.on("remove", this.onRemove, this);
4447 store.on("update", this.onUpdate, this);
4448 store.on("clear", this.refresh, this);
4449 store.on("beforeload", this.onBeforeLoad, this);
4450 store.on("load", this.onLoad, this);
4451 store.on("loadexception", this.onLoad, this);
4459 * onbeforeLoad - masks the loading area.
4462 onBeforeLoad : function(store,opts)
4464 //Roo.log('onBeforeLoad');
4468 this.el.mask(this.mask ? this.mask : "Loading" );
4470 onLoad : function ()
4477 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4478 * @param {HTMLElement} node
4479 * @return {HTMLElement} The template node
4481 findItemFromChild : function(node){
4482 var el = this.dataName ?
4483 this.el.child('.roo-tpl-' + this.dataName,true) :
4486 if(!node || node.parentNode == el){
4489 var p = node.parentNode;
4490 while(p && p != el){
4491 if(p.parentNode == el){
4500 onClick : function(e){
4501 var item = this.findItemFromChild(e.getTarget());
4503 var index = this.indexOf(item);
4504 if(this.onItemClick(item, index, e) !== false){
4505 this.fireEvent("click", this, index, item, e);
4508 this.clearSelections();
4513 onContextMenu : function(e){
4514 var item = this.findItemFromChild(e.getTarget());
4516 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4521 onDblClick : function(e){
4522 var item = this.findItemFromChild(e.getTarget());
4524 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4528 onItemClick : function(item, index, e)
4530 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4533 if (this.toggleSelect) {
4534 var m = this.isSelected(item) ? 'unselect' : 'select';
4537 _t[m](item, true, false);
4540 if(this.multiSelect || this.singleSelect){
4541 if(this.multiSelect && e.shiftKey && this.lastSelection){
4542 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4544 this.select(item, this.multiSelect && e.ctrlKey);
4545 this.lastSelection = item;
4557 * Get the number of selected nodes.
4560 getSelectionCount : function(){
4561 return this.selections.length;
4565 * Get the currently selected nodes.
4566 * @return {Array} An array of HTMLElements
4568 getSelectedNodes : function(){
4569 return this.selections;
4573 * Get the indexes of the selected nodes.
4576 getSelectedIndexes : function(){
4577 var indexes = [], s = this.selections;
4578 for(var i = 0, len = s.length; i < len; i++){
4579 indexes.push(s[i].nodeIndex);
4585 * Clear all selections
4586 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4588 clearSelections : function(suppressEvent){
4589 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4590 this.cmp.elements = this.selections;
4591 this.cmp.removeClass(this.selectedClass);
4592 this.selections = [];
4594 this.fireEvent("selectionchange", this, this.selections);
4600 * Returns true if the passed node is selected
4601 * @param {HTMLElement/Number} node The node or node index
4604 isSelected : function(node){
4605 var s = this.selections;
4609 node = this.getNode(node);
4610 return s.indexOf(node) !== -1;
4615 * @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
4616 * @param {Boolean} keepExisting (optional) true to keep existing selections
4617 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4619 select : function(nodeInfo, keepExisting, suppressEvent){
4620 if(nodeInfo instanceof Array){
4622 this.clearSelections(true);
4624 for(var i = 0, len = nodeInfo.length; i < len; i++){
4625 this.select(nodeInfo[i], true, true);
4629 var node = this.getNode(nodeInfo);
4630 if(!node || this.isSelected(node)){
4631 return; // already selected.
4634 this.clearSelections(true);
4637 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4638 Roo.fly(node).addClass(this.selectedClass);
4639 this.selections.push(node);
4641 this.fireEvent("selectionchange", this, this.selections);
4649 * @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
4650 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4651 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4653 unselect : function(nodeInfo, keepExisting, suppressEvent)
4655 if(nodeInfo instanceof Array){
4656 Roo.each(this.selections, function(s) {
4657 this.unselect(s, nodeInfo);
4661 var node = this.getNode(nodeInfo);
4662 if(!node || !this.isSelected(node)){
4663 //Roo.log("not selected");
4664 return; // not selected.
4668 Roo.each(this.selections, function(s) {
4670 Roo.fly(node).removeClass(this.selectedClass);
4677 this.selections= ns;
4678 this.fireEvent("selectionchange", this, this.selections);
4682 * Gets a template node.
4683 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4684 * @return {HTMLElement} The node or null if it wasn't found
4686 getNode : function(nodeInfo){
4687 if(typeof nodeInfo == "string"){
4688 return document.getElementById(nodeInfo);
4689 }else if(typeof nodeInfo == "number"){
4690 return this.nodes[nodeInfo];
4696 * Gets a range template nodes.
4697 * @param {Number} startIndex
4698 * @param {Number} endIndex
4699 * @return {Array} An array of nodes
4701 getNodes : function(start, end){
4702 var ns = this.nodes;
4704 end = typeof end == "undefined" ? ns.length - 1 : end;
4707 for(var i = start; i <= end; i++){
4711 for(var i = start; i >= end; i--){
4719 * Finds the index of the passed node
4720 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4721 * @return {Number} The index of the node or -1
4723 indexOf : function(node){
4724 node = this.getNode(node);
4725 if(typeof node.nodeIndex == "number"){
4726 return node.nodeIndex;
4728 var ns = this.nodes;
4729 for(var i = 0, len = ns.length; i < len; i++){
4739 * Ext JS Library 1.1.1
4740 * Copyright(c) 2006-2007, Ext JS, LLC.
4742 * Originally Released Under LGPL - original licence link has changed is not relivant.
4745 * <script type="text/javascript">
4749 * @class Roo.JsonView
4751 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4753 var view = new Roo.JsonView({
4754 container: "my-element",
4755 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4760 // listen for node click?
4761 view.on("click", function(vw, index, node, e){
4762 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4765 // direct load of JSON data
4766 view.load("foobar.php");
4768 // Example from my blog list
4769 var tpl = new Roo.Template(
4770 '<div class="entry">' +
4771 '<a class="entry-title" href="{link}">{title}</a>' +
4772 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4773 "</div><hr />"
4776 var moreView = new Roo.JsonView({
4777 container : "entry-list",
4781 moreView.on("beforerender", this.sortEntries, this);
4783 url: "/blog/get-posts.php",
4784 params: "allposts=true",
4785 text: "Loading Blog Entries..."
4789 * Note: old code is supported with arguments : (container, template, config)
4793 * Create a new JsonView
4795 * @param {Object} config The config object
4798 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4801 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4803 var um = this.el.getUpdateManager();
4804 um.setRenderer(this);
4805 um.on("update", this.onLoad, this);
4806 um.on("failure", this.onLoadException, this);
4809 * @event beforerender
4810 * Fires before rendering of the downloaded JSON data.
4811 * @param {Roo.JsonView} this
4812 * @param {Object} data The JSON data loaded
4816 * Fires when data is loaded.
4817 * @param {Roo.JsonView} this
4818 * @param {Object} data The JSON data loaded
4819 * @param {Object} response The raw Connect response object
4822 * @event loadexception
4823 * Fires when loading fails.
4824 * @param {Roo.JsonView} this
4825 * @param {Object} response The raw Connect response object
4828 'beforerender' : true,
4830 'loadexception' : true
4833 Roo.extend(Roo.JsonView, Roo.View, {
4835 * @type {String} The root property in the loaded JSON object that contains the data
4840 * Refreshes the view.
4842 refresh : function(){
4843 this.clearSelections();
4846 var o = this.jsonData;
4847 if(o && o.length > 0){
4848 for(var i = 0, len = o.length; i < len; i++){
4849 var data = this.prepareData(o[i], i, o);
4850 html[html.length] = this.tpl.apply(data);
4853 html.push(this.emptyText);
4855 this.el.update(html.join(""));
4856 this.nodes = this.el.dom.childNodes;
4857 this.updateIndexes(0);
4861 * 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.
4862 * @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:
4865 url: "your-url.php",
4866 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4867 callback: yourFunction,
4868 scope: yourObject, //(optional scope)
4876 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4877 * 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.
4878 * @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}
4879 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4880 * @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.
4883 var um = this.el.getUpdateManager();
4884 um.update.apply(um, arguments);
4887 // note - render is a standard framework call...
4888 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4889 render : function(el, response){
4891 this.clearSelections();
4895 if (response != '') {
4896 o = Roo.util.JSON.decode(response.responseText);
4899 o = o[this.jsonRoot];
4905 * The current JSON data or null
4908 this.beforeRender();
4913 * Get the number of records in the current JSON dataset
4916 getCount : function(){
4917 return this.jsonData ? this.jsonData.length : 0;
4921 * Returns the JSON object for the specified node(s)
4922 * @param {HTMLElement/Array} node The node or an array of nodes
4923 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4924 * you get the JSON object for the node
4926 getNodeData : function(node){
4927 if(node instanceof Array){
4929 for(var i = 0, len = node.length; i < len; i++){
4930 data.push(this.getNodeData(node[i]));
4934 return this.jsonData[this.indexOf(node)] || null;
4937 beforeRender : function(){
4938 this.snapshot = this.jsonData;
4940 this.sort.apply(this, this.sortInfo);
4942 this.fireEvent("beforerender", this, this.jsonData);
4945 onLoad : function(el, o){
4946 this.fireEvent("load", this, this.jsonData, o);
4949 onLoadException : function(el, o){
4950 this.fireEvent("loadexception", this, o);
4954 * Filter the data by a specific property.
4955 * @param {String} property A property on your JSON objects
4956 * @param {String/RegExp} value Either string that the property values
4957 * should start with, or a RegExp to test against the property
4959 filter : function(property, value){
4962 var ss = this.snapshot;
4963 if(typeof value == "string"){
4964 var vlen = value.length;
4969 value = value.toLowerCase();
4970 for(var i = 0, len = ss.length; i < len; i++){
4972 if(o[property].substr(0, vlen).toLowerCase() == value){
4976 } else if(value.exec){ // regex?
4977 for(var i = 0, len = ss.length; i < len; i++){
4979 if(value.test(o[property])){
4986 this.jsonData = data;
4992 * Filter by a function. The passed function will be called with each
4993 * object in the current dataset. If the function returns true the value is kept,
4994 * otherwise it is filtered.
4995 * @param {Function} fn
4996 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4998 filterBy : function(fn, scope){
5001 var ss = this.snapshot;
5002 for(var i = 0, len = ss.length; i < len; i++){
5004 if(fn.call(scope || this, o)){
5008 this.jsonData = data;
5014 * Clears the current filter.
5016 clearFilter : function(){
5017 if(this.snapshot && this.jsonData != this.snapshot){
5018 this.jsonData = this.snapshot;
5025 * Sorts the data for this view and refreshes it.
5026 * @param {String} property A property on your JSON objects to sort on
5027 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5028 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5030 sort : function(property, dir, sortType){
5031 this.sortInfo = Array.prototype.slice.call(arguments, 0);
5034 var dsc = dir && dir.toLowerCase() == "desc";
5035 var f = function(o1, o2){
5036 var v1 = sortType ? sortType(o1[p]) : o1[p];
5037 var v2 = sortType ? sortType(o2[p]) : o2[p];
5040 return dsc ? +1 : -1;
5042 return dsc ? -1 : +1;
5047 this.jsonData.sort(f);
5049 if(this.jsonData != this.snapshot){
5050 this.snapshot.sort(f);
5056 * Ext JS Library 1.1.1
5057 * Copyright(c) 2006-2007, Ext JS, LLC.
5059 * Originally Released Under LGPL - original licence link has changed is not relivant.
5062 * <script type="text/javascript">
5067 * @class Roo.ColorPalette
5068 * @extends Roo.Component
5069 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
5070 * Here's an example of typical usage:
5072 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
5073 cp.render('my-div');
5075 cp.on('select', function(palette, selColor){
5076 // do something with selColor
5080 * Create a new ColorPalette
5081 * @param {Object} config The config object
5083 Roo.ColorPalette = function(config){
5084 Roo.ColorPalette.superclass.constructor.call(this, config);
5088 * Fires when a color is selected
5089 * @param {ColorPalette} this
5090 * @param {String} color The 6-digit color hex code (without the # symbol)
5096 this.on("select", this.handler, this.scope, true);
5099 Roo.extend(Roo.ColorPalette, Roo.Component, {
5101 * @cfg {String} itemCls
5102 * The CSS class to apply to the containing element (defaults to "x-color-palette")
5104 itemCls : "x-color-palette",
5106 * @cfg {String} value
5107 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
5108 * the hex codes are case-sensitive.
5113 ctype: "Roo.ColorPalette",
5116 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5118 allowReselect : false,
5121 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
5122 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
5123 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5124 * of colors with the width setting until the box is symmetrical.</p>
5125 * <p>You can override individual colors if needed:</p>
5127 var cp = new Roo.ColorPalette();
5128 cp.colors[0] = "FF0000"; // change the first box to red
5131 Or you can provide a custom array of your own for complete control:
5133 var cp = new Roo.ColorPalette();
5134 cp.colors = ["000000", "993300", "333300"];
5139 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5140 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5141 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5142 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5143 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5147 onRender : function(container, position){
5148 var t = new Roo.MasterTemplate(
5149 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
5151 var c = this.colors;
5152 for(var i = 0, len = c.length; i < len; i++){
5155 var el = document.createElement("div");
5156 el.className = this.itemCls;
5158 container.dom.insertBefore(el, position);
5159 this.el = Roo.get(el);
5160 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
5161 if(this.clickEvent != 'click'){
5162 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
5167 afterRender : function(){
5168 Roo.ColorPalette.superclass.afterRender.call(this);
5177 handleClick : function(e, t){
5180 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5181 this.select(c.toUpperCase());
5186 * Selects the specified color in the palette (fires the select event)
5187 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5189 select : function(color){
5190 color = color.replace("#", "");
5191 if(color != this.value || this.allowReselect){
5194 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5196 el.child("a.color-"+color).addClass("x-color-palette-sel");
5198 this.fireEvent("select", this, color);
5203 * Ext JS Library 1.1.1
5204 * Copyright(c) 2006-2007, Ext JS, LLC.
5206 * Originally Released Under LGPL - original licence link has changed is not relivant.
5209 * <script type="text/javascript">
5213 * @class Roo.DatePicker
5214 * @extends Roo.Component
5215 * Simple date picker class.
5217 * Create a new DatePicker
5218 * @param {Object} config The config object
5220 Roo.DatePicker = function(config){
5221 Roo.DatePicker.superclass.constructor.call(this, config);
5223 this.value = config && config.value ?
5224 config.value.clearTime() : new Date().clearTime();
5229 * Fires when a date is selected
5230 * @param {DatePicker} this
5231 * @param {Date} date The selected date
5235 * @event monthchange
5236 * Fires when the displayed month changes
5237 * @param {DatePicker} this
5238 * @param {Date} date The selected month
5244 this.on("select", this.handler, this.scope || this);
5246 // build the disabledDatesRE
5247 if(!this.disabledDatesRE && this.disabledDates){
5248 var dd = this.disabledDates;
5250 for(var i = 0; i < dd.length; i++){
5252 if(i != dd.length-1) {
5256 this.disabledDatesRE = new RegExp(re + ")");
5260 Roo.extend(Roo.DatePicker, Roo.Component, {
5262 * @cfg {String} todayText
5263 * The text to display on the button that selects the current date (defaults to "Today")
5265 todayText : "Today",
5267 * @cfg {String} okText
5268 * The text to display on the ok button
5270 okText : " OK ", //   to give the user extra clicking room
5272 * @cfg {String} cancelText
5273 * The text to display on the cancel button
5275 cancelText : "Cancel",
5277 * @cfg {String} todayTip
5278 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5280 todayTip : "{0} (Spacebar)",
5282 * @cfg {Date} minDate
5283 * Minimum allowable date (JavaScript date object, defaults to null)
5287 * @cfg {Date} maxDate
5288 * Maximum allowable date (JavaScript date object, defaults to null)
5292 * @cfg {String} minText
5293 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5295 minText : "This date is before the minimum date",
5297 * @cfg {String} maxText
5298 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5300 maxText : "This date is after the maximum date",
5302 * @cfg {String} format
5303 * The default date format string which can be overriden for localization support. The format must be
5304 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5308 * @cfg {Array} disabledDays
5309 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5311 disabledDays : null,
5313 * @cfg {String} disabledDaysText
5314 * The tooltip to display when the date falls on a disabled day (defaults to "")
5316 disabledDaysText : "",
5318 * @cfg {RegExp} disabledDatesRE
5319 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5321 disabledDatesRE : null,
5323 * @cfg {String} disabledDatesText
5324 * The tooltip text to display when the date falls on a disabled date (defaults to "")
5326 disabledDatesText : "",
5328 * @cfg {Boolean} constrainToViewport
5329 * True to constrain the date picker to the viewport (defaults to true)
5331 constrainToViewport : true,
5333 * @cfg {Array} monthNames
5334 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5336 monthNames : Date.monthNames,
5338 * @cfg {Array} dayNames
5339 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5341 dayNames : Date.dayNames,
5343 * @cfg {String} nextText
5344 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5346 nextText: 'Next Month (Control+Right)',
5348 * @cfg {String} prevText
5349 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5351 prevText: 'Previous Month (Control+Left)',
5353 * @cfg {String} monthYearText
5354 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5356 monthYearText: 'Choose a month (Control+Up/Down to move years)',
5358 * @cfg {Number} startDay
5359 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5363 * @cfg {Bool} showClear
5364 * Show a clear button (usefull for date form elements that can be blank.)
5370 * Sets the value of the date field
5371 * @param {Date} value The date to set
5373 setValue : function(value){
5374 var old = this.value;
5376 if (typeof(value) == 'string') {
5378 value = Date.parseDate(value, this.format);
5384 this.value = value.clearTime(true);
5386 this.update(this.value);
5391 * Gets the current selected value of the date field
5392 * @return {Date} The selected date
5394 getValue : function(){
5401 this.update(this.activeDate);
5406 onRender : function(container, position){
5409 '<table cellspacing="0">',
5410 '<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>',
5411 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5412 var dn = this.dayNames;
5413 for(var i = 0; i < 7; i++){
5414 var d = this.startDay+i;
5418 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5420 m[m.length] = "</tr></thead><tbody><tr>";
5421 for(var i = 0; i < 42; i++) {
5422 if(i % 7 == 0 && i != 0){
5423 m[m.length] = "</tr><tr>";
5425 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5427 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5428 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5430 var el = document.createElement("div");
5431 el.className = "x-date-picker";
5432 el.innerHTML = m.join("");
5434 container.dom.insertBefore(el, position);
5436 this.el = Roo.get(el);
5437 this.eventEl = Roo.get(el.firstChild);
5439 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5440 handler: this.showPrevMonth,
5442 preventDefault:true,
5446 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5447 handler: this.showNextMonth,
5449 preventDefault:true,
5453 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5455 this.monthPicker = this.el.down('div.x-date-mp');
5456 this.monthPicker.enableDisplayMode('block');
5458 var kn = new Roo.KeyNav(this.eventEl, {
5459 "left" : function(e){
5461 this.showPrevMonth() :
5462 this.update(this.activeDate.add("d", -1));
5465 "right" : function(e){
5467 this.showNextMonth() :
5468 this.update(this.activeDate.add("d", 1));
5473 this.showNextYear() :
5474 this.update(this.activeDate.add("d", -7));
5477 "down" : function(e){
5479 this.showPrevYear() :
5480 this.update(this.activeDate.add("d", 7));
5483 "pageUp" : function(e){
5484 this.showNextMonth();
5487 "pageDown" : function(e){
5488 this.showPrevMonth();
5491 "enter" : function(e){
5492 e.stopPropagation();
5499 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5501 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5503 this.el.unselectable();
5505 this.cells = this.el.select("table.x-date-inner tbody td");
5506 this.textNodes = this.el.query("table.x-date-inner tbody span");
5508 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5510 tooltip: this.monthYearText
5513 this.mbtn.on('click', this.showMonthPicker, this);
5514 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5517 var today = (new Date()).dateFormat(this.format);
5519 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5520 if (this.showClear) {
5521 baseTb.add( new Roo.Toolbar.Fill());
5524 text: String.format(this.todayText, today),
5525 tooltip: String.format(this.todayTip, today),
5526 handler: this.selectToday,
5530 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5533 if (this.showClear) {
5535 baseTb.add( new Roo.Toolbar.Fill());
5538 cls: 'x-btn-icon x-btn-clear',
5539 handler: function() {
5541 this.fireEvent("select", this, '');
5551 this.update(this.value);
5554 createMonthPicker : function(){
5555 if(!this.monthPicker.dom.firstChild){
5556 var buf = ['<table border="0" cellspacing="0">'];
5557 for(var i = 0; i < 6; i++){
5559 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5560 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5562 '<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>' :
5563 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5567 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5569 '</button><button type="button" class="x-date-mp-cancel">',
5571 '</button></td></tr>',
5574 this.monthPicker.update(buf.join(''));
5575 this.monthPicker.on('click', this.onMonthClick, this);
5576 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5578 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5579 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5581 this.mpMonths.each(function(m, a, i){
5584 m.dom.xmonth = 5 + Math.round(i * .5);
5586 m.dom.xmonth = Math.round((i-1) * .5);
5592 showMonthPicker : function(){
5593 this.createMonthPicker();
5594 var size = this.el.getSize();
5595 this.monthPicker.setSize(size);
5596 this.monthPicker.child('table').setSize(size);
5598 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5599 this.updateMPMonth(this.mpSelMonth);
5600 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5601 this.updateMPYear(this.mpSelYear);
5603 this.monthPicker.slideIn('t', {duration:.2});
5606 updateMPYear : function(y){
5608 var ys = this.mpYears.elements;
5609 for(var i = 1; i <= 10; i++){
5610 var td = ys[i-1], y2;
5612 y2 = y + Math.round(i * .5);
5613 td.firstChild.innerHTML = y2;
5616 y2 = y - (5-Math.round(i * .5));
5617 td.firstChild.innerHTML = y2;
5620 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5624 updateMPMonth : function(sm){
5625 this.mpMonths.each(function(m, a, i){
5626 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5630 selectMPMonth: function(m){
5634 onMonthClick : function(e, t){
5636 var el = new Roo.Element(t), pn;
5637 if(el.is('button.x-date-mp-cancel')){
5638 this.hideMonthPicker();
5640 else if(el.is('button.x-date-mp-ok')){
5641 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5642 this.hideMonthPicker();
5644 else if(pn = el.up('td.x-date-mp-month', 2)){
5645 this.mpMonths.removeClass('x-date-mp-sel');
5646 pn.addClass('x-date-mp-sel');
5647 this.mpSelMonth = pn.dom.xmonth;
5649 else if(pn = el.up('td.x-date-mp-year', 2)){
5650 this.mpYears.removeClass('x-date-mp-sel');
5651 pn.addClass('x-date-mp-sel');
5652 this.mpSelYear = pn.dom.xyear;
5654 else if(el.is('a.x-date-mp-prev')){
5655 this.updateMPYear(this.mpyear-10);
5657 else if(el.is('a.x-date-mp-next')){
5658 this.updateMPYear(this.mpyear+10);
5662 onMonthDblClick : function(e, t){
5664 var el = new Roo.Element(t), pn;
5665 if(pn = el.up('td.x-date-mp-month', 2)){
5666 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5667 this.hideMonthPicker();
5669 else if(pn = el.up('td.x-date-mp-year', 2)){
5670 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5671 this.hideMonthPicker();
5675 hideMonthPicker : function(disableAnim){
5676 if(this.monthPicker){
5677 if(disableAnim === true){
5678 this.monthPicker.hide();
5680 this.monthPicker.slideOut('t', {duration:.2});
5686 showPrevMonth : function(e){
5687 this.update(this.activeDate.add("mo", -1));
5691 showNextMonth : function(e){
5692 this.update(this.activeDate.add("mo", 1));
5696 showPrevYear : function(){
5697 this.update(this.activeDate.add("y", -1));
5701 showNextYear : function(){
5702 this.update(this.activeDate.add("y", 1));
5706 handleMouseWheel : function(e){
5707 var delta = e.getWheelDelta();
5709 this.showPrevMonth();
5711 } else if(delta < 0){
5712 this.showNextMonth();
5718 handleDateClick : function(e, t){
5720 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5721 this.setValue(new Date(t.dateValue));
5722 this.fireEvent("select", this, this.value);
5727 selectToday : function(){
5728 this.setValue(new Date().clearTime());
5729 this.fireEvent("select", this, this.value);
5733 update : function(date)
5735 var vd = this.activeDate;
5736 this.activeDate = date;
5738 var t = date.getTime();
5739 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5740 this.cells.removeClass("x-date-selected");
5741 this.cells.each(function(c){
5742 if(c.dom.firstChild.dateValue == t){
5743 c.addClass("x-date-selected");
5744 setTimeout(function(){
5745 try{c.dom.firstChild.focus();}catch(e){}
5754 var days = date.getDaysInMonth();
5755 var firstOfMonth = date.getFirstDateOfMonth();
5756 var startingPos = firstOfMonth.getDay()-this.startDay;
5758 if(startingPos <= this.startDay){
5762 var pm = date.add("mo", -1);
5763 var prevStart = pm.getDaysInMonth()-startingPos;
5765 var cells = this.cells.elements;
5766 var textEls = this.textNodes;
5767 days += startingPos;
5769 // convert everything to numbers so it's fast
5771 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5772 var today = new Date().clearTime().getTime();
5773 var sel = date.clearTime().getTime();
5774 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5775 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5776 var ddMatch = this.disabledDatesRE;
5777 var ddText = this.disabledDatesText;
5778 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5779 var ddaysText = this.disabledDaysText;
5780 var format = this.format;
5782 var setCellClass = function(cal, cell){
5784 var t = d.getTime();
5785 cell.firstChild.dateValue = t;
5787 cell.className += " x-date-today";
5788 cell.title = cal.todayText;
5791 cell.className += " x-date-selected";
5792 setTimeout(function(){
5793 try{cell.firstChild.focus();}catch(e){}
5798 cell.className = " x-date-disabled";
5799 cell.title = cal.minText;
5803 cell.className = " x-date-disabled";
5804 cell.title = cal.maxText;
5808 if(ddays.indexOf(d.getDay()) != -1){
5809 cell.title = ddaysText;
5810 cell.className = " x-date-disabled";
5813 if(ddMatch && format){
5814 var fvalue = d.dateFormat(format);
5815 if(ddMatch.test(fvalue)){
5816 cell.title = ddText.replace("%0", fvalue);
5817 cell.className = " x-date-disabled";
5823 for(; i < startingPos; i++) {
5824 textEls[i].innerHTML = (++prevStart);
5825 d.setDate(d.getDate()+1);
5826 cells[i].className = "x-date-prevday";
5827 setCellClass(this, cells[i]);
5829 for(; i < days; i++){
5830 intDay = i - startingPos + 1;
5831 textEls[i].innerHTML = (intDay);
5832 d.setDate(d.getDate()+1);
5833 cells[i].className = "x-date-active";
5834 setCellClass(this, cells[i]);
5837 for(; i < 42; i++) {
5838 textEls[i].innerHTML = (++extraDays);
5839 d.setDate(d.getDate()+1);
5840 cells[i].className = "x-date-nextday";
5841 setCellClass(this, cells[i]);
5844 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5845 this.fireEvent('monthchange', this, date);
5847 if(!this.internalRender){
5848 var main = this.el.dom.firstChild;
5849 var w = main.offsetWidth;
5850 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5851 Roo.fly(main).setWidth(w);
5852 this.internalRender = true;
5853 // opera does not respect the auto grow header center column
5854 // then, after it gets a width opera refuses to recalculate
5855 // without a second pass
5856 if(Roo.isOpera && !this.secondPass){
5857 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5858 this.secondPass = true;
5859 this.update.defer(10, this, [date]);
5867 * Ext JS Library 1.1.1
5868 * Copyright(c) 2006-2007, Ext JS, LLC.
5870 * Originally Released Under LGPL - original licence link has changed is not relivant.
5873 * <script type="text/javascript">
5876 * @class Roo.TabPanel
5877 * @extends Roo.util.Observable
5878 * A lightweight tab container.
5882 // basic tabs 1, built from existing content
5883 var tabs = new Roo.TabPanel("tabs1");
5884 tabs.addTab("script", "View Script");
5885 tabs.addTab("markup", "View Markup");
5886 tabs.activate("script");
5888 // more advanced tabs, built from javascript
5889 var jtabs = new Roo.TabPanel("jtabs");
5890 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5892 // set up the UpdateManager
5893 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5894 var updater = tab2.getUpdateManager();
5895 updater.setDefaultUrl("ajax1.htm");
5896 tab2.on('activate', updater.refresh, updater, true);
5898 // Use setUrl for Ajax loading
5899 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5900 tab3.setUrl("ajax2.htm", null, true);
5903 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5906 jtabs.activate("jtabs-1");
5909 * Create a new TabPanel.
5910 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5911 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5913 Roo.TabPanel = function(container, config){
5915 * The container element for this TabPanel.
5918 this.el = Roo.get(container, true);
5920 if(typeof config == "boolean"){
5921 this.tabPosition = config ? "bottom" : "top";
5923 Roo.apply(this, config);
5926 if(this.tabPosition == "bottom"){
5927 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5928 this.el.addClass("x-tabs-bottom");
5930 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5931 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5932 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5934 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5936 if(this.tabPosition != "bottom"){
5937 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5940 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5941 this.el.addClass("x-tabs-top");
5945 this.bodyEl.setStyle("position", "relative");
5948 this.activateDelegate = this.activate.createDelegate(this);
5953 * Fires when the active tab changes
5954 * @param {Roo.TabPanel} this
5955 * @param {Roo.TabPanelItem} activePanel The new active tab
5959 * @event beforetabchange
5960 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5961 * @param {Roo.TabPanel} this
5962 * @param {Object} e Set cancel to true on this object to cancel the tab change
5963 * @param {Roo.TabPanelItem} tab The tab being changed to
5965 "beforetabchange" : true
5968 Roo.EventManager.onWindowResize(this.onResize, this);
5969 this.cpad = this.el.getPadding("lr");
5970 this.hiddenCount = 0;
5973 // toolbar on the tabbar support...
5975 var tcfg = this.toolbar;
5976 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5977 this.toolbar = new Roo.Toolbar(tcfg);
5979 var tbl = tcfg.container.child('table', true);
5980 tbl.setAttribute('width', '100%');
5987 Roo.TabPanel.superclass.constructor.call(this);
5990 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5992 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5994 tabPosition : "top",
5996 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5998 currentTabWidth : 0,
6000 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6004 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6008 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6010 preferredTabWidth : 175,
6012 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6016 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6018 monitorResize : true,
6020 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
6025 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6026 * @param {String} id The id of the div to use <b>or create</b>
6027 * @param {String} text The text for the tab
6028 * @param {String} content (optional) Content to put in the TabPanelItem body
6029 * @param {Boolean} closable (optional) True to create a close icon on the tab
6030 * @return {Roo.TabPanelItem} The created TabPanelItem
6032 addTab : function(id, text, content, closable){
6033 var item = new Roo.TabPanelItem(this, id, text, closable);
6034 this.addTabItem(item);
6036 item.setContent(content);
6042 * Returns the {@link Roo.TabPanelItem} with the specified id/index
6043 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6044 * @return {Roo.TabPanelItem}
6046 getTab : function(id){
6047 return this.items[id];
6051 * Hides the {@link Roo.TabPanelItem} with the specified id/index
6052 * @param {String/Number} id The id or index of the TabPanelItem to hide.
6054 hideTab : function(id){
6055 var t = this.items[id];
6059 this.autoSizeTabs();
6064 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6065 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6067 unhideTab : function(id){
6068 var t = this.items[id];
6072 this.autoSizeTabs();
6077 * Adds an existing {@link Roo.TabPanelItem}.
6078 * @param {Roo.TabPanelItem} item The TabPanelItem to add
6080 addTabItem : function(item){
6081 this.items[item.id] = item;
6082 this.items.push(item);
6083 if(this.resizeTabs){
6084 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6085 this.autoSizeTabs();
6092 * Removes a {@link Roo.TabPanelItem}.
6093 * @param {String/Number} id The id or index of the TabPanelItem to remove.
6095 removeTab : function(id){
6096 var items = this.items;
6097 var tab = items[id];
6098 if(!tab) { return; }
6099 var index = items.indexOf(tab);
6100 if(this.active == tab && items.length > 1){
6101 var newTab = this.getNextAvailable(index);
6106 this.stripEl.dom.removeChild(tab.pnode.dom);
6107 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6108 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6110 items.splice(index, 1);
6111 delete this.items[tab.id];
6112 tab.fireEvent("close", tab);
6113 tab.purgeListeners();
6114 this.autoSizeTabs();
6117 getNextAvailable : function(start){
6118 var items = this.items;
6120 // look for a next tab that will slide over to
6121 // replace the one being removed
6122 while(index < items.length){
6123 var item = items[++index];
6124 if(item && !item.isHidden()){
6128 // if one isn't found select the previous tab (on the left)
6131 var item = items[--index];
6132 if(item && !item.isHidden()){
6140 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6141 * @param {String/Number} id The id or index of the TabPanelItem to disable.
6143 disableTab : function(id){
6144 var tab = this.items[id];
6145 if(tab && this.active != tab){
6151 * Enables a {@link Roo.TabPanelItem} that is disabled.
6152 * @param {String/Number} id The id or index of the TabPanelItem to enable.
6154 enableTab : function(id){
6155 var tab = this.items[id];
6160 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6161 * @param {String/Number} id The id or index of the TabPanelItem to activate.
6162 * @return {Roo.TabPanelItem} The TabPanelItem.
6164 activate : function(id){
6165 var tab = this.items[id];
6169 if(tab == this.active || tab.disabled){
6173 this.fireEvent("beforetabchange", this, e, tab);
6174 if(e.cancel !== true && !tab.disabled){
6178 this.active = this.items[id];
6180 this.fireEvent("tabchange", this, this.active);
6186 * Gets the active {@link Roo.TabPanelItem}.
6187 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6189 getActiveTab : function(){
6194 * Updates the tab body element to fit the height of the container element
6195 * for overflow scrolling
6196 * @param {Number} targetHeight (optional) Override the starting height from the elements height
6198 syncHeight : function(targetHeight){
6199 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6200 var bm = this.bodyEl.getMargins();
6201 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6202 this.bodyEl.setHeight(newHeight);
6206 onResize : function(){
6207 if(this.monitorResize){
6208 this.autoSizeTabs();
6213 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6215 beginUpdate : function(){
6216 this.updating = true;
6220 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6222 endUpdate : function(){
6223 this.updating = false;
6224 this.autoSizeTabs();
6228 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6230 autoSizeTabs : function(){
6231 var count = this.items.length;
6232 var vcount = count - this.hiddenCount;
6233 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6236 var w = Math.max(this.el.getWidth() - this.cpad, 10);
6237 var availWidth = Math.floor(w / vcount);
6238 var b = this.stripBody;
6239 if(b.getWidth() > w){
6240 var tabs = this.items;
6241 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6242 if(availWidth < this.minTabWidth){
6243 /*if(!this.sleft){ // incomplete scrolling code
6244 this.createScrollButtons();
6247 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6250 if(this.currentTabWidth < this.preferredTabWidth){
6251 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6257 * Returns the number of tabs in this TabPanel.
6260 getCount : function(){
6261 return this.items.length;
6265 * Resizes all the tabs to the passed width
6266 * @param {Number} The new width
6268 setTabWidth : function(width){
6269 this.currentTabWidth = width;
6270 for(var i = 0, len = this.items.length; i < len; i++) {
6271 if(!this.items[i].isHidden()) {
6272 this.items[i].setWidth(width);
6278 * Destroys this TabPanel
6279 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6281 destroy : function(removeEl){
6282 Roo.EventManager.removeResizeListener(this.onResize, this);
6283 for(var i = 0, len = this.items.length; i < len; i++){
6284 this.items[i].purgeListeners();
6286 if(removeEl === true){
6294 * @class Roo.TabPanelItem
6295 * @extends Roo.util.Observable
6296 * Represents an individual item (tab plus body) in a TabPanel.
6297 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6298 * @param {String} id The id of this TabPanelItem
6299 * @param {String} text The text for the tab of this TabPanelItem
6300 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6302 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6304 * The {@link Roo.TabPanel} this TabPanelItem belongs to
6305 * @type Roo.TabPanel
6307 this.tabPanel = tabPanel;
6309 * The id for this TabPanelItem
6314 this.disabled = false;
6318 this.loaded = false;
6319 this.closable = closable;
6322 * The body element for this TabPanelItem.
6325 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6326 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6327 this.bodyEl.setStyle("display", "block");
6328 this.bodyEl.setStyle("zoom", "1");
6331 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6333 this.el = Roo.get(els.el, true);
6334 this.inner = Roo.get(els.inner, true);
6335 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6336 this.pnode = Roo.get(els.el.parentNode, true);
6337 this.el.on("mousedown", this.onTabMouseDown, this);
6338 this.el.on("click", this.onTabClick, this);
6341 var c = Roo.get(els.close, true);
6342 c.dom.title = this.closeText;
6343 c.addClassOnOver("close-over");
6344 c.on("click", this.closeClick, this);
6350 * Fires when this tab becomes the active tab.
6351 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6352 * @param {Roo.TabPanelItem} this
6356 * @event beforeclose
6357 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6358 * @param {Roo.TabPanelItem} this
6359 * @param {Object} e Set cancel to true on this object to cancel the close.
6361 "beforeclose": true,
6364 * Fires when this tab is closed.
6365 * @param {Roo.TabPanelItem} this
6370 * Fires when this tab is no longer the active tab.
6371 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6372 * @param {Roo.TabPanelItem} this
6376 this.hidden = false;
6378 Roo.TabPanelItem.superclass.constructor.call(this);
6381 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6382 purgeListeners : function(){
6383 Roo.util.Observable.prototype.purgeListeners.call(this);
6384 this.el.removeAllListeners();
6387 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6390 this.pnode.addClass("on");
6393 this.tabPanel.stripWrap.repaint();
6395 this.fireEvent("activate", this.tabPanel, this);
6399 * Returns true if this tab is the active tab.
6402 isActive : function(){
6403 return this.tabPanel.getActiveTab() == this;
6407 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6410 this.pnode.removeClass("on");
6412 this.fireEvent("deactivate", this.tabPanel, this);
6415 hideAction : function(){
6417 this.bodyEl.setStyle("position", "absolute");
6418 this.bodyEl.setLeft("-20000px");
6419 this.bodyEl.setTop("-20000px");
6422 showAction : function(){
6423 this.bodyEl.setStyle("position", "relative");
6424 this.bodyEl.setTop("");
6425 this.bodyEl.setLeft("");
6430 * Set the tooltip for the tab.
6431 * @param {String} tooltip The tab's tooltip
6433 setTooltip : function(text){
6434 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6435 this.textEl.dom.qtip = text;
6436 this.textEl.dom.removeAttribute('title');
6438 this.textEl.dom.title = text;
6442 onTabClick : function(e){
6444 this.tabPanel.activate(this.id);
6447 onTabMouseDown : function(e){
6449 this.tabPanel.activate(this.id);
6452 getWidth : function(){
6453 return this.inner.getWidth();
6456 setWidth : function(width){
6457 var iwidth = width - this.pnode.getPadding("lr");
6458 this.inner.setWidth(iwidth);
6459 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6460 this.pnode.setWidth(width);
6464 * Show or hide the tab
6465 * @param {Boolean} hidden True to hide or false to show.
6467 setHidden : function(hidden){
6468 this.hidden = hidden;
6469 this.pnode.setStyle("display", hidden ? "none" : "");
6473 * Returns true if this tab is "hidden"
6476 isHidden : function(){
6481 * Returns the text for this tab
6484 getText : function(){
6488 autoSize : function(){
6489 //this.el.beginMeasure();
6490 this.textEl.setWidth(1);
6492 * #2804 [new] Tabs in Roojs
6493 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6495 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6496 //this.el.endMeasure();
6500 * Sets the text for the tab (Note: this also sets the tooltip text)
6501 * @param {String} text The tab's text and tooltip
6503 setText : function(text){
6505 this.textEl.update(text);
6506 this.setTooltip(text);
6507 if(!this.tabPanel.resizeTabs){
6512 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6514 activate : function(){
6515 this.tabPanel.activate(this.id);
6519 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6521 disable : function(){
6522 if(this.tabPanel.active != this){
6523 this.disabled = true;
6524 this.pnode.addClass("disabled");
6529 * Enables this TabPanelItem if it was previously disabled.
6531 enable : function(){
6532 this.disabled = false;
6533 this.pnode.removeClass("disabled");
6537 * Sets the content for this TabPanelItem.
6538 * @param {String} content The content
6539 * @param {Boolean} loadScripts true to look for and load scripts
6541 setContent : function(content, loadScripts){
6542 this.bodyEl.update(content, loadScripts);
6546 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6547 * @return {Roo.UpdateManager} The UpdateManager
6549 getUpdateManager : function(){
6550 return this.bodyEl.getUpdateManager();
6554 * Set a URL to be used to load the content for this TabPanelItem.
6555 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6556 * @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)
6557 * @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)
6558 * @return {Roo.UpdateManager} The UpdateManager
6560 setUrl : function(url, params, loadOnce){
6561 if(this.refreshDelegate){
6562 this.un('activate', this.refreshDelegate);
6564 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6565 this.on("activate", this.refreshDelegate);
6566 return this.bodyEl.getUpdateManager();
6570 _handleRefresh : function(url, params, loadOnce){
6571 if(!loadOnce || !this.loaded){
6572 var updater = this.bodyEl.getUpdateManager();
6573 updater.update(url, params, this._setLoaded.createDelegate(this));
6578 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6579 * Will fail silently if the setUrl method has not been called.
6580 * This does not activate the panel, just updates its content.
6582 refresh : function(){
6583 if(this.refreshDelegate){
6584 this.loaded = false;
6585 this.refreshDelegate();
6590 _setLoaded : function(){
6595 closeClick : function(e){
6598 this.fireEvent("beforeclose", this, o);
6599 if(o.cancel !== true){
6600 this.tabPanel.removeTab(this.id);
6604 * The text displayed in the tooltip for the close icon.
6607 closeText : "Close this tab"
6611 Roo.TabPanel.prototype.createStrip = function(container){
6612 var strip = document.createElement("div");
6613 strip.className = "x-tabs-wrap";
6614 container.appendChild(strip);
6618 Roo.TabPanel.prototype.createStripList = function(strip){
6619 // div wrapper for retard IE
6620 // returns the "tr" element.
6621 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6622 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6623 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6624 return strip.firstChild.firstChild.firstChild.firstChild;
6627 Roo.TabPanel.prototype.createBody = function(container){
6628 var body = document.createElement("div");
6629 Roo.id(body, "tab-body");
6630 Roo.fly(body).addClass("x-tabs-body");
6631 container.appendChild(body);
6635 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6636 var body = Roo.getDom(id);
6638 body = document.createElement("div");
6641 Roo.fly(body).addClass("x-tabs-item-body");
6642 bodyEl.insertBefore(body, bodyEl.firstChild);
6646 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6647 var td = document.createElement("td");
6648 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6649 //stripEl.appendChild(td);
6651 td.className = "x-tabs-closable";
6653 this.closeTpl = new Roo.Template(
6654 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6655 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6656 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6659 var el = this.closeTpl.overwrite(td, {"text": text});
6660 var close = el.getElementsByTagName("div")[0];
6661 var inner = el.getElementsByTagName("em")[0];
6662 return {"el": el, "close": close, "inner": inner};
6665 this.tabTpl = new Roo.Template(
6666 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6667 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6670 var el = this.tabTpl.overwrite(td, {"text": text});
6671 var inner = el.getElementsByTagName("em")[0];
6672 return {"el": el, "inner": inner};
6676 * Ext JS Library 1.1.1
6677 * Copyright(c) 2006-2007, Ext JS, LLC.
6679 * Originally Released Under LGPL - original licence link has changed is not relivant.
6682 * <script type="text/javascript">
6687 * @extends Roo.util.Observable
6688 * Simple Button class
6689 * @cfg {String} text The button text
6690 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6691 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6692 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6693 * @cfg {Object} scope The scope of the handler
6694 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6695 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6696 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6697 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6698 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6699 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6700 applies if enableToggle = true)
6701 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6702 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6703 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6705 * Create a new button
6706 * @param {Object} config The config object
6708 Roo.Button = function(renderTo, config)
6712 renderTo = config.renderTo || false;
6715 Roo.apply(this, config);
6719 * Fires when this button is clicked
6720 * @param {Button} this
6721 * @param {EventObject} e The click event
6726 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6727 * @param {Button} this
6728 * @param {Boolean} pressed
6733 * Fires when the mouse hovers over the button
6734 * @param {Button} this
6735 * @param {Event} e The event object
6740 * Fires when the mouse exits the button
6741 * @param {Button} this
6742 * @param {Event} e The event object
6747 * Fires when the button is rendered
6748 * @param {Button} this
6753 this.menu = Roo.menu.MenuMgr.get(this.menu);
6755 // register listeners first!! - so render can be captured..
6756 Roo.util.Observable.call(this);
6758 this.render(renderTo);
6764 Roo.extend(Roo.Button, Roo.util.Observable, {
6770 * Read-only. True if this button is hidden
6775 * Read-only. True if this button is disabled
6780 * Read-only. True if this button is pressed (only if enableToggle = true)
6786 * @cfg {Number} tabIndex
6787 * The DOM tabIndex for this button (defaults to undefined)
6789 tabIndex : undefined,
6792 * @cfg {Boolean} enableToggle
6793 * True to enable pressed/not pressed toggling (defaults to false)
6795 enableToggle: false,
6798 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6802 * @cfg {String} menuAlign
6803 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6805 menuAlign : "tl-bl?",
6808 * @cfg {String} iconCls
6809 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6811 iconCls : undefined,
6813 * @cfg {String} type
6814 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6819 menuClassTarget: 'tr',
6822 * @cfg {String} clickEvent
6823 * The type of event to map to the button's event handler (defaults to 'click')
6825 clickEvent : 'click',
6828 * @cfg {Boolean} handleMouseEvents
6829 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6831 handleMouseEvents : true,
6834 * @cfg {String} tooltipType
6835 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6837 tooltipType : 'qtip',
6841 * A CSS class to apply to the button's main element.
6845 * @cfg {Roo.Template} template (Optional)
6846 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6847 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6848 * require code modifications if required elements (e.g. a button) aren't present.
6852 render : function(renderTo){
6854 if(this.hideParent){
6855 this.parentEl = Roo.get(renderTo);
6859 if(!Roo.Button.buttonTemplate){
6860 // hideous table template
6861 Roo.Button.buttonTemplate = new Roo.Template(
6862 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6863 '<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>',
6864 "</tr></tbody></table>");
6866 this.template = Roo.Button.buttonTemplate;
6868 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6869 var btnEl = btn.child("button:first");
6870 btnEl.on('focus', this.onFocus, this);
6871 btnEl.on('blur', this.onBlur, this);
6873 btn.addClass(this.cls);
6876 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6879 btnEl.addClass(this.iconCls);
6881 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6884 if(this.tabIndex !== undefined){
6885 btnEl.dom.tabIndex = this.tabIndex;
6888 if(typeof this.tooltip == 'object'){
6889 Roo.QuickTips.tips(Roo.apply({
6893 btnEl.dom[this.tooltipType] = this.tooltip;
6897 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6901 this.el.dom.id = this.el.id = this.id;
6904 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6905 this.menu.on("show", this.onMenuShow, this);
6906 this.menu.on("hide", this.onMenuHide, this);
6908 btn.addClass("x-btn");
6909 if(Roo.isIE && !Roo.isIE7){
6910 this.autoWidth.defer(1, this);
6914 if(this.handleMouseEvents){
6915 btn.on("mouseover", this.onMouseOver, this);
6916 btn.on("mouseout", this.onMouseOut, this);
6917 btn.on("mousedown", this.onMouseDown, this);
6919 btn.on(this.clickEvent, this.onClick, this);
6920 //btn.on("mouseup", this.onMouseUp, this);
6927 Roo.ButtonToggleMgr.register(this);
6929 this.el.addClass("x-btn-pressed");
6932 var repeater = new Roo.util.ClickRepeater(btn,
6933 typeof this.repeat == "object" ? this.repeat : {}
6935 repeater.on("click", this.onClick, this);
6938 this.fireEvent('render', this);
6942 * Returns the button's underlying element
6943 * @return {Roo.Element} The element
6950 * Destroys this Button and removes any listeners.
6952 destroy : function(){
6953 Roo.ButtonToggleMgr.unregister(this);
6954 this.el.removeAllListeners();
6955 this.purgeListeners();
6960 autoWidth : function(){
6962 this.el.setWidth("auto");
6963 if(Roo.isIE7 && Roo.isStrict){
6964 var ib = this.el.child('button');
6965 if(ib && ib.getWidth() > 20){
6967 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6972 this.el.beginMeasure();
6974 if(this.el.getWidth() < this.minWidth){
6975 this.el.setWidth(this.minWidth);
6978 this.el.endMeasure();
6985 * Assigns this button's click handler
6986 * @param {Function} handler The function to call when the button is clicked
6987 * @param {Object} scope (optional) Scope for the function passed in
6989 setHandler : function(handler, scope){
6990 this.handler = handler;
6995 * Sets this button's text
6996 * @param {String} text The button text
6998 setText : function(text){
7001 this.el.child("td.x-btn-center button.x-btn-text").update(text);
7007 * Gets the text for this button
7008 * @return {String} The button text
7010 getText : function(){
7018 this.hidden = false;
7020 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7030 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7035 * Convenience function for boolean show/hide
7036 * @param {Boolean} visible True to show, false to hide
7038 setVisible: function(visible){
7047 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7048 * @param {Boolean} state (optional) Force a particular state
7050 toggle : function(state){
7051 state = state === undefined ? !this.pressed : state;
7052 if(state != this.pressed){
7054 this.el.addClass("x-btn-pressed");
7055 this.pressed = true;
7056 this.fireEvent("toggle", this, true);
7058 this.el.removeClass("x-btn-pressed");
7059 this.pressed = false;
7060 this.fireEvent("toggle", this, false);
7062 if(this.toggleHandler){
7063 this.toggleHandler.call(this.scope || this, this, state);
7072 this.el.child('button:first').focus();
7076 * Disable this button
7078 disable : function(){
7080 this.el.addClass("x-btn-disabled");
7082 this.disabled = true;
7086 * Enable this button
7088 enable : function(){
7090 this.el.removeClass("x-btn-disabled");
7092 this.disabled = false;
7096 * Convenience function for boolean enable/disable
7097 * @param {Boolean} enabled True to enable, false to disable
7099 setDisabled : function(v){
7100 this[v !== true ? "enable" : "disable"]();
7104 onClick : function(e)
7113 if(this.enableToggle){
7116 if(this.menu && !this.menu.isVisible()){
7117 this.menu.show(this.el, this.menuAlign);
7119 this.fireEvent("click", this, e);
7121 this.el.removeClass("x-btn-over");
7122 this.handler.call(this.scope || this, this, e);
7127 onMouseOver : function(e){
7129 this.el.addClass("x-btn-over");
7130 this.fireEvent('mouseover', this, e);
7134 onMouseOut : function(e){
7135 if(!e.within(this.el, true)){
7136 this.el.removeClass("x-btn-over");
7137 this.fireEvent('mouseout', this, e);
7141 onFocus : function(e){
7143 this.el.addClass("x-btn-focus");
7147 onBlur : function(e){
7148 this.el.removeClass("x-btn-focus");
7151 onMouseDown : function(e){
7152 if(!this.disabled && e.button == 0){
7153 this.el.addClass("x-btn-click");
7154 Roo.get(document).on('mouseup', this.onMouseUp, this);
7158 onMouseUp : function(e){
7160 this.el.removeClass("x-btn-click");
7161 Roo.get(document).un('mouseup', this.onMouseUp, this);
7165 onMenuShow : function(e){
7166 this.el.addClass("x-btn-menu-active");
7169 onMenuHide : function(e){
7170 this.el.removeClass("x-btn-menu-active");
7174 // Private utility class used by Button
7175 Roo.ButtonToggleMgr = function(){
7178 function toggleGroup(btn, state){
7180 var g = groups[btn.toggleGroup];
7181 for(var i = 0, l = g.length; i < l; i++){
7190 register : function(btn){
7191 if(!btn.toggleGroup){
7194 var g = groups[btn.toggleGroup];
7196 g = groups[btn.toggleGroup] = [];
7199 btn.on("toggle", toggleGroup);
7202 unregister : function(btn){
7203 if(!btn.toggleGroup){
7206 var g = groups[btn.toggleGroup];
7209 btn.un("toggle", toggleGroup);
7215 * Ext JS Library 1.1.1
7216 * Copyright(c) 2006-2007, Ext JS, LLC.
7218 * Originally Released Under LGPL - original licence link has changed is not relivant.
7221 * <script type="text/javascript">
7225 * @class Roo.SplitButton
7226 * @extends Roo.Button
7227 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7228 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
7229 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7230 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7231 * @cfg {String} arrowTooltip The title attribute of the arrow
7233 * Create a new menu button
7234 * @param {String/HTMLElement/Element} renderTo The element to append the button to
7235 * @param {Object} config The config object
7237 Roo.SplitButton = function(renderTo, config){
7238 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7241 * Fires when this button's arrow is clicked
7242 * @param {SplitButton} this
7243 * @param {EventObject} e The click event
7245 this.addEvents({"arrowclick":true});
7248 Roo.extend(Roo.SplitButton, Roo.Button, {
7249 render : function(renderTo){
7250 // this is one sweet looking template!
7251 var tpl = new Roo.Template(
7252 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7253 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7254 '<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>',
7255 "</tbody></table></td><td>",
7256 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7257 '<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>',
7258 "</tbody></table></td></tr></table>"
7260 var btn = tpl.append(renderTo, [this.text, this.type], true);
7261 var btnEl = btn.child("button");
7263 btn.addClass(this.cls);
7266 btnEl.setStyle('background-image', 'url(' +this.icon +')');
7269 btnEl.addClass(this.iconCls);
7271 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7275 if(this.handleMouseEvents){
7276 btn.on("mouseover", this.onMouseOver, this);
7277 btn.on("mouseout", this.onMouseOut, this);
7278 btn.on("mousedown", this.onMouseDown, this);
7279 btn.on("mouseup", this.onMouseUp, this);
7281 btn.on(this.clickEvent, this.onClick, this);
7283 if(typeof this.tooltip == 'object'){
7284 Roo.QuickTips.tips(Roo.apply({
7288 btnEl.dom[this.tooltipType] = this.tooltip;
7291 if(this.arrowTooltip){
7292 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7301 this.el.addClass("x-btn-pressed");
7303 if(Roo.isIE && !Roo.isIE7){
7304 this.autoWidth.defer(1, this);
7309 this.menu.on("show", this.onMenuShow, this);
7310 this.menu.on("hide", this.onMenuHide, this);
7312 this.fireEvent('render', this);
7316 autoWidth : function(){
7318 var tbl = this.el.child("table:first");
7319 var tbl2 = this.el.child("table:last");
7320 this.el.setWidth("auto");
7321 tbl.setWidth("auto");
7322 if(Roo.isIE7 && Roo.isStrict){
7323 var ib = this.el.child('button:first');
7324 if(ib && ib.getWidth() > 20){
7326 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7331 this.el.beginMeasure();
7333 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7334 tbl.setWidth(this.minWidth-tbl2.getWidth());
7337 this.el.endMeasure();
7340 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7344 * Sets this button's click handler
7345 * @param {Function} handler The function to call when the button is clicked
7346 * @param {Object} scope (optional) Scope for the function passed above
7348 setHandler : function(handler, scope){
7349 this.handler = handler;
7354 * Sets this button's arrow click handler
7355 * @param {Function} handler The function to call when the arrow is clicked
7356 * @param {Object} scope (optional) Scope for the function passed above
7358 setArrowHandler : function(handler, scope){
7359 this.arrowHandler = handler;
7368 this.el.child("button:first").focus();
7373 onClick : function(e){
7376 if(e.getTarget(".x-btn-menu-arrow-wrap")){
7377 if(this.menu && !this.menu.isVisible()){
7378 this.menu.show(this.el, this.menuAlign);
7380 this.fireEvent("arrowclick", this, e);
7381 if(this.arrowHandler){
7382 this.arrowHandler.call(this.scope || this, this, e);
7385 this.fireEvent("click", this, e);
7387 this.handler.call(this.scope || this, this, e);
7393 onMouseDown : function(e){
7395 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7399 onMouseUp : function(e){
7400 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7406 Roo.MenuButton = Roo.SplitButton;/*
7408 * Ext JS Library 1.1.1
7409 * Copyright(c) 2006-2007, Ext JS, LLC.
7411 * Originally Released Under LGPL - original licence link has changed is not relivant.
7414 * <script type="text/javascript">
7418 * @class Roo.Toolbar
7419 * Basic Toolbar class.
7421 * Creates a new Toolbar
7422 * @param {Object} container The config object
7424 Roo.Toolbar = function(container, buttons, config)
7426 /// old consturctor format still supported..
7427 if(container instanceof Array){ // omit the container for later rendering
7428 buttons = container;
7432 if (typeof(container) == 'object' && container.xtype) {
7434 container = config.container;
7435 buttons = config.buttons || []; // not really - use items!!
7438 if (config && config.items) {
7439 xitems = config.items;
7440 delete config.items;
7442 Roo.apply(this, config);
7443 this.buttons = buttons;
7446 this.render(container);
7448 this.xitems = xitems;
7449 Roo.each(xitems, function(b) {
7455 Roo.Toolbar.prototype = {
7457 * @cfg {Array} items
7458 * array of button configs or elements to add (will be converted to a MixedCollection)
7462 * @cfg {String/HTMLElement/Element} container
7463 * The id or element that will contain the toolbar
7466 render : function(ct){
7467 this.el = Roo.get(ct);
7469 this.el.addClass(this.cls);
7471 // using a table allows for vertical alignment
7472 // 100% width is needed by Safari...
7473 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7474 this.tr = this.el.child("tr", true);
7476 this.items = new Roo.util.MixedCollection(false, function(o){
7477 return o.id || ("item" + (++autoId));
7480 this.add.apply(this, this.buttons);
7481 delete this.buttons;
7486 * Adds element(s) to the toolbar -- this function takes a variable number of
7487 * arguments of mixed type and adds them to the toolbar.
7488 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7490 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7491 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7492 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7493 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7494 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7495 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7496 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7497 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7498 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7500 * @param {Mixed} arg2
7501 * @param {Mixed} etc.
7504 var a = arguments, l = a.length;
7505 for(var i = 0; i < l; i++){
7510 _add : function(el) {
7513 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7516 if (el.applyTo){ // some kind of form field
7517 return this.addField(el);
7519 if (el.render){ // some kind of Toolbar.Item
7520 return this.addItem(el);
7522 if (typeof el == "string"){ // string
7523 if(el == "separator" || el == "-"){
7524 return this.addSeparator();
7527 return this.addSpacer();
7530 return this.addFill();
7532 return this.addText(el);
7535 if(el.tagName){ // element
7536 return this.addElement(el);
7538 if(typeof el == "object"){ // must be button config?
7539 return this.addButton(el);
7547 * Add an Xtype element
7548 * @param {Object} xtype Xtype Object
7549 * @return {Object} created Object
7551 addxtype : function(e){
7556 * Returns the Element for this toolbar.
7557 * @return {Roo.Element}
7565 * @return {Roo.Toolbar.Item} The separator item
7567 addSeparator : function(){
7568 return this.addItem(new Roo.Toolbar.Separator());
7572 * Adds a spacer element
7573 * @return {Roo.Toolbar.Spacer} The spacer item
7575 addSpacer : function(){
7576 return this.addItem(new Roo.Toolbar.Spacer());
7580 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7581 * @return {Roo.Toolbar.Fill} The fill item
7583 addFill : function(){
7584 return this.addItem(new Roo.Toolbar.Fill());
7588 * Adds any standard HTML element to the toolbar
7589 * @param {String/HTMLElement/Element} el The element or id of the element to add
7590 * @return {Roo.Toolbar.Item} The element's item
7592 addElement : function(el){
7593 return this.addItem(new Roo.Toolbar.Item(el));
7596 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7597 * @type Roo.util.MixedCollection
7602 * Adds any Toolbar.Item or subclass
7603 * @param {Roo.Toolbar.Item} item
7604 * @return {Roo.Toolbar.Item} The item
7606 addItem : function(item){
7607 var td = this.nextBlock();
7609 this.items.add(item);
7614 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7615 * @param {Object/Array} config A button config or array of configs
7616 * @return {Roo.Toolbar.Button/Array}
7618 addButton : function(config){
7619 if(config instanceof Array){
7621 for(var i = 0, len = config.length; i < len; i++) {
7622 buttons.push(this.addButton(config[i]));
7627 if(!(config instanceof Roo.Toolbar.Button)){
7629 new Roo.Toolbar.SplitButton(config) :
7630 new Roo.Toolbar.Button(config);
7632 var td = this.nextBlock();
7639 * Adds text to the toolbar
7640 * @param {String} text The text to add
7641 * @return {Roo.Toolbar.Item} The element's item
7643 addText : function(text){
7644 return this.addItem(new Roo.Toolbar.TextItem(text));
7648 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7649 * @param {Number} index The index where the item is to be inserted
7650 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7651 * @return {Roo.Toolbar.Button/Item}
7653 insertButton : function(index, item){
7654 if(item instanceof Array){
7656 for(var i = 0, len = item.length; i < len; i++) {
7657 buttons.push(this.insertButton(index + i, item[i]));
7661 if (!(item instanceof Roo.Toolbar.Button)){
7662 item = new Roo.Toolbar.Button(item);
7664 var td = document.createElement("td");
7665 this.tr.insertBefore(td, this.tr.childNodes[index]);
7667 this.items.insert(index, item);
7672 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7673 * @param {Object} config
7674 * @return {Roo.Toolbar.Item} The element's item
7676 addDom : function(config, returnEl){
7677 var td = this.nextBlock();
7678 Roo.DomHelper.overwrite(td, config);
7679 var ti = new Roo.Toolbar.Item(td.firstChild);
7686 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7687 * @type Roo.util.MixedCollection
7692 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7693 * Note: the field should not have been rendered yet. For a field that has already been
7694 * rendered, use {@link #addElement}.
7695 * @param {Roo.form.Field} field
7696 * @return {Roo.ToolbarItem}
7700 addField : function(field) {
7703 this.fields = new Roo.util.MixedCollection(false, function(o){
7704 return o.id || ("item" + (++autoId));
7709 var td = this.nextBlock();
7711 var ti = new Roo.Toolbar.Item(td.firstChild);
7714 this.fields.add(field);
7725 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7726 this.el.child('div').hide();
7734 this.el.child('div').show();
7738 nextBlock : function(){
7739 var td = document.createElement("td");
7740 this.tr.appendChild(td);
7745 destroy : function(){
7746 if(this.items){ // rendered?
7747 Roo.destroy.apply(Roo, this.items.items);
7749 if(this.fields){ // rendered?
7750 Roo.destroy.apply(Roo, this.fields.items);
7752 Roo.Element.uncache(this.el, this.tr);
7757 * @class Roo.Toolbar.Item
7758 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7760 * Creates a new Item
7761 * @param {HTMLElement} el
7763 Roo.Toolbar.Item = function(el){
7765 if (typeof (el.xtype) != 'undefined') {
7770 this.el = Roo.getDom(el);
7771 this.id = Roo.id(this.el);
7772 this.hidden = false;
7777 * Fires when the button is rendered
7778 * @param {Button} this
7782 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7784 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7785 //Roo.Toolbar.Item.prototype = {
7788 * Get this item's HTML Element
7789 * @return {HTMLElement}
7796 render : function(td){
7799 td.appendChild(this.el);
7801 this.fireEvent('render', this);
7805 * Removes and destroys this item.
7807 destroy : function(){
7808 this.td.parentNode.removeChild(this.td);
7815 this.hidden = false;
7816 this.td.style.display = "";
7824 this.td.style.display = "none";
7828 * Convenience function for boolean show/hide.
7829 * @param {Boolean} visible true to show/false to hide
7831 setVisible: function(visible){
7840 * Try to focus this item.
7843 Roo.fly(this.el).focus();
7847 * Disables this item.
7849 disable : function(){
7850 Roo.fly(this.td).addClass("x-item-disabled");
7851 this.disabled = true;
7852 this.el.disabled = true;
7856 * Enables this item.
7858 enable : function(){
7859 Roo.fly(this.td).removeClass("x-item-disabled");
7860 this.disabled = false;
7861 this.el.disabled = false;
7867 * @class Roo.Toolbar.Separator
7868 * @extends Roo.Toolbar.Item
7869 * A simple toolbar separator class
7871 * Creates a new Separator
7873 Roo.Toolbar.Separator = function(cfg){
7875 var s = document.createElement("span");
7876 s.className = "ytb-sep";
7881 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7883 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7885 disable:Roo.emptyFn,
7890 * @class Roo.Toolbar.Spacer
7891 * @extends Roo.Toolbar.Item
7892 * A simple element that adds extra horizontal space to a toolbar.
7894 * Creates a new Spacer
7896 Roo.Toolbar.Spacer = function(cfg){
7897 var s = document.createElement("div");
7898 s.className = "ytb-spacer";
7902 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7904 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7906 disable:Roo.emptyFn,
7911 * @class Roo.Toolbar.Fill
7912 * @extends Roo.Toolbar.Spacer
7913 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7915 * Creates a new Spacer
7917 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7919 render : function(td){
7920 td.style.width = '100%';
7921 Roo.Toolbar.Fill.superclass.render.call(this, td);
7926 * @class Roo.Toolbar.TextItem
7927 * @extends Roo.Toolbar.Item
7928 * A simple class that renders text directly into a toolbar.
7930 * Creates a new TextItem
7931 * @param {String} text
7933 Roo.Toolbar.TextItem = function(cfg){
7934 var text = cfg || "";
7935 if (typeof(cfg) == 'object') {
7936 text = cfg.text || "";
7940 var s = document.createElement("span");
7941 s.className = "ytb-text";
7947 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7949 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7953 disable:Roo.emptyFn,
7958 * @class Roo.Toolbar.Button
7959 * @extends Roo.Button
7960 * A button that renders into a toolbar.
7962 * Creates a new Button
7963 * @param {Object} config A standard {@link Roo.Button} config object
7965 Roo.Toolbar.Button = function(config){
7966 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7968 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7969 render : function(td){
7971 Roo.Toolbar.Button.superclass.render.call(this, td);
7975 * Removes and destroys this button
7977 destroy : function(){
7978 Roo.Toolbar.Button.superclass.destroy.call(this);
7979 this.td.parentNode.removeChild(this.td);
7986 this.hidden = false;
7987 this.td.style.display = "";
7995 this.td.style.display = "none";
7999 * Disables this item
8001 disable : function(){
8002 Roo.fly(this.td).addClass("x-item-disabled");
8003 this.disabled = true;
8009 enable : function(){
8010 Roo.fly(this.td).removeClass("x-item-disabled");
8011 this.disabled = false;
8015 Roo.ToolbarButton = Roo.Toolbar.Button;
8018 * @class Roo.Toolbar.SplitButton
8019 * @extends Roo.SplitButton
8020 * A menu button that renders into a toolbar.
8022 * Creates a new SplitButton
8023 * @param {Object} config A standard {@link Roo.SplitButton} config object
8025 Roo.Toolbar.SplitButton = function(config){
8026 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8028 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8029 render : function(td){
8031 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8035 * Removes and destroys this button
8037 destroy : function(){
8038 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8039 this.td.parentNode.removeChild(this.td);
8046 this.hidden = false;
8047 this.td.style.display = "";
8055 this.td.style.display = "none";
8060 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8062 * Ext JS Library 1.1.1
8063 * Copyright(c) 2006-2007, Ext JS, LLC.
8065 * Originally Released Under LGPL - original licence link has changed is not relivant.
8068 * <script type="text/javascript">
8072 * @class Roo.PagingToolbar
8073 * @extends Roo.Toolbar
8074 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8076 * Create a new PagingToolbar
8077 * @param {Object} config The config object
8079 Roo.PagingToolbar = function(el, ds, config)
8081 // old args format still supported... - xtype is prefered..
8082 if (typeof(el) == 'object' && el.xtype) {
8083 // created from xtype...
8086 el = config.container;
8090 items = config.items;
8094 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8097 this.renderButtons(this.el);
8100 // supprot items array.
8102 Roo.each(items, function(e) {
8103 this.add(Roo.factory(e));
8108 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8110 * @cfg {Roo.data.Store} dataSource
8111 * The underlying data store providing the paged data
8114 * @cfg {String/HTMLElement/Element} container
8115 * container The id or element that will contain the toolbar
8118 * @cfg {Boolean} displayInfo
8119 * True to display the displayMsg (defaults to false)
8122 * @cfg {Number} pageSize
8123 * The number of records to display per page (defaults to 20)
8127 * @cfg {String} displayMsg
8128 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8130 displayMsg : 'Displaying {0} - {1} of {2}',
8132 * @cfg {String} emptyMsg
8133 * The message to display when no records are found (defaults to "No data to display")
8135 emptyMsg : 'No data to display',
8137 * Customizable piece of the default paging text (defaults to "Page")
8140 beforePageText : "Page",
8142 * Customizable piece of the default paging text (defaults to "of %0")
8145 afterPageText : "of {0}",
8147 * Customizable piece of the default paging text (defaults to "First Page")
8150 firstText : "First Page",
8152 * Customizable piece of the default paging text (defaults to "Previous Page")
8155 prevText : "Previous Page",
8157 * Customizable piece of the default paging text (defaults to "Next Page")
8160 nextText : "Next Page",
8162 * Customizable piece of the default paging text (defaults to "Last Page")
8165 lastText : "Last Page",
8167 * Customizable piece of the default paging text (defaults to "Refresh")
8170 refreshText : "Refresh",
8173 renderButtons : function(el){
8174 Roo.PagingToolbar.superclass.render.call(this, el);
8175 this.first = this.addButton({
8176 tooltip: this.firstText,
8177 cls: "x-btn-icon x-grid-page-first",
8179 handler: this.onClick.createDelegate(this, ["first"])
8181 this.prev = this.addButton({
8182 tooltip: this.prevText,
8183 cls: "x-btn-icon x-grid-page-prev",
8185 handler: this.onClick.createDelegate(this, ["prev"])
8187 //this.addSeparator();
8188 this.add(this.beforePageText);
8189 this.field = Roo.get(this.addDom({
8194 cls: "x-grid-page-number"
8196 this.field.on("keydown", this.onPagingKeydown, this);
8197 this.field.on("focus", function(){this.dom.select();});
8198 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8199 this.field.setHeight(18);
8200 //this.addSeparator();
8201 this.next = this.addButton({
8202 tooltip: this.nextText,
8203 cls: "x-btn-icon x-grid-page-next",
8205 handler: this.onClick.createDelegate(this, ["next"])
8207 this.last = this.addButton({
8208 tooltip: this.lastText,
8209 cls: "x-btn-icon x-grid-page-last",
8211 handler: this.onClick.createDelegate(this, ["last"])
8213 //this.addSeparator();
8214 this.loading = this.addButton({
8215 tooltip: this.refreshText,
8216 cls: "x-btn-icon x-grid-loading",
8217 handler: this.onClick.createDelegate(this, ["refresh"])
8220 if(this.displayInfo){
8221 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8226 updateInfo : function(){
8228 var count = this.ds.getCount();
8229 var msg = count == 0 ?
8233 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
8235 this.displayEl.update(msg);
8240 onLoad : function(ds, r, o){
8241 this.cursor = o.params ? o.params.start : 0;
8242 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8244 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8245 this.field.dom.value = ap;
8246 this.first.setDisabled(ap == 1);
8247 this.prev.setDisabled(ap == 1);
8248 this.next.setDisabled(ap == ps);
8249 this.last.setDisabled(ap == ps);
8250 this.loading.enable();
8255 getPageData : function(){
8256 var total = this.ds.getTotalCount();
8259 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8260 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8265 onLoadError : function(){
8266 this.loading.enable();
8270 onPagingKeydown : function(e){
8272 var d = this.getPageData();
8274 var v = this.field.dom.value, pageNum;
8275 if(!v || isNaN(pageNum = parseInt(v, 10))){
8276 this.field.dom.value = d.activePage;
8279 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8280 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8283 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))
8285 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8286 this.field.dom.value = pageNum;
8287 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8290 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8292 var v = this.field.dom.value, pageNum;
8293 var increment = (e.shiftKey) ? 10 : 1;
8294 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8297 if(!v || isNaN(pageNum = parseInt(v, 10))) {
8298 this.field.dom.value = d.activePage;
8301 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8303 this.field.dom.value = parseInt(v, 10) + increment;
8304 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8305 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8312 beforeLoad : function(){
8314 this.loading.disable();
8319 onClick : function(which){
8323 ds.load({params:{start: 0, limit: this.pageSize}});
8326 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8329 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8332 var total = ds.getTotalCount();
8333 var extra = total % this.pageSize;
8334 var lastStart = extra ? (total - extra) : total-this.pageSize;
8335 ds.load({params:{start: lastStart, limit: this.pageSize}});
8338 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8344 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8345 * @param {Roo.data.Store} store The data store to unbind
8347 unbind : function(ds){
8348 ds.un("beforeload", this.beforeLoad, this);
8349 ds.un("load", this.onLoad, this);
8350 ds.un("loadexception", this.onLoadError, this);
8351 ds.un("remove", this.updateInfo, this);
8352 ds.un("add", this.updateInfo, this);
8353 this.ds = undefined;
8357 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8358 * @param {Roo.data.Store} store The data store to bind
8360 bind : function(ds){
8361 ds.on("beforeload", this.beforeLoad, this);
8362 ds.on("load", this.onLoad, this);
8363 ds.on("loadexception", this.onLoadError, this);
8364 ds.on("remove", this.updateInfo, this);
8365 ds.on("add", this.updateInfo, this);
8370 * Ext JS Library 1.1.1
8371 * Copyright(c) 2006-2007, Ext JS, LLC.
8373 * Originally Released Under LGPL - original licence link has changed is not relivant.
8376 * <script type="text/javascript">
8380 * @class Roo.Resizable
8381 * @extends Roo.util.Observable
8382 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8383 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8384 * 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
8385 * the element will be wrapped for you automatically.</p>
8386 * <p>Here is the list of valid resize handles:</p>
8389 ------ -------------------
8398 'hd' horizontal drag
8401 * <p>Here's an example showing the creation of a typical Resizable:</p>
8403 var resizer = new Roo.Resizable("element-id", {
8411 resizer.on("resize", myHandler);
8413 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8414 * resizer.east.setDisplayed(false);</p>
8415 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8416 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8417 * resize operation's new size (defaults to [0, 0])
8418 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8419 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8420 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8421 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8422 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8423 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8424 * @cfg {Number} width The width of the element in pixels (defaults to null)
8425 * @cfg {Number} height The height of the element in pixels (defaults to null)
8426 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8427 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8428 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8429 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8430 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8431 * in favor of the handles config option (defaults to false)
8432 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8433 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8434 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8435 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8436 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8437 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8438 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8439 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8440 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8441 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8442 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8444 * Create a new resizable component
8445 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8446 * @param {Object} config configuration options
8448 Roo.Resizable = function(el, config)
8450 this.el = Roo.get(el);
8452 if(config && config.wrap){
8453 config.resizeChild = this.el;
8454 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8455 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8456 this.el.setStyle("overflow", "hidden");
8457 this.el.setPositioning(config.resizeChild.getPositioning());
8458 config.resizeChild.clearPositioning();
8459 if(!config.width || !config.height){
8460 var csize = config.resizeChild.getSize();
8461 this.el.setSize(csize.width, csize.height);
8463 if(config.pinned && !config.adjustments){
8464 config.adjustments = "auto";
8468 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8469 this.proxy.unselectable();
8470 this.proxy.enableDisplayMode('block');
8472 Roo.apply(this, config);
8475 this.disableTrackOver = true;
8476 this.el.addClass("x-resizable-pinned");
8478 // if the element isn't positioned, make it relative
8479 var position = this.el.getStyle("position");
8480 if(position != "absolute" && position != "fixed"){
8481 this.el.setStyle("position", "relative");
8483 if(!this.handles){ // no handles passed, must be legacy style
8484 this.handles = 's,e,se';
8485 if(this.multiDirectional){
8486 this.handles += ',n,w';
8489 if(this.handles == "all"){
8490 this.handles = "n s e w ne nw se sw";
8492 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8493 var ps = Roo.Resizable.positions;
8494 for(var i = 0, len = hs.length; i < len; i++){
8495 if(hs[i] && ps[hs[i]]){
8496 var pos = ps[hs[i]];
8497 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8501 this.corner = this.southeast;
8503 // updateBox = the box can move..
8504 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8505 this.updateBox = true;
8508 this.activeHandle = null;
8510 if(this.resizeChild){
8511 if(typeof this.resizeChild == "boolean"){
8512 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8514 this.resizeChild = Roo.get(this.resizeChild, true);
8518 if(this.adjustments == "auto"){
8519 var rc = this.resizeChild;
8520 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8521 if(rc && (hw || hn)){
8522 rc.position("relative");
8523 rc.setLeft(hw ? hw.el.getWidth() : 0);
8524 rc.setTop(hn ? hn.el.getHeight() : 0);
8526 this.adjustments = [
8527 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8528 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8533 this.dd = this.dynamic ?
8534 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8535 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8541 * @event beforeresize
8542 * Fired before resize is allowed. Set enabled to false to cancel resize.
8543 * @param {Roo.Resizable} this
8544 * @param {Roo.EventObject} e The mousedown event
8546 "beforeresize" : true,
8550 * @param {Roo.Resizable} this
8551 * @param {Number} x The new x position
8552 * @param {Number} y The new y position
8553 * @param {Number} w The new w width
8554 * @param {Number} h The new h hight
8555 * @param {Roo.EventObject} e The mouseup event
8560 * Fired after a resize.
8561 * @param {Roo.Resizable} this
8562 * @param {Number} width The new width
8563 * @param {Number} height The new height
8564 * @param {Roo.EventObject} e The mouseup event
8569 if(this.width !== null && this.height !== null){
8570 this.resizeTo(this.width, this.height);
8572 this.updateChildSize();
8575 this.el.dom.style.zoom = 1;
8577 Roo.Resizable.superclass.constructor.call(this);
8580 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8581 resizeChild : false,
8582 adjustments : [0, 0],
8592 multiDirectional : false,
8593 disableTrackOver : false,
8594 easing : 'easeOutStrong',
8596 heightIncrement : 0,
8600 preserveRatio : false,
8607 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8609 constrainTo: undefined,
8611 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8613 resizeRegion: undefined,
8617 * Perform a manual resize
8618 * @param {Number} width
8619 * @param {Number} height
8621 resizeTo : function(width, height){
8622 this.el.setSize(width, height);
8623 this.updateChildSize();
8624 this.fireEvent("resize", this, width, height, null);
8628 startSizing : function(e, handle){
8629 this.fireEvent("beforeresize", this, e);
8630 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8633 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8634 this.overlay.unselectable();
8635 this.overlay.enableDisplayMode("block");
8636 this.overlay.on("mousemove", this.onMouseMove, this);
8637 this.overlay.on("mouseup", this.onMouseUp, this);
8639 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8641 this.resizing = true;
8642 this.startBox = this.el.getBox();
8643 this.startPoint = e.getXY();
8644 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8645 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8647 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8648 this.overlay.show();
8650 if(this.constrainTo) {
8651 var ct = Roo.get(this.constrainTo);
8652 this.resizeRegion = ct.getRegion().adjust(
8653 ct.getFrameWidth('t'),
8654 ct.getFrameWidth('l'),
8655 -ct.getFrameWidth('b'),
8656 -ct.getFrameWidth('r')
8660 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8662 this.proxy.setBox(this.startBox);
8664 this.proxy.setStyle('visibility', 'visible');
8670 onMouseDown : function(handle, e){
8673 this.activeHandle = handle;
8674 this.startSizing(e, handle);
8679 onMouseUp : function(e){
8680 var size = this.resizeElement();
8681 this.resizing = false;
8683 this.overlay.hide();
8685 this.fireEvent("resize", this, size.width, size.height, e);
8689 updateChildSize : function(){
8691 if(this.resizeChild){
8693 var child = this.resizeChild;
8694 var adj = this.adjustments;
8695 if(el.dom.offsetWidth){
8696 var b = el.getSize(true);
8697 child.setSize(b.width+adj[0], b.height+adj[1]);
8699 // Second call here for IE
8700 // The first call enables instant resizing and
8701 // the second call corrects scroll bars if they
8704 setTimeout(function(){
8705 if(el.dom.offsetWidth){
8706 var b = el.getSize(true);
8707 child.setSize(b.width+adj[0], b.height+adj[1]);
8715 snap : function(value, inc, min){
8716 if(!inc || !value) {
8719 var newValue = value;
8720 var m = value % inc;
8723 newValue = value + (inc-m);
8725 newValue = value - m;
8728 return Math.max(min, newValue);
8732 resizeElement : function(){
8733 var box = this.proxy.getBox();
8735 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8737 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8739 this.updateChildSize();
8747 constrain : function(v, diff, m, mx){
8750 }else if(v - diff > mx){
8757 onMouseMove : function(e){
8760 try{// try catch so if something goes wrong the user doesn't get hung
8762 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8766 //var curXY = this.startPoint;
8767 var curSize = this.curSize || this.startBox;
8768 var x = this.startBox.x, y = this.startBox.y;
8770 var w = curSize.width, h = curSize.height;
8772 var mw = this.minWidth, mh = this.minHeight;
8773 var mxw = this.maxWidth, mxh = this.maxHeight;
8774 var wi = this.widthIncrement;
8775 var hi = this.heightIncrement;
8777 var eventXY = e.getXY();
8778 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8779 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8781 var pos = this.activeHandle.position;
8786 w = Math.min(Math.max(mw, w), mxw);
8791 h = Math.min(Math.max(mh, h), mxh);
8796 w = Math.min(Math.max(mw, w), mxw);
8797 h = Math.min(Math.max(mh, h), mxh);
8800 diffY = this.constrain(h, diffY, mh, mxh);
8807 var adiffX = Math.abs(diffX);
8808 var sub = (adiffX % wi); // how much
8809 if (sub > (wi/2)) { // far enough to snap
8810 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8812 // remove difference..
8813 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8817 x = Math.max(this.minX, x);
8820 diffX = this.constrain(w, diffX, mw, mxw);
8826 w = Math.min(Math.max(mw, w), mxw);
8827 diffY = this.constrain(h, diffY, mh, mxh);
8832 diffX = this.constrain(w, diffX, mw, mxw);
8833 diffY = this.constrain(h, diffY, mh, mxh);
8840 diffX = this.constrain(w, diffX, mw, mxw);
8842 h = Math.min(Math.max(mh, h), mxh);
8848 var sw = this.snap(w, wi, mw);
8849 var sh = this.snap(h, hi, mh);
8850 if(sw != w || sh != h){
8873 if(this.preserveRatio){
8878 h = Math.min(Math.max(mh, h), mxh);
8883 w = Math.min(Math.max(mw, w), mxw);
8888 w = Math.min(Math.max(mw, w), mxw);
8894 w = Math.min(Math.max(mw, w), mxw);
8900 h = Math.min(Math.max(mh, h), mxh);
8908 h = Math.min(Math.max(mh, h), mxh);
8918 h = Math.min(Math.max(mh, h), mxh);
8926 if (pos == 'hdrag') {
8929 this.proxy.setBounds(x, y, w, h);
8931 this.resizeElement();
8935 this.fireEvent("resizing", this, x, y, w, h, e);
8939 handleOver : function(){
8941 this.el.addClass("x-resizable-over");
8946 handleOut : function(){
8948 this.el.removeClass("x-resizable-over");
8953 * Returns the element this component is bound to.
8954 * @return {Roo.Element}
8961 * Returns the resizeChild element (or null).
8962 * @return {Roo.Element}
8964 getResizeChild : function(){
8965 return this.resizeChild;
8967 groupHandler : function()
8972 * Destroys this resizable. If the element was wrapped and
8973 * removeEl is not true then the element remains.
8974 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8976 destroy : function(removeEl){
8977 this.proxy.remove();
8979 this.overlay.removeAllListeners();
8980 this.overlay.remove();
8982 var ps = Roo.Resizable.positions;
8984 if(typeof ps[k] != "function" && this[ps[k]]){
8985 var h = this[ps[k]];
8986 h.el.removeAllListeners();
8998 // hash to map config positions to true positions
8999 Roo.Resizable.positions = {
9000 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
9005 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9007 // only initialize the template if resizable is used
9008 var tpl = Roo.DomHelper.createTemplate(
9009 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9012 Roo.Resizable.Handle.prototype.tpl = tpl;
9014 this.position = pos;
9016 // show north drag fro topdra
9017 var handlepos = pos == 'hdrag' ? 'north' : pos;
9019 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9020 if (pos == 'hdrag') {
9021 this.el.setStyle('cursor', 'pointer');
9023 this.el.unselectable();
9025 this.el.setOpacity(0);
9027 this.el.on("mousedown", this.onMouseDown, this);
9028 if(!disableTrackOver){
9029 this.el.on("mouseover", this.onMouseOver, this);
9030 this.el.on("mouseout", this.onMouseOut, this);
9035 Roo.Resizable.Handle.prototype = {
9036 afterResize : function(rz){
9041 onMouseDown : function(e){
9042 this.rz.onMouseDown(this, e);
9045 onMouseOver : function(e){
9046 this.rz.handleOver(this, e);
9049 onMouseOut : function(e){
9050 this.rz.handleOut(this, e);
9054 * Ext JS Library 1.1.1
9055 * Copyright(c) 2006-2007, Ext JS, LLC.
9057 * Originally Released Under LGPL - original licence link has changed is not relivant.
9060 * <script type="text/javascript">
9065 * @extends Roo.Component
9066 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9068 * Create a new Editor
9069 * @param {Roo.form.Field} field The Field object (or descendant)
9070 * @param {Object} config The config object
9072 Roo.Editor = function(field, config){
9073 Roo.Editor.superclass.constructor.call(this, config);
9077 * @event beforestartedit
9078 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
9079 * false from the handler of this event.
9080 * @param {Editor} this
9081 * @param {Roo.Element} boundEl The underlying element bound to this editor
9082 * @param {Mixed} value The field value being set
9084 "beforestartedit" : true,
9087 * Fires when this editor is displayed
9088 * @param {Roo.Element} boundEl The underlying element bound to this editor
9089 * @param {Mixed} value The starting field value
9093 * @event beforecomplete
9094 * Fires after a change has been made to the field, but before the change is reflected in the underlying
9095 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
9096 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9097 * event will not fire since no edit actually occurred.
9098 * @param {Editor} this
9099 * @param {Mixed} value The current field value
9100 * @param {Mixed} startValue The original field value
9102 "beforecomplete" : true,
9105 * Fires after editing is complete and any changed value has been written to the underlying field.
9106 * @param {Editor} this
9107 * @param {Mixed} value The current field value
9108 * @param {Mixed} startValue The original field value
9113 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9114 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9115 * @param {Roo.form.Field} this
9116 * @param {Roo.EventObject} e The event object
9122 Roo.extend(Roo.Editor, Roo.Component, {
9124 * @cfg {Boolean/String} autosize
9125 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9126 * or "height" to adopt the height only (defaults to false)
9129 * @cfg {Boolean} revertInvalid
9130 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9131 * validation fails (defaults to true)
9134 * @cfg {Boolean} ignoreNoChange
9135 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9136 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
9137 * will never be ignored.
9140 * @cfg {Boolean} hideEl
9141 * False to keep the bound element visible while the editor is displayed (defaults to true)
9144 * @cfg {Mixed} value
9145 * The data value of the underlying field (defaults to "")
9149 * @cfg {String} alignment
9150 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9154 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9155 * for bottom-right shadow (defaults to "frame")
9159 * @cfg {Boolean} constrain True to constrain the editor to the viewport
9163 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9165 completeOnEnter : false,
9167 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9169 cancelOnEsc : false,
9171 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9176 onRender : function(ct, position){
9177 this.el = new Roo.Layer({
9178 shadow: this.shadow,
9184 constrain: this.constrain
9186 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9187 if(this.field.msgTarget != 'title'){
9188 this.field.msgTarget = 'qtip';
9190 this.field.render(this.el);
9192 this.field.el.dom.setAttribute('autocomplete', 'off');
9194 this.field.on("specialkey", this.onSpecialKey, this);
9195 if(this.swallowKeys){
9196 this.field.el.swallowEvent(['keydown','keypress']);
9199 this.field.on("blur", this.onBlur, this);
9200 if(this.field.grow){
9201 this.field.on("autosize", this.el.sync, this.el, {delay:1});
9205 onSpecialKey : function(field, e)
9207 //Roo.log('editor onSpecialKey');
9208 if(this.completeOnEnter && e.getKey() == e.ENTER){
9210 this.completeEdit();
9213 // do not fire special key otherwise it might hide close the editor...
9214 if(e.getKey() == e.ENTER){
9217 if(this.cancelOnEsc && e.getKey() == e.ESC){
9221 this.fireEvent('specialkey', field, e);
9226 * Starts the editing process and shows the editor.
9227 * @param {String/HTMLElement/Element} el The element to edit
9228 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9229 * to the innerHTML of el.
9231 startEdit : function(el, value){
9233 this.completeEdit();
9235 this.boundEl = Roo.get(el);
9236 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9238 this.render(this.parentEl || document.body);
9240 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9243 this.startValue = v;
9244 this.field.setValue(v);
9246 var sz = this.boundEl.getSize();
9247 switch(this.autoSize){
9249 this.setSize(sz.width, "");
9252 this.setSize("", sz.height);
9255 this.setSize(sz.width, sz.height);
9258 this.el.alignTo(this.boundEl, this.alignment);
9259 this.editing = true;
9261 Roo.QuickTips.disable();
9267 * Sets the height and width of this editor.
9268 * @param {Number} width The new width
9269 * @param {Number} height The new height
9271 setSize : function(w, h){
9272 this.field.setSize(w, h);
9279 * Realigns the editor to the bound field based on the current alignment config value.
9281 realign : function(){
9282 this.el.alignTo(this.boundEl, this.alignment);
9286 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9287 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9289 completeEdit : function(remainVisible){
9293 var v = this.getValue();
9294 if(this.revertInvalid !== false && !this.field.isValid()){
9295 v = this.startValue;
9296 this.cancelEdit(true);
9298 if(String(v) === String(this.startValue) && this.ignoreNoChange){
9299 this.editing = false;
9303 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9304 this.editing = false;
9305 if(this.updateEl && this.boundEl){
9306 this.boundEl.update(v);
9308 if(remainVisible !== true){
9311 this.fireEvent("complete", this, v, this.startValue);
9316 onShow : function(){
9318 if(this.hideEl !== false){
9319 this.boundEl.hide();
9322 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9323 this.fixIEFocus = true;
9324 this.deferredFocus.defer(50, this);
9328 this.fireEvent("startedit", this.boundEl, this.startValue);
9331 deferredFocus : function(){
9338 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
9339 * reverted to the original starting value.
9340 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9341 * cancel (defaults to false)
9343 cancelEdit : function(remainVisible){
9345 this.setValue(this.startValue);
9346 if(remainVisible !== true){
9353 onBlur : function(){
9354 if(this.allowBlur !== true && this.editing){
9355 this.completeEdit();
9360 onHide : function(){
9362 this.completeEdit();
9366 if(this.field.collapse){
9367 this.field.collapse();
9370 if(this.hideEl !== false){
9371 this.boundEl.show();
9374 Roo.QuickTips.enable();
9379 * Sets the data value of the editor
9380 * @param {Mixed} value Any valid value supported by the underlying field
9382 setValue : function(v){
9383 this.field.setValue(v);
9387 * Gets the data value of the editor
9388 * @return {Mixed} The data value
9390 getValue : function(){
9391 return this.field.getValue();
9395 * Ext JS Library 1.1.1
9396 * Copyright(c) 2006-2007, Ext JS, LLC.
9398 * Originally Released Under LGPL - original licence link has changed is not relivant.
9401 * <script type="text/javascript">
9405 * @class Roo.BasicDialog
9406 * @extends Roo.util.Observable
9407 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9409 var dlg = new Roo.BasicDialog("my-dlg", {
9418 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9419 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9420 dlg.addButton('Cancel', dlg.hide, dlg);
9423 <b>A Dialog should always be a direct child of the body element.</b>
9424 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9425 * @cfg {String} title Default text to display in the title bar (defaults to null)
9426 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9427 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9428 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9429 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9430 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9431 * (defaults to null with no animation)
9432 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9433 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9434 * property for valid values (defaults to 'all')
9435 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9436 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9437 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9438 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9439 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9440 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9441 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9442 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9443 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9444 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9445 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9446 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9447 * draggable = true (defaults to false)
9448 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9449 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9450 * shadow (defaults to false)
9451 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9452 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9453 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9454 * @cfg {Array} buttons Array of buttons
9455 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9457 * Create a new BasicDialog.
9458 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9459 * @param {Object} config Configuration options
9461 Roo.BasicDialog = function(el, config){
9462 this.el = Roo.get(el);
9463 var dh = Roo.DomHelper;
9464 if(!this.el && config && config.autoCreate){
9465 if(typeof config.autoCreate == "object"){
9466 if(!config.autoCreate.id){
9467 config.autoCreate.id = el;
9469 this.el = dh.append(document.body,
9470 config.autoCreate, true);
9472 this.el = dh.append(document.body,
9473 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9477 el.setDisplayed(true);
9478 el.hide = this.hideAction;
9480 el.addClass("x-dlg");
9482 Roo.apply(this, config);
9484 this.proxy = el.createProxy("x-dlg-proxy");
9485 this.proxy.hide = this.hideAction;
9486 this.proxy.setOpacity(.5);
9490 el.setWidth(config.width);
9493 el.setHeight(config.height);
9495 this.size = el.getSize();
9496 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9497 this.xy = [config.x,config.y];
9499 this.xy = el.getCenterXY(true);
9501 /** The header element @type Roo.Element */
9502 this.header = el.child("> .x-dlg-hd");
9503 /** The body element @type Roo.Element */
9504 this.body = el.child("> .x-dlg-bd");
9505 /** The footer element @type Roo.Element */
9506 this.footer = el.child("> .x-dlg-ft");
9509 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9512 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9515 this.header.unselectable();
9517 this.header.update(this.title);
9519 // this element allows the dialog to be focused for keyboard event
9520 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9521 this.focusEl.swallowEvent("click", true);
9523 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9525 // wrap the body and footer for special rendering
9526 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9528 this.bwrap.dom.appendChild(this.footer.dom);
9531 this.bg = this.el.createChild({
9532 tag: "div", cls:"x-dlg-bg",
9533 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9535 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9538 if(this.autoScroll !== false && !this.autoTabs){
9539 this.body.setStyle("overflow", "auto");
9542 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9544 if(this.closable !== false){
9545 this.el.addClass("x-dlg-closable");
9546 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9547 this.close.on("click", this.closeClick, this);
9548 this.close.addClassOnOver("x-dlg-close-over");
9550 if(this.collapsible !== false){
9551 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9552 this.collapseBtn.on("click", this.collapseClick, this);
9553 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9554 this.header.on("dblclick", this.collapseClick, this);
9556 if(this.resizable !== false){
9557 this.el.addClass("x-dlg-resizable");
9558 this.resizer = new Roo.Resizable(el, {
9559 minWidth: this.minWidth || 80,
9560 minHeight:this.minHeight || 80,
9561 handles: this.resizeHandles || "all",
9564 this.resizer.on("beforeresize", this.beforeResize, this);
9565 this.resizer.on("resize", this.onResize, this);
9567 if(this.draggable !== false){
9568 el.addClass("x-dlg-draggable");
9569 if (!this.proxyDrag) {
9570 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9573 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9575 dd.setHandleElId(this.header.id);
9576 dd.endDrag = this.endMove.createDelegate(this);
9577 dd.startDrag = this.startMove.createDelegate(this);
9578 dd.onDrag = this.onDrag.createDelegate(this);
9583 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9584 this.mask.enableDisplayMode("block");
9586 this.el.addClass("x-dlg-modal");
9589 this.shadow = new Roo.Shadow({
9590 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9591 offset : this.shadowOffset
9594 this.shadowOffset = 0;
9596 if(Roo.useShims && this.shim !== false){
9597 this.shim = this.el.createShim();
9598 this.shim.hide = this.hideAction;
9607 var bts= this.buttons;
9609 Roo.each(bts, function(b) {
9618 * Fires when a key is pressed
9619 * @param {Roo.BasicDialog} this
9620 * @param {Roo.EventObject} e
9625 * Fires when this dialog is moved by the user.
9626 * @param {Roo.BasicDialog} this
9627 * @param {Number} x The new page X
9628 * @param {Number} y The new page Y
9633 * Fires when this dialog is resized by the user.
9634 * @param {Roo.BasicDialog} this
9635 * @param {Number} width The new width
9636 * @param {Number} height The new height
9641 * Fires before this dialog is hidden.
9642 * @param {Roo.BasicDialog} this
9644 "beforehide" : true,
9647 * Fires when this dialog is hidden.
9648 * @param {Roo.BasicDialog} this
9653 * Fires before this dialog is shown.
9654 * @param {Roo.BasicDialog} this
9656 "beforeshow" : true,
9659 * Fires when this dialog is shown.
9660 * @param {Roo.BasicDialog} this
9664 el.on("keydown", this.onKeyDown, this);
9665 el.on("mousedown", this.toFront, this);
9666 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9668 Roo.DialogManager.register(this);
9669 Roo.BasicDialog.superclass.constructor.call(this);
9672 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9673 shadowOffset: Roo.isIE ? 6 : 5,
9677 defaultButton: null,
9678 buttonAlign: "right",
9683 * Sets the dialog title text
9684 * @param {String} text The title text to display
9685 * @return {Roo.BasicDialog} this
9687 setTitle : function(text){
9688 this.header.update(text);
9693 closeClick : function(){
9698 collapseClick : function(){
9699 this[this.collapsed ? "expand" : "collapse"]();
9703 * Collapses the dialog to its minimized state (only the title bar is visible).
9704 * Equivalent to the user clicking the collapse dialog button.
9706 collapse : function(){
9707 if(!this.collapsed){
9708 this.collapsed = true;
9709 this.el.addClass("x-dlg-collapsed");
9710 this.restoreHeight = this.el.getHeight();
9711 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9716 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9717 * clicking the expand dialog button.
9719 expand : function(){
9721 this.collapsed = false;
9722 this.el.removeClass("x-dlg-collapsed");
9723 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9728 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9729 * @return {Roo.TabPanel} The tabs component
9731 initTabs : function(){
9732 var tabs = this.getTabs();
9733 while(tabs.getTab(0)){
9736 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9738 tabs.addTab(Roo.id(dom), dom.title);
9746 beforeResize : function(){
9747 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9751 onResize : function(){
9753 this.syncBodyHeight();
9754 this.adjustAssets();
9756 this.fireEvent("resize", this, this.size.width, this.size.height);
9760 onKeyDown : function(e){
9761 if(this.isVisible()){
9762 this.fireEvent("keydown", this, e);
9767 * Resizes the dialog.
9768 * @param {Number} width
9769 * @param {Number} height
9770 * @return {Roo.BasicDialog} this
9772 resizeTo : function(width, height){
9773 this.el.setSize(width, height);
9774 this.size = {width: width, height: height};
9775 this.syncBodyHeight();
9776 if(this.fixedcenter){
9779 if(this.isVisible()){
9781 this.adjustAssets();
9783 this.fireEvent("resize", this, width, height);
9789 * Resizes the dialog to fit the specified content size.
9790 * @param {Number} width
9791 * @param {Number} height
9792 * @return {Roo.BasicDialog} this
9794 setContentSize : function(w, h){
9795 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9796 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9797 //if(!this.el.isBorderBox()){
9798 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9799 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9802 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9803 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9805 this.resizeTo(w, h);
9810 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9811 * executed in response to a particular key being pressed while the dialog is active.
9812 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9813 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9814 * @param {Function} fn The function to call
9815 * @param {Object} scope (optional) The scope of the function
9816 * @return {Roo.BasicDialog} this
9818 addKeyListener : function(key, fn, scope){
9819 var keyCode, shift, ctrl, alt;
9820 if(typeof key == "object" && !(key instanceof Array)){
9821 keyCode = key["key"];
9822 shift = key["shift"];
9828 var handler = function(dlg, e){
9829 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9831 if(keyCode instanceof Array){
9832 for(var i = 0, len = keyCode.length; i < len; i++){
9833 if(keyCode[i] == k){
9834 fn.call(scope || window, dlg, k, e);
9840 fn.call(scope || window, dlg, k, e);
9845 this.on("keydown", handler);
9850 * Returns the TabPanel component (creates it if it doesn't exist).
9851 * Note: If you wish to simply check for the existence of tabs without creating them,
9852 * check for a null 'tabs' property.
9853 * @return {Roo.TabPanel} The tabs component
9855 getTabs : function(){
9857 this.el.addClass("x-dlg-auto-tabs");
9858 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9859 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9865 * Adds a button to the footer section of the dialog.
9866 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9867 * object or a valid Roo.DomHelper element config
9868 * @param {Function} handler The function called when the button is clicked
9869 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9870 * @return {Roo.Button} The new button
9872 addButton : function(config, handler, scope){
9873 var dh = Roo.DomHelper;
9875 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9877 if(!this.btnContainer){
9878 var tb = this.footer.createChild({
9880 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9881 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9883 this.btnContainer = tb.firstChild.firstChild.firstChild;
9888 minWidth: this.minButtonWidth,
9891 if(typeof config == "string"){
9892 bconfig.text = config;
9895 bconfig.dhconfig = config;
9897 Roo.apply(bconfig, config);
9901 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9902 bconfig.position = Math.max(0, bconfig.position);
9903 fc = this.btnContainer.childNodes[bconfig.position];
9906 var btn = new Roo.Button(
9908 this.btnContainer.insertBefore(document.createElement("td"),fc)
9909 : this.btnContainer.appendChild(document.createElement("td")),
9910 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9913 this.syncBodyHeight();
9916 * Array of all the buttons that have been added to this dialog via addButton
9921 this.buttons.push(btn);
9926 * Sets the default button to be focused when the dialog is displayed.
9927 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9928 * @return {Roo.BasicDialog} this
9930 setDefaultButton : function(btn){
9931 this.defaultButton = btn;
9936 getHeaderFooterHeight : function(safe){
9939 height += this.header.getHeight();
9942 var fm = this.footer.getMargins();
9943 height += (this.footer.getHeight()+fm.top+fm.bottom);
9945 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9946 height += this.centerBg.getPadding("tb");
9951 syncBodyHeight : function()
9953 var bd = this.body, // the text
9954 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9956 var height = this.size.height - this.getHeaderFooterHeight(false);
9957 bd.setHeight(height-bd.getMargins("tb"));
9958 var hh = this.header.getHeight();
9959 var h = this.size.height-hh;
9962 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9963 bw.setHeight(h-cb.getPadding("tb"));
9965 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9966 bd.setWidth(bw.getWidth(true));
9968 this.tabs.syncHeight();
9970 this.tabs.el.repaint();
9976 * Restores the previous state of the dialog if Roo.state is configured.
9977 * @return {Roo.BasicDialog} this
9979 restoreState : function(){
9980 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9981 if(box && box.width){
9982 this.xy = [box.x, box.y];
9983 this.resizeTo(box.width, box.height);
9989 beforeShow : function(){
9991 if(this.fixedcenter){
9992 this.xy = this.el.getCenterXY(true);
9995 Roo.get(document.body).addClass("x-body-masked");
9996 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10003 animShow : function(){
10004 var b = Roo.get(this.animateTarget).getBox();
10005 this.proxy.setSize(b.width, b.height);
10006 this.proxy.setLocation(b.x, b.y);
10008 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10009 true, .35, this.showEl.createDelegate(this));
10013 * Shows the dialog.
10014 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10015 * @return {Roo.BasicDialog} this
10017 show : function(animateTarget){
10018 if (this.fireEvent("beforeshow", this) === false){
10021 if(this.syncHeightBeforeShow){
10022 this.syncBodyHeight();
10023 }else if(this.firstShow){
10024 this.firstShow = false;
10025 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10027 this.animateTarget = animateTarget || this.animateTarget;
10028 if(!this.el.isVisible()){
10030 if(this.animateTarget && Roo.get(this.animateTarget)){
10040 showEl : function(){
10042 this.el.setXY(this.xy);
10044 this.adjustAssets(true);
10047 // IE peekaboo bug - fix found by Dave Fenwick
10051 this.fireEvent("show", this);
10055 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
10056 * dialog itself will receive focus.
10058 focus : function(){
10059 if(this.defaultButton){
10060 this.defaultButton.focus();
10062 this.focusEl.focus();
10067 constrainXY : function(){
10068 if(this.constraintoviewport !== false){
10069 if(!this.viewSize){
10070 if(this.container){
10071 var s = this.container.getSize();
10072 this.viewSize = [s.width, s.height];
10074 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10077 var s = Roo.get(this.container||document).getScroll();
10079 var x = this.xy[0], y = this.xy[1];
10080 var w = this.size.width, h = this.size.height;
10081 var vw = this.viewSize[0], vh = this.viewSize[1];
10082 // only move it if it needs it
10084 // first validate right/bottom
10085 if(x + w > vw+s.left){
10089 if(y + h > vh+s.top){
10093 // then make sure top/left isn't negative
10105 if(this.isVisible()){
10106 this.el.setLocation(x, y);
10107 this.adjustAssets();
10114 onDrag : function(){
10115 if(!this.proxyDrag){
10116 this.xy = this.el.getXY();
10117 this.adjustAssets();
10122 adjustAssets : function(doShow){
10123 var x = this.xy[0], y = this.xy[1];
10124 var w = this.size.width, h = this.size.height;
10125 if(doShow === true){
10127 this.shadow.show(this.el);
10133 if(this.shadow && this.shadow.isVisible()){
10134 this.shadow.show(this.el);
10136 if(this.shim && this.shim.isVisible()){
10137 this.shim.setBounds(x, y, w, h);
10142 adjustViewport : function(w, h){
10144 w = Roo.lib.Dom.getViewWidth();
10145 h = Roo.lib.Dom.getViewHeight();
10148 this.viewSize = [w, h];
10149 if(this.modal && this.mask.isVisible()){
10150 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10151 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10153 if(this.isVisible()){
10154 this.constrainXY();
10159 * Destroys this dialog and all its supporting elements (including any tabs, shim,
10160 * shadow, proxy, mask, etc.) Also removes all event listeners.
10161 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10163 destroy : function(removeEl){
10164 if(this.isVisible()){
10165 this.animateTarget = null;
10168 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10170 this.tabs.destroy(removeEl);
10183 for(var i = 0, len = this.buttons.length; i < len; i++){
10184 this.buttons[i].destroy();
10187 this.el.removeAllListeners();
10188 if(removeEl === true){
10189 this.el.update("");
10192 Roo.DialogManager.unregister(this);
10196 startMove : function(){
10197 if(this.proxyDrag){
10200 if(this.constraintoviewport !== false){
10201 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10206 endMove : function(){
10207 if(!this.proxyDrag){
10208 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10210 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10213 this.refreshSize();
10214 this.adjustAssets();
10216 this.fireEvent("move", this, this.xy[0], this.xy[1]);
10220 * Brings this dialog to the front of any other visible dialogs
10221 * @return {Roo.BasicDialog} this
10223 toFront : function(){
10224 Roo.DialogManager.bringToFront(this);
10229 * Sends this dialog to the back (under) of any other visible dialogs
10230 * @return {Roo.BasicDialog} this
10232 toBack : function(){
10233 Roo.DialogManager.sendToBack(this);
10238 * Centers this dialog in the viewport
10239 * @return {Roo.BasicDialog} this
10241 center : function(){
10242 var xy = this.el.getCenterXY(true);
10243 this.moveTo(xy[0], xy[1]);
10248 * Moves the dialog's top-left corner to the specified point
10249 * @param {Number} x
10250 * @param {Number} y
10251 * @return {Roo.BasicDialog} this
10253 moveTo : function(x, y){
10255 if(this.isVisible()){
10256 this.el.setXY(this.xy);
10257 this.adjustAssets();
10263 * Aligns the dialog to the specified element
10264 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10265 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10266 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10267 * @return {Roo.BasicDialog} this
10269 alignTo : function(element, position, offsets){
10270 this.xy = this.el.getAlignToXY(element, position, offsets);
10271 if(this.isVisible()){
10272 this.el.setXY(this.xy);
10273 this.adjustAssets();
10279 * Anchors an element to another element and realigns it when the window is resized.
10280 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10281 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10282 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10283 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10284 * is a number, it is used as the buffer delay (defaults to 50ms).
10285 * @return {Roo.BasicDialog} this
10287 anchorTo : function(el, alignment, offsets, monitorScroll){
10288 var action = function(){
10289 this.alignTo(el, alignment, offsets);
10291 Roo.EventManager.onWindowResize(action, this);
10292 var tm = typeof monitorScroll;
10293 if(tm != 'undefined'){
10294 Roo.EventManager.on(window, 'scroll', action, this,
10295 {buffer: tm == 'number' ? monitorScroll : 50});
10302 * Returns true if the dialog is visible
10303 * @return {Boolean}
10305 isVisible : function(){
10306 return this.el.isVisible();
10310 animHide : function(callback){
10311 var b = Roo.get(this.animateTarget).getBox();
10313 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10315 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10316 this.hideEl.createDelegate(this, [callback]));
10320 * Hides the dialog.
10321 * @param {Function} callback (optional) Function to call when the dialog is hidden
10322 * @return {Roo.BasicDialog} this
10324 hide : function(callback){
10325 if (this.fireEvent("beforehide", this) === false){
10329 this.shadow.hide();
10334 // sometimes animateTarget seems to get set.. causing problems...
10335 // this just double checks..
10336 if(this.animateTarget && Roo.get(this.animateTarget)) {
10337 this.animHide(callback);
10340 this.hideEl(callback);
10346 hideEl : function(callback){
10350 Roo.get(document.body).removeClass("x-body-masked");
10352 this.fireEvent("hide", this);
10353 if(typeof callback == "function"){
10359 hideAction : function(){
10360 this.setLeft("-10000px");
10361 this.setTop("-10000px");
10362 this.setStyle("visibility", "hidden");
10366 refreshSize : function(){
10367 this.size = this.el.getSize();
10368 this.xy = this.el.getXY();
10369 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10373 // z-index is managed by the DialogManager and may be overwritten at any time
10374 setZIndex : function(index){
10376 this.mask.setStyle("z-index", index);
10379 this.shim.setStyle("z-index", ++index);
10382 this.shadow.setZIndex(++index);
10384 this.el.setStyle("z-index", ++index);
10386 this.proxy.setStyle("z-index", ++index);
10389 this.resizer.proxy.setStyle("z-index", ++index);
10392 this.lastZIndex = index;
10396 * Returns the element for this dialog
10397 * @return {Roo.Element} The underlying dialog Element
10399 getEl : function(){
10405 * @class Roo.DialogManager
10406 * Provides global access to BasicDialogs that have been created and
10407 * support for z-indexing (layering) multiple open dialogs.
10409 Roo.DialogManager = function(){
10411 var accessList = [];
10415 var sortDialogs = function(d1, d2){
10416 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10420 var orderDialogs = function(){
10421 accessList.sort(sortDialogs);
10422 var seed = Roo.DialogManager.zseed;
10423 for(var i = 0, len = accessList.length; i < len; i++){
10424 var dlg = accessList[i];
10426 dlg.setZIndex(seed + (i*10));
10433 * The starting z-index for BasicDialogs (defaults to 9000)
10434 * @type Number The z-index value
10439 register : function(dlg){
10440 list[dlg.id] = dlg;
10441 accessList.push(dlg);
10445 unregister : function(dlg){
10446 delete list[dlg.id];
10449 if(!accessList.indexOf){
10450 for( i = 0, len = accessList.length; i < len; i++){
10451 if(accessList[i] == dlg){
10452 accessList.splice(i, 1);
10457 i = accessList.indexOf(dlg);
10459 accessList.splice(i, 1);
10465 * Gets a registered dialog by id
10466 * @param {String/Object} id The id of the dialog or a dialog
10467 * @return {Roo.BasicDialog} this
10469 get : function(id){
10470 return typeof id == "object" ? id : list[id];
10474 * Brings the specified dialog to the front
10475 * @param {String/Object} dlg The id of the dialog or a dialog
10476 * @return {Roo.BasicDialog} this
10478 bringToFront : function(dlg){
10479 dlg = this.get(dlg);
10482 dlg._lastAccess = new Date().getTime();
10489 * Sends the specified dialog to the back
10490 * @param {String/Object} dlg The id of the dialog or a dialog
10491 * @return {Roo.BasicDialog} this
10493 sendToBack : function(dlg){
10494 dlg = this.get(dlg);
10495 dlg._lastAccess = -(new Date().getTime());
10501 * Hides all dialogs
10503 hideAll : function(){
10504 for(var id in list){
10505 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10514 * @class Roo.LayoutDialog
10515 * @extends Roo.BasicDialog
10516 * Dialog which provides adjustments for working with a layout in a Dialog.
10517 * Add your necessary layout config options to the dialog's config.<br>
10518 * Example usage (including a nested layout):
10521 dialog = new Roo.LayoutDialog("download-dlg", {
10530 // layout config merges with the dialog config
10532 tabPosition: "top",
10533 alwaysShowTabs: true
10536 dialog.addKeyListener(27, dialog.hide, dialog);
10537 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10538 dialog.addButton("Build It!", this.getDownload, this);
10540 // we can even add nested layouts
10541 var innerLayout = new Roo.BorderLayout("dl-inner", {
10551 innerLayout.beginUpdate();
10552 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10553 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10554 innerLayout.endUpdate(true);
10556 var layout = dialog.getLayout();
10557 layout.beginUpdate();
10558 layout.add("center", new Roo.ContentPanel("standard-panel",
10559 {title: "Download the Source", fitToFrame:true}));
10560 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10561 {title: "Build your own roo.js"}));
10562 layout.getRegion("center").showPanel(sp);
10563 layout.endUpdate();
10567 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10568 * @param {Object} config configuration options
10570 Roo.LayoutDialog = function(el, cfg){
10573 if (typeof(cfg) == 'undefined') {
10574 config = Roo.apply({}, el);
10575 // not sure why we use documentElement here.. - it should always be body.
10576 // IE7 borks horribly if we use documentElement.
10577 // webkit also does not like documentElement - it creates a body element...
10578 el = Roo.get( document.body || document.documentElement ).createChild();
10579 //config.autoCreate = true;
10583 config.autoTabs = false;
10584 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10585 this.body.setStyle({overflow:"hidden", position:"relative"});
10586 this.layout = new Roo.BorderLayout(this.body.dom, config);
10587 this.layout.monitorWindowResize = false;
10588 this.el.addClass("x-dlg-auto-layout");
10589 // fix case when center region overwrites center function
10590 this.center = Roo.BasicDialog.prototype.center;
10591 this.on("show", this.layout.layout, this.layout, true);
10592 if (config.items) {
10593 var xitems = config.items;
10594 delete config.items;
10595 Roo.each(xitems, this.addxtype, this);
10600 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10602 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10605 endUpdate : function(){
10606 this.layout.endUpdate();
10610 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10613 beginUpdate : function(){
10614 this.layout.beginUpdate();
10618 * Get the BorderLayout for this dialog
10619 * @return {Roo.BorderLayout}
10621 getLayout : function(){
10622 return this.layout;
10625 showEl : function(){
10626 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10628 this.layout.layout();
10633 // Use the syncHeightBeforeShow config option to control this automatically
10634 syncBodyHeight : function(){
10635 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10636 if(this.layout){this.layout.layout();}
10640 * Add an xtype element (actually adds to the layout.)
10641 * @return {Object} xdata xtype object data.
10644 addxtype : function(c) {
10645 return this.layout.addxtype(c);
10649 * Ext JS Library 1.1.1
10650 * Copyright(c) 2006-2007, Ext JS, LLC.
10652 * Originally Released Under LGPL - original licence link has changed is not relivant.
10655 * <script type="text/javascript">
10659 * @class Roo.MessageBox
10660 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10664 Roo.Msg.alert('Status', 'Changes saved successfully.');
10666 // Prompt for user data:
10667 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10669 // process text value...
10673 // Show a dialog using config options:
10675 title:'Save Changes?',
10676 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10677 buttons: Roo.Msg.YESNOCANCEL,
10684 Roo.MessageBox = function(){
10685 var dlg, opt, mask, waitTimer;
10686 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10687 var buttons, activeTextEl, bwidth;
10690 var handleButton = function(button){
10692 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10696 var handleHide = function(){
10697 if(opt && opt.cls){
10698 dlg.el.removeClass(opt.cls);
10701 Roo.TaskMgr.stop(waitTimer);
10707 var updateButtons = function(b){
10710 buttons["ok"].hide();
10711 buttons["cancel"].hide();
10712 buttons["yes"].hide();
10713 buttons["no"].hide();
10714 dlg.footer.dom.style.display = 'none';
10717 dlg.footer.dom.style.display = '';
10718 for(var k in buttons){
10719 if(typeof buttons[k] != "function"){
10722 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10723 width += buttons[k].el.getWidth()+15;
10733 var handleEsc = function(d, k, e){
10734 if(opt && opt.closable !== false){
10744 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10745 * @return {Roo.BasicDialog} The BasicDialog element
10747 getDialog : function(){
10749 dlg = new Roo.BasicDialog("x-msg-box", {
10754 constraintoviewport:false,
10756 collapsible : false,
10759 width:400, height:100,
10760 buttonAlign:"center",
10761 closeClick : function(){
10762 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10763 handleButton("no");
10765 handleButton("cancel");
10769 dlg.on("hide", handleHide);
10771 dlg.addKeyListener(27, handleEsc);
10773 var bt = this.buttonText;
10774 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10775 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10776 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10777 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10778 bodyEl = dlg.body.createChild({
10780 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>'
10782 msgEl = bodyEl.dom.firstChild;
10783 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10784 textboxEl.enableDisplayMode();
10785 textboxEl.addKeyListener([10,13], function(){
10786 if(dlg.isVisible() && opt && opt.buttons){
10787 if(opt.buttons.ok){
10788 handleButton("ok");
10789 }else if(opt.buttons.yes){
10790 handleButton("yes");
10794 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10795 textareaEl.enableDisplayMode();
10796 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10797 progressEl.enableDisplayMode();
10798 var pf = progressEl.dom.firstChild;
10800 pp = Roo.get(pf.firstChild);
10801 pp.setHeight(pf.offsetHeight);
10809 * Updates the message box body text
10810 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10811 * the XHTML-compliant non-breaking space character '&#160;')
10812 * @return {Roo.MessageBox} This message box
10814 updateText : function(text){
10815 if(!dlg.isVisible() && !opt.width){
10816 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10818 msgEl.innerHTML = text || ' ';
10820 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10821 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10823 Math.min(opt.width || cw , this.maxWidth),
10824 Math.max(opt.minWidth || this.minWidth, bwidth)
10827 activeTextEl.setWidth(w);
10829 if(dlg.isVisible()){
10830 dlg.fixedcenter = false;
10832 // to big, make it scroll. = But as usual stupid IE does not support
10835 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10836 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10837 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10839 bodyEl.dom.style.height = '';
10840 bodyEl.dom.style.overflowY = '';
10843 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10845 bodyEl.dom.style.overflowX = '';
10848 dlg.setContentSize(w, bodyEl.getHeight());
10849 if(dlg.isVisible()){
10850 dlg.fixedcenter = true;
10856 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10857 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10858 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10859 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10860 * @return {Roo.MessageBox} This message box
10862 updateProgress : function(value, text){
10864 this.updateText(text);
10866 if (pp) { // weird bug on my firefox - for some reason this is not defined
10867 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10873 * Returns true if the message box is currently displayed
10874 * @return {Boolean} True if the message box is visible, else false
10876 isVisible : function(){
10877 return dlg && dlg.isVisible();
10881 * Hides the message box if it is displayed
10884 if(this.isVisible()){
10890 * Displays a new message box, or reinitializes an existing message box, based on the config options
10891 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10892 * The following config object properties are supported:
10894 Property Type Description
10895 ---------- --------------- ------------------------------------------------------------------------------------
10896 animEl String/Element An id or Element from which the message box should animate as it opens and
10897 closes (defaults to undefined)
10898 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10899 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10900 closable Boolean False to hide the top-right close button (defaults to true). Note that
10901 progress and wait dialogs will ignore this property and always hide the
10902 close button as they can only be closed programmatically.
10903 cls String A custom CSS class to apply to the message box element
10904 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10905 displayed (defaults to 75)
10906 fn Function A callback function to execute after closing the dialog. The arguments to the
10907 function will be btn (the name of the button that was clicked, if applicable,
10908 e.g. "ok"), and text (the value of the active text field, if applicable).
10909 Progress and wait dialogs will ignore this option since they do not respond to
10910 user actions and can only be closed programmatically, so any required function
10911 should be called by the same code after it closes the dialog.
10912 icon String A CSS class that provides a background image to be used as an icon for
10913 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10914 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10915 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10916 modal Boolean False to allow user interaction with the page while the message box is
10917 displayed (defaults to true)
10918 msg String A string that will replace the existing message box body text (defaults
10919 to the XHTML-compliant non-breaking space character ' ')
10920 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10921 progress Boolean True to display a progress bar (defaults to false)
10922 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10923 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10924 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10925 title String The title text
10926 value String The string value to set into the active textbox element if displayed
10927 wait Boolean True to display a progress bar (defaults to false)
10928 width Number The width of the dialog in pixels
10935 msg: 'Please enter your address:',
10937 buttons: Roo.MessageBox.OKCANCEL,
10940 animEl: 'addAddressBtn'
10943 * @param {Object} config Configuration options
10944 * @return {Roo.MessageBox} This message box
10946 show : function(options)
10949 // this causes nightmares if you show one dialog after another
10950 // especially on callbacks..
10952 if(this.isVisible()){
10955 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10956 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10957 Roo.log("New Dialog Message:" + options.msg )
10958 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10959 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10962 var d = this.getDialog();
10964 d.setTitle(opt.title || " ");
10965 d.close.setDisplayed(opt.closable !== false);
10966 activeTextEl = textboxEl;
10967 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10972 textareaEl.setHeight(typeof opt.multiline == "number" ?
10973 opt.multiline : this.defaultTextHeight);
10974 activeTextEl = textareaEl;
10983 progressEl.setDisplayed(opt.progress === true);
10984 this.updateProgress(0);
10985 activeTextEl.dom.value = opt.value || "";
10987 dlg.setDefaultButton(activeTextEl);
10989 var bs = opt.buttons;
10992 db = buttons["ok"];
10993 }else if(bs && bs.yes){
10994 db = buttons["yes"];
10996 dlg.setDefaultButton(db);
10998 bwidth = updateButtons(opt.buttons);
10999 this.updateText(opt.msg);
11001 d.el.addClass(opt.cls);
11003 d.proxyDrag = opt.proxyDrag === true;
11004 d.modal = opt.modal !== false;
11005 d.mask = opt.modal !== false ? mask : false;
11006 if(!d.isVisible()){
11007 // force it to the end of the z-index stack so it gets a cursor in FF
11008 document.body.appendChild(dlg.el.dom);
11009 d.animateTarget = null;
11010 d.show(options.animEl);
11016 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
11017 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11018 * and closing the message box when the process is complete.
11019 * @param {String} title The title bar text
11020 * @param {String} msg The message box body text
11021 * @return {Roo.MessageBox} This message box
11023 progress : function(title, msg){
11030 minWidth: this.minProgressWidth,
11037 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11038 * If a callback function is passed it will be called after the user clicks the button, and the
11039 * id of the button that was clicked will be passed as the only parameter to the callback
11040 * (could also be the top-right close button).
11041 * @param {String} title The title bar text
11042 * @param {String} msg The message box body text
11043 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11044 * @param {Object} scope (optional) The scope of the callback function
11045 * @return {Roo.MessageBox} This message box
11047 alert : function(title, msg, fn, scope){
11060 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
11061 * interaction while waiting for a long-running process to complete that does not have defined intervals.
11062 * You are responsible for closing the message box when the process is complete.
11063 * @param {String} msg The message box body text
11064 * @param {String} title (optional) The title bar text
11065 * @return {Roo.MessageBox} This message box
11067 wait : function(msg, title){
11078 waitTimer = Roo.TaskMgr.start({
11080 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11088 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11089 * If a callback function is passed it will be called after the user clicks either button, and the id of the
11090 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11091 * @param {String} title The title bar text
11092 * @param {String} msg The message box body text
11093 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11094 * @param {Object} scope (optional) The scope of the callback function
11095 * @return {Roo.MessageBox} This message box
11097 confirm : function(title, msg, fn, scope){
11101 buttons: this.YESNO,
11110 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11111 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
11112 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11113 * (could also be the top-right close button) and the text that was entered will be passed as the two
11114 * parameters to the callback.
11115 * @param {String} title The title bar text
11116 * @param {String} msg The message box body text
11117 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11118 * @param {Object} scope (optional) The scope of the callback function
11119 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11120 * property, or the height in pixels to create the textbox (defaults to false / single-line)
11121 * @return {Roo.MessageBox} This message box
11123 prompt : function(title, msg, fn, scope, multiline){
11127 buttons: this.OKCANCEL,
11132 multiline: multiline,
11139 * Button config that displays a single OK button
11144 * Button config that displays Yes and No buttons
11147 YESNO : {yes:true, no:true},
11149 * Button config that displays OK and Cancel buttons
11152 OKCANCEL : {ok:true, cancel:true},
11154 * Button config that displays Yes, No and Cancel buttons
11157 YESNOCANCEL : {yes:true, no:true, cancel:true},
11160 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11163 defaultTextHeight : 75,
11165 * The maximum width in pixels of the message box (defaults to 600)
11170 * The minimum width in pixels of the message box (defaults to 100)
11175 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
11176 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11179 minProgressWidth : 250,
11181 * An object containing the default button text strings that can be overriden for localized language support.
11182 * Supported properties are: ok, cancel, yes and no.
11183 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11196 * Shorthand for {@link Roo.MessageBox}
11198 Roo.Msg = Roo.MessageBox;/*
11200 * Ext JS Library 1.1.1
11201 * Copyright(c) 2006-2007, Ext JS, LLC.
11203 * Originally Released Under LGPL - original licence link has changed is not relivant.
11206 * <script type="text/javascript">
11209 * @class Roo.QuickTips
11210 * Provides attractive and customizable tooltips for any element.
11213 Roo.QuickTips = function(){
11214 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11215 var ce, bd, xy, dd;
11216 var visible = false, disabled = true, inited = false;
11217 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11219 var onOver = function(e){
11223 var t = e.getTarget();
11224 if(!t || t.nodeType !== 1 || t == document || t == document.body){
11227 if(ce && t == ce.el){
11228 clearTimeout(hideProc);
11231 if(t && tagEls[t.id]){
11232 tagEls[t.id].el = t;
11233 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11236 var ttp, et = Roo.fly(t);
11237 var ns = cfg.namespace;
11238 if(tm.interceptTitles && t.title){
11241 t.removeAttribute("title");
11242 e.preventDefault();
11244 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11247 showProc = show.defer(tm.showDelay, tm, [{
11249 text: ttp.replace(/\\n/g,'<br/>'),
11250 width: et.getAttributeNS(ns, cfg.width),
11251 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11252 title: et.getAttributeNS(ns, cfg.title),
11253 cls: et.getAttributeNS(ns, cfg.cls)
11258 var onOut = function(e){
11259 clearTimeout(showProc);
11260 var t = e.getTarget();
11261 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11262 hideProc = setTimeout(hide, tm.hideDelay);
11266 var onMove = function(e){
11272 if(tm.trackMouse && ce){
11277 var onDown = function(e){
11278 clearTimeout(showProc);
11279 clearTimeout(hideProc);
11281 if(tm.hideOnClick){
11284 tm.enable.defer(100, tm);
11289 var getPad = function(){
11290 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11293 var show = function(o){
11297 clearTimeout(dismissProc);
11299 if(removeCls){ // in case manually hidden
11300 el.removeClass(removeCls);
11304 el.addClass(ce.cls);
11305 removeCls = ce.cls;
11308 tipTitle.update(ce.title);
11311 tipTitle.update('');
11314 el.dom.style.width = tm.maxWidth+'px';
11315 //tipBody.dom.style.width = '';
11316 tipBodyText.update(o.text);
11317 var p = getPad(), w = ce.width;
11319 var td = tipBodyText.dom;
11320 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11321 if(aw > tm.maxWidth){
11323 }else if(aw < tm.minWidth){
11329 //tipBody.setWidth(w);
11330 el.setWidth(parseInt(w, 10) + p);
11331 if(ce.autoHide === false){
11332 close.setDisplayed(true);
11337 close.setDisplayed(false);
11343 el.avoidY = xy[1]-18;
11348 el.setStyle("visibility", "visible");
11349 el.fadeIn({callback: afterShow});
11355 var afterShow = function(){
11359 if(tm.autoDismiss && ce.autoHide !== false){
11360 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11365 var hide = function(noanim){
11366 clearTimeout(dismissProc);
11367 clearTimeout(hideProc);
11369 if(el.isVisible()){
11371 if(noanim !== true && tm.animate){
11372 el.fadeOut({callback: afterHide});
11379 var afterHide = function(){
11382 el.removeClass(removeCls);
11389 * @cfg {Number} minWidth
11390 * The minimum width of the quick tip (defaults to 40)
11394 * @cfg {Number} maxWidth
11395 * The maximum width of the quick tip (defaults to 300)
11399 * @cfg {Boolean} interceptTitles
11400 * True to automatically use the element's DOM title value if available (defaults to false)
11402 interceptTitles : false,
11404 * @cfg {Boolean} trackMouse
11405 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11407 trackMouse : false,
11409 * @cfg {Boolean} hideOnClick
11410 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11412 hideOnClick : true,
11414 * @cfg {Number} showDelay
11415 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11419 * @cfg {Number} hideDelay
11420 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11424 * @cfg {Boolean} autoHide
11425 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11426 * Used in conjunction with hideDelay.
11431 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11432 * (defaults to true). Used in conjunction with autoDismissDelay.
11434 autoDismiss : true,
11437 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11439 autoDismissDelay : 5000,
11441 * @cfg {Boolean} animate
11442 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11447 * @cfg {String} title
11448 * Title text to display (defaults to ''). This can be any valid HTML markup.
11452 * @cfg {String} text
11453 * Body text to display (defaults to ''). This can be any valid HTML markup.
11457 * @cfg {String} cls
11458 * A CSS class to apply to the base quick tip element (defaults to '').
11462 * @cfg {Number} width
11463 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11464 * minWidth or maxWidth.
11469 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11470 * or display QuickTips in a page.
11473 tm = Roo.QuickTips;
11474 cfg = tm.tagConfig;
11476 if(!Roo.isReady){ // allow calling of init() before onReady
11477 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11480 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11481 el.fxDefaults = {stopFx: true};
11482 // maximum custom styling
11483 //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>');
11484 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>');
11485 tipTitle = el.child('h3');
11486 tipTitle.enableDisplayMode("block");
11487 tipBody = el.child('div.x-tip-bd');
11488 tipBodyText = el.child('div.x-tip-bd-inner');
11489 //bdLeft = el.child('div.x-tip-bd-left');
11490 //bdRight = el.child('div.x-tip-bd-right');
11491 close = el.child('div.x-tip-close');
11492 close.enableDisplayMode("block");
11493 close.on("click", hide);
11494 var d = Roo.get(document);
11495 d.on("mousedown", onDown);
11496 d.on("mouseover", onOver);
11497 d.on("mouseout", onOut);
11498 d.on("mousemove", onMove);
11499 esc = d.addKeyListener(27, hide);
11502 dd = el.initDD("default", null, {
11503 onDrag : function(){
11507 dd.setHandleElId(tipTitle.id);
11516 * Configures a new quick tip instance and assigns it to a target element. The following config options
11519 Property Type Description
11520 ---------- --------------------- ------------------------------------------------------------------------
11521 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11523 * @param {Object} config The config object
11525 register : function(config){
11526 var cs = config instanceof Array ? config : arguments;
11527 for(var i = 0, len = cs.length; i < len; i++) {
11529 var target = c.target;
11531 if(target instanceof Array){
11532 for(var j = 0, jlen = target.length; j < jlen; j++){
11533 tagEls[target[j]] = c;
11536 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11543 * Removes this quick tip from its element and destroys it.
11544 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11546 unregister : function(el){
11547 delete tagEls[Roo.id(el)];
11551 * Enable this quick tip.
11553 enable : function(){
11554 if(inited && disabled){
11556 if(locks.length < 1){
11563 * Disable this quick tip.
11565 disable : function(){
11567 clearTimeout(showProc);
11568 clearTimeout(hideProc);
11569 clearTimeout(dismissProc);
11577 * Returns true if the quick tip is enabled, else false.
11579 isEnabled : function(){
11585 namespace : "roo", // was ext?? this may break..
11586 alt_namespace : "ext",
11587 attribute : "qtip",
11597 // backwards compat
11598 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11600 * Ext JS Library 1.1.1
11601 * Copyright(c) 2006-2007, Ext JS, LLC.
11603 * Originally Released Under LGPL - original licence link has changed is not relivant.
11606 * <script type="text/javascript">
11611 * @class Roo.tree.TreePanel
11612 * @extends Roo.data.Tree
11614 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11615 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11616 * @cfg {Boolean} enableDD true to enable drag and drop
11617 * @cfg {Boolean} enableDrag true to enable just drag
11618 * @cfg {Boolean} enableDrop true to enable just drop
11619 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11620 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11621 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11622 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11623 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11624 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11625 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11626 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11627 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11628 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11629 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11630 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11631 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11632 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11633 * @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>
11634 * @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>
11637 * @param {String/HTMLElement/Element} el The container element
11638 * @param {Object} config
11640 Roo.tree.TreePanel = function(el, config){
11642 var loader = false;
11644 root = config.root;
11645 delete config.root;
11647 if (config.loader) {
11648 loader = config.loader;
11649 delete config.loader;
11652 Roo.apply(this, config);
11653 Roo.tree.TreePanel.superclass.constructor.call(this);
11654 this.el = Roo.get(el);
11655 this.el.addClass('x-tree');
11656 //console.log(root);
11658 this.setRootNode( Roo.factory(root, Roo.tree));
11661 this.loader = Roo.factory(loader, Roo.tree);
11664 * Read-only. The id of the container element becomes this TreePanel's id.
11666 this.id = this.el.id;
11669 * @event beforeload
11670 * Fires before a node is loaded, return false to cancel
11671 * @param {Node} node The node being loaded
11673 "beforeload" : true,
11676 * Fires when a node is loaded
11677 * @param {Node} node The node that was loaded
11681 * @event textchange
11682 * Fires when the text for a node is changed
11683 * @param {Node} node The node
11684 * @param {String} text The new text
11685 * @param {String} oldText The old text
11687 "textchange" : true,
11689 * @event beforeexpand
11690 * Fires before a node is expanded, return false to cancel.
11691 * @param {Node} node The node
11692 * @param {Boolean} deep
11693 * @param {Boolean} anim
11695 "beforeexpand" : true,
11697 * @event beforecollapse
11698 * Fires before a node is collapsed, return false to cancel.
11699 * @param {Node} node The node
11700 * @param {Boolean} deep
11701 * @param {Boolean} anim
11703 "beforecollapse" : true,
11706 * Fires when a node is expanded
11707 * @param {Node} node The node
11711 * @event disabledchange
11712 * Fires when the disabled status of a node changes
11713 * @param {Node} node The node
11714 * @param {Boolean} disabled
11716 "disabledchange" : true,
11719 * Fires when a node is collapsed
11720 * @param {Node} node The node
11724 * @event beforeclick
11725 * Fires before click processing on a node. Return false to cancel the default action.
11726 * @param {Node} node The node
11727 * @param {Roo.EventObject} e The event object
11729 "beforeclick":true,
11731 * @event checkchange
11732 * Fires when a node with a checkbox's checked property changes
11733 * @param {Node} this This node
11734 * @param {Boolean} checked
11736 "checkchange":true,
11739 * Fires when a node is clicked
11740 * @param {Node} node The node
11741 * @param {Roo.EventObject} e The event object
11746 * Fires when a node is double clicked
11747 * @param {Node} node The node
11748 * @param {Roo.EventObject} e The event object
11752 * @event contextmenu
11753 * Fires when a node is right clicked
11754 * @param {Node} node The node
11755 * @param {Roo.EventObject} e The event object
11757 "contextmenu":true,
11759 * @event beforechildrenrendered
11760 * Fires right before the child nodes for a node are rendered
11761 * @param {Node} node The node
11763 "beforechildrenrendered":true,
11766 * Fires when a node starts being dragged
11767 * @param {Roo.tree.TreePanel} this
11768 * @param {Roo.tree.TreeNode} node
11769 * @param {event} e The raw browser event
11771 "startdrag" : true,
11774 * Fires when a drag operation is complete
11775 * @param {Roo.tree.TreePanel} this
11776 * @param {Roo.tree.TreeNode} node
11777 * @param {event} e The raw browser event
11782 * Fires when a dragged node is dropped on a valid DD target
11783 * @param {Roo.tree.TreePanel} this
11784 * @param {Roo.tree.TreeNode} node
11785 * @param {DD} dd The dd it was dropped on
11786 * @param {event} e The raw browser event
11790 * @event beforenodedrop
11791 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11792 * passed to handlers has the following properties:<br />
11793 * <ul style="padding:5px;padding-left:16px;">
11794 * <li>tree - The TreePanel</li>
11795 * <li>target - The node being targeted for the drop</li>
11796 * <li>data - The drag data from the drag source</li>
11797 * <li>point - The point of the drop - append, above or below</li>
11798 * <li>source - The drag source</li>
11799 * <li>rawEvent - Raw mouse event</li>
11800 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11801 * to be inserted by setting them on this object.</li>
11802 * <li>cancel - Set this to true to cancel the drop.</li>
11804 * @param {Object} dropEvent
11806 "beforenodedrop" : true,
11809 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11810 * passed to handlers has the following properties:<br />
11811 * <ul style="padding:5px;padding-left:16px;">
11812 * <li>tree - The TreePanel</li>
11813 * <li>target - The node being targeted for the drop</li>
11814 * <li>data - The drag data from the drag source</li>
11815 * <li>point - The point of the drop - append, above or below</li>
11816 * <li>source - The drag source</li>
11817 * <li>rawEvent - Raw mouse event</li>
11818 * <li>dropNode - Dropped node(s).</li>
11820 * @param {Object} dropEvent
11824 * @event nodedragover
11825 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11826 * passed to handlers has the following properties:<br />
11827 * <ul style="padding:5px;padding-left:16px;">
11828 * <li>tree - The TreePanel</li>
11829 * <li>target - The node being targeted for the drop</li>
11830 * <li>data - The drag data from the drag source</li>
11831 * <li>point - The point of the drop - append, above or below</li>
11832 * <li>source - The drag source</li>
11833 * <li>rawEvent - Raw mouse event</li>
11834 * <li>dropNode - Drop node(s) provided by the source.</li>
11835 * <li>cancel - Set this to true to signal drop not allowed.</li>
11837 * @param {Object} dragOverEvent
11839 "nodedragover" : true,
11841 * @event appendnode
11842 * Fires when append node to the tree
11843 * @param {Roo.tree.TreePanel} this
11844 * @param {Roo.tree.TreeNode} node
11845 * @param {Number} index The index of the newly appended node
11847 "appendnode" : true
11850 if(this.singleExpand){
11851 this.on("beforeexpand", this.restrictExpand, this);
11854 this.editor.tree = this;
11855 this.editor = Roo.factory(this.editor, Roo.tree);
11858 if (this.selModel) {
11859 this.selModel = Roo.factory(this.selModel, Roo.tree);
11863 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11864 rootVisible : true,
11865 animate: Roo.enableFx,
11868 hlDrop : Roo.enableFx,
11872 rendererTip: false,
11874 restrictExpand : function(node){
11875 var p = node.parentNode;
11877 if(p.expandedChild && p.expandedChild.parentNode == p){
11878 p.expandedChild.collapse();
11880 p.expandedChild = node;
11884 // private override
11885 setRootNode : function(node){
11886 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11887 if(!this.rootVisible){
11888 node.ui = new Roo.tree.RootTreeNodeUI(node);
11894 * Returns the container element for this TreePanel
11896 getEl : function(){
11901 * Returns the default TreeLoader for this TreePanel
11903 getLoader : function(){
11904 return this.loader;
11910 expandAll : function(){
11911 this.root.expand(true);
11915 * Collapse all nodes
11917 collapseAll : function(){
11918 this.root.collapse(true);
11922 * Returns the selection model used by this TreePanel
11924 getSelectionModel : function(){
11925 if(!this.selModel){
11926 this.selModel = new Roo.tree.DefaultSelectionModel();
11928 return this.selModel;
11932 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11933 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11934 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11937 getChecked : function(a, startNode){
11938 startNode = startNode || this.root;
11940 var f = function(){
11941 if(this.attributes.checked){
11942 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11945 startNode.cascade(f);
11950 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11951 * @param {String} path
11952 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11953 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11954 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11956 expandPath : function(path, attr, callback){
11957 attr = attr || "id";
11958 var keys = path.split(this.pathSeparator);
11959 var curNode = this.root;
11960 if(curNode.attributes[attr] != keys[1]){ // invalid root
11962 callback(false, null);
11967 var f = function(){
11968 if(++index == keys.length){
11970 callback(true, curNode);
11974 var c = curNode.findChild(attr, keys[index]);
11977 callback(false, curNode);
11982 c.expand(false, false, f);
11984 curNode.expand(false, false, f);
11988 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11989 * @param {String} path
11990 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11991 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11992 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11994 selectPath : function(path, attr, callback){
11995 attr = attr || "id";
11996 var keys = path.split(this.pathSeparator);
11997 var v = keys.pop();
11998 if(keys.length > 0){
11999 var f = function(success, node){
12000 if(success && node){
12001 var n = node.findChild(attr, v);
12007 }else if(callback){
12008 callback(false, n);
12012 callback(false, n);
12016 this.expandPath(keys.join(this.pathSeparator), attr, f);
12018 this.root.select();
12020 callback(true, this.root);
12025 getTreeEl : function(){
12030 * Trigger rendering of this TreePanel
12032 render : function(){
12033 if (this.innerCt) {
12034 return this; // stop it rendering more than once!!
12037 this.innerCt = this.el.createChild({tag:"ul",
12038 cls:"x-tree-root-ct " +
12039 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12041 if(this.containerScroll){
12042 Roo.dd.ScrollManager.register(this.el);
12044 if((this.enableDD || this.enableDrop) && !this.dropZone){
12046 * The dropZone used by this tree if drop is enabled
12047 * @type Roo.tree.TreeDropZone
12049 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12050 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12053 if((this.enableDD || this.enableDrag) && !this.dragZone){
12055 * The dragZone used by this tree if drag is enabled
12056 * @type Roo.tree.TreeDragZone
12058 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12059 ddGroup: this.ddGroup || "TreeDD",
12060 scroll: this.ddScroll
12063 this.getSelectionModel().init(this);
12065 Roo.log("ROOT not set in tree");
12068 this.root.render();
12069 if(!this.rootVisible){
12070 this.root.renderChildren();
12076 * Ext JS Library 1.1.1
12077 * Copyright(c) 2006-2007, Ext JS, LLC.
12079 * Originally Released Under LGPL - original licence link has changed is not relivant.
12082 * <script type="text/javascript">
12087 * @class Roo.tree.DefaultSelectionModel
12088 * @extends Roo.util.Observable
12089 * The default single selection for a TreePanel.
12090 * @param {Object} cfg Configuration
12092 Roo.tree.DefaultSelectionModel = function(cfg){
12093 this.selNode = null;
12099 * @event selectionchange
12100 * Fires when the selected node changes
12101 * @param {DefaultSelectionModel} this
12102 * @param {TreeNode} node the new selection
12104 "selectionchange" : true,
12107 * @event beforeselect
12108 * Fires before the selected node changes, return false to cancel the change
12109 * @param {DefaultSelectionModel} this
12110 * @param {TreeNode} node the new selection
12111 * @param {TreeNode} node the old selection
12113 "beforeselect" : true
12116 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12119 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12120 init : function(tree){
12122 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12123 tree.on("click", this.onNodeClick, this);
12126 onNodeClick : function(node, e){
12127 if (e.ctrlKey && this.selNode == node) {
12128 this.unselect(node);
12136 * @param {TreeNode} node The node to select
12137 * @return {TreeNode} The selected node
12139 select : function(node){
12140 var last = this.selNode;
12141 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12143 last.ui.onSelectedChange(false);
12145 this.selNode = node;
12146 node.ui.onSelectedChange(true);
12147 this.fireEvent("selectionchange", this, node, last);
12154 * @param {TreeNode} node The node to unselect
12156 unselect : function(node){
12157 if(this.selNode == node){
12158 this.clearSelections();
12163 * Clear all selections
12165 clearSelections : function(){
12166 var n = this.selNode;
12168 n.ui.onSelectedChange(false);
12169 this.selNode = null;
12170 this.fireEvent("selectionchange", this, null);
12176 * Get the selected node
12177 * @return {TreeNode} The selected node
12179 getSelectedNode : function(){
12180 return this.selNode;
12184 * Returns true if the node is selected
12185 * @param {TreeNode} node The node to check
12186 * @return {Boolean}
12188 isSelected : function(node){
12189 return this.selNode == node;
12193 * Selects the node above the selected node in the tree, intelligently walking the nodes
12194 * @return TreeNode The new selection
12196 selectPrevious : function(){
12197 var s = this.selNode || this.lastSelNode;
12201 var ps = s.previousSibling;
12203 if(!ps.isExpanded() || ps.childNodes.length < 1){
12204 return this.select(ps);
12206 var lc = ps.lastChild;
12207 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12210 return this.select(lc);
12212 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12213 return this.select(s.parentNode);
12219 * Selects the node above the selected node in the tree, intelligently walking the nodes
12220 * @return TreeNode The new selection
12222 selectNext : function(){
12223 var s = this.selNode || this.lastSelNode;
12227 if(s.firstChild && s.isExpanded()){
12228 return this.select(s.firstChild);
12229 }else if(s.nextSibling){
12230 return this.select(s.nextSibling);
12231 }else if(s.parentNode){
12233 s.parentNode.bubble(function(){
12234 if(this.nextSibling){
12235 newS = this.getOwnerTree().selModel.select(this.nextSibling);
12244 onKeyDown : function(e){
12245 var s = this.selNode || this.lastSelNode;
12246 // undesirable, but required
12251 var k = e.getKey();
12259 this.selectPrevious();
12262 e.preventDefault();
12263 if(s.hasChildNodes()){
12264 if(!s.isExpanded()){
12266 }else if(s.firstChild){
12267 this.select(s.firstChild, e);
12272 e.preventDefault();
12273 if(s.hasChildNodes() && s.isExpanded()){
12275 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12276 this.select(s.parentNode, e);
12284 * @class Roo.tree.MultiSelectionModel
12285 * @extends Roo.util.Observable
12286 * Multi selection for a TreePanel.
12287 * @param {Object} cfg Configuration
12289 Roo.tree.MultiSelectionModel = function(){
12290 this.selNodes = [];
12294 * @event selectionchange
12295 * Fires when the selected nodes change
12296 * @param {MultiSelectionModel} this
12297 * @param {Array} nodes Array of the selected nodes
12299 "selectionchange" : true
12301 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12305 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12306 init : function(tree){
12308 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12309 tree.on("click", this.onNodeClick, this);
12312 onNodeClick : function(node, e){
12313 this.select(node, e, e.ctrlKey);
12318 * @param {TreeNode} node The node to select
12319 * @param {EventObject} e (optional) An event associated with the selection
12320 * @param {Boolean} keepExisting True to retain existing selections
12321 * @return {TreeNode} The selected node
12323 select : function(node, e, keepExisting){
12324 if(keepExisting !== true){
12325 this.clearSelections(true);
12327 if(this.isSelected(node)){
12328 this.lastSelNode = node;
12331 this.selNodes.push(node);
12332 this.selMap[node.id] = node;
12333 this.lastSelNode = node;
12334 node.ui.onSelectedChange(true);
12335 this.fireEvent("selectionchange", this, this.selNodes);
12341 * @param {TreeNode} node The node to unselect
12343 unselect : function(node){
12344 if(this.selMap[node.id]){
12345 node.ui.onSelectedChange(false);
12346 var sn = this.selNodes;
12349 index = sn.indexOf(node);
12351 for(var i = 0, len = sn.length; i < len; i++){
12359 this.selNodes.splice(index, 1);
12361 delete this.selMap[node.id];
12362 this.fireEvent("selectionchange", this, this.selNodes);
12367 * Clear all selections
12369 clearSelections : function(suppressEvent){
12370 var sn = this.selNodes;
12372 for(var i = 0, len = sn.length; i < len; i++){
12373 sn[i].ui.onSelectedChange(false);
12375 this.selNodes = [];
12377 if(suppressEvent !== true){
12378 this.fireEvent("selectionchange", this, this.selNodes);
12384 * Returns true if the node is selected
12385 * @param {TreeNode} node The node to check
12386 * @return {Boolean}
12388 isSelected : function(node){
12389 return this.selMap[node.id] ? true : false;
12393 * Returns an array of the selected nodes
12396 getSelectedNodes : function(){
12397 return this.selNodes;
12400 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12402 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12404 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12407 * Ext JS Library 1.1.1
12408 * Copyright(c) 2006-2007, Ext JS, LLC.
12410 * Originally Released Under LGPL - original licence link has changed is not relivant.
12413 * <script type="text/javascript">
12417 * @class Roo.tree.TreeNode
12418 * @extends Roo.data.Node
12419 * @cfg {String} text The text for this node
12420 * @cfg {Boolean} expanded true to start the node expanded
12421 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12422 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12423 * @cfg {Boolean} disabled true to start the node disabled
12424 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12425 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12426 * @cfg {String} cls A css class to be added to the node
12427 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12428 * @cfg {String} href URL of the link used for the node (defaults to #)
12429 * @cfg {String} hrefTarget target frame for the link
12430 * @cfg {String} qtip An Ext QuickTip for the node
12431 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12432 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12433 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12434 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12435 * (defaults to undefined with no checkbox rendered)
12437 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12439 Roo.tree.TreeNode = function(attributes){
12440 attributes = attributes || {};
12441 if(typeof attributes == "string"){
12442 attributes = {text: attributes};
12444 this.childrenRendered = false;
12445 this.rendered = false;
12446 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12447 this.expanded = attributes.expanded === true;
12448 this.isTarget = attributes.isTarget !== false;
12449 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12450 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12453 * Read-only. The text for this node. To change it use setText().
12456 this.text = attributes.text;
12458 * True if this node is disabled.
12461 this.disabled = attributes.disabled === true;
12465 * @event textchange
12466 * Fires when the text for this node is changed
12467 * @param {Node} this This node
12468 * @param {String} text The new text
12469 * @param {String} oldText The old text
12471 "textchange" : true,
12473 * @event beforeexpand
12474 * Fires before this node is expanded, return false to cancel.
12475 * @param {Node} this This node
12476 * @param {Boolean} deep
12477 * @param {Boolean} anim
12479 "beforeexpand" : true,
12481 * @event beforecollapse
12482 * Fires before this node is collapsed, return false to cancel.
12483 * @param {Node} this This node
12484 * @param {Boolean} deep
12485 * @param {Boolean} anim
12487 "beforecollapse" : true,
12490 * Fires when this node is expanded
12491 * @param {Node} this This node
12495 * @event disabledchange
12496 * Fires when the disabled status of this node changes
12497 * @param {Node} this This node
12498 * @param {Boolean} disabled
12500 "disabledchange" : true,
12503 * Fires when this node is collapsed
12504 * @param {Node} this This node
12508 * @event beforeclick
12509 * Fires before click processing. Return false to cancel the default action.
12510 * @param {Node} this This node
12511 * @param {Roo.EventObject} e The event object
12513 "beforeclick":true,
12515 * @event checkchange
12516 * Fires when a node with a checkbox's checked property changes
12517 * @param {Node} this This node
12518 * @param {Boolean} checked
12520 "checkchange":true,
12523 * Fires when this node is clicked
12524 * @param {Node} this This node
12525 * @param {Roo.EventObject} e The event object
12530 * Fires when this node is double clicked
12531 * @param {Node} this This node
12532 * @param {Roo.EventObject} e The event object
12536 * @event contextmenu
12537 * Fires when this node is right clicked
12538 * @param {Node} this This node
12539 * @param {Roo.EventObject} e The event object
12541 "contextmenu":true,
12543 * @event beforechildrenrendered
12544 * Fires right before the child nodes for this node are rendered
12545 * @param {Node} this This node
12547 "beforechildrenrendered":true
12550 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12553 * Read-only. The UI for this node
12556 this.ui = new uiClass(this);
12558 // finally support items[]
12559 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12564 Roo.each(this.attributes.items, function(c) {
12565 this.appendChild(Roo.factory(c,Roo.Tree));
12567 delete this.attributes.items;
12572 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12573 preventHScroll: true,
12575 * Returns true if this node is expanded
12576 * @return {Boolean}
12578 isExpanded : function(){
12579 return this.expanded;
12583 * Returns the UI object for this node
12584 * @return {TreeNodeUI}
12586 getUI : function(){
12590 // private override
12591 setFirstChild : function(node){
12592 var of = this.firstChild;
12593 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12594 if(this.childrenRendered && of && node != of){
12595 of.renderIndent(true, true);
12598 this.renderIndent(true, true);
12602 // private override
12603 setLastChild : function(node){
12604 var ol = this.lastChild;
12605 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12606 if(this.childrenRendered && ol && node != ol){
12607 ol.renderIndent(true, true);
12610 this.renderIndent(true, true);
12614 // these methods are overridden to provide lazy rendering support
12615 // private override
12616 appendChild : function()
12618 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12619 if(node && this.childrenRendered){
12622 this.ui.updateExpandIcon();
12626 // private override
12627 removeChild : function(node){
12628 this.ownerTree.getSelectionModel().unselect(node);
12629 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12630 // if it's been rendered remove dom node
12631 if(this.childrenRendered){
12634 if(this.childNodes.length < 1){
12635 this.collapse(false, false);
12637 this.ui.updateExpandIcon();
12639 if(!this.firstChild) {
12640 this.childrenRendered = false;
12645 // private override
12646 insertBefore : function(node, refNode){
12647 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12648 if(newNode && refNode && this.childrenRendered){
12651 this.ui.updateExpandIcon();
12656 * Sets the text for this node
12657 * @param {String} text
12659 setText : function(text){
12660 var oldText = this.text;
12662 this.attributes.text = text;
12663 if(this.rendered){ // event without subscribing
12664 this.ui.onTextChange(this, text, oldText);
12666 this.fireEvent("textchange", this, text, oldText);
12670 * Triggers selection of this node
12672 select : function(){
12673 this.getOwnerTree().getSelectionModel().select(this);
12677 * Triggers deselection of this node
12679 unselect : function(){
12680 this.getOwnerTree().getSelectionModel().unselect(this);
12684 * Returns true if this node is selected
12685 * @return {Boolean}
12687 isSelected : function(){
12688 return this.getOwnerTree().getSelectionModel().isSelected(this);
12692 * Expand this node.
12693 * @param {Boolean} deep (optional) True to expand all children as well
12694 * @param {Boolean} anim (optional) false to cancel the default animation
12695 * @param {Function} callback (optional) A callback to be called when
12696 * expanding this node completes (does not wait for deep expand to complete).
12697 * Called with 1 parameter, this node.
12699 expand : function(deep, anim, callback){
12700 if(!this.expanded){
12701 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12704 if(!this.childrenRendered){
12705 this.renderChildren();
12707 this.expanded = true;
12709 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12710 this.ui.animExpand(function(){
12711 this.fireEvent("expand", this);
12712 if(typeof callback == "function"){
12716 this.expandChildNodes(true);
12718 }.createDelegate(this));
12722 this.fireEvent("expand", this);
12723 if(typeof callback == "function"){
12728 if(typeof callback == "function"){
12733 this.expandChildNodes(true);
12737 isHiddenRoot : function(){
12738 return this.isRoot && !this.getOwnerTree().rootVisible;
12742 * Collapse this node.
12743 * @param {Boolean} deep (optional) True to collapse all children as well
12744 * @param {Boolean} anim (optional) false to cancel the default animation
12746 collapse : function(deep, anim){
12747 if(this.expanded && !this.isHiddenRoot()){
12748 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12751 this.expanded = false;
12752 if((this.getOwnerTree().animate && anim !== false) || anim){
12753 this.ui.animCollapse(function(){
12754 this.fireEvent("collapse", this);
12756 this.collapseChildNodes(true);
12758 }.createDelegate(this));
12761 this.ui.collapse();
12762 this.fireEvent("collapse", this);
12766 var cs = this.childNodes;
12767 for(var i = 0, len = cs.length; i < len; i++) {
12768 cs[i].collapse(true, false);
12774 delayedExpand : function(delay){
12775 if(!this.expandProcId){
12776 this.expandProcId = this.expand.defer(delay, this);
12781 cancelExpand : function(){
12782 if(this.expandProcId){
12783 clearTimeout(this.expandProcId);
12785 this.expandProcId = false;
12789 * Toggles expanded/collapsed state of the node
12791 toggle : function(){
12800 * Ensures all parent nodes are expanded
12802 ensureVisible : function(callback){
12803 var tree = this.getOwnerTree();
12804 tree.expandPath(this.parentNode.getPath(), false, function(){
12805 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12806 Roo.callback(callback);
12807 }.createDelegate(this));
12811 * Expand all child nodes
12812 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12814 expandChildNodes : function(deep){
12815 var cs = this.childNodes;
12816 for(var i = 0, len = cs.length; i < len; i++) {
12817 cs[i].expand(deep);
12822 * Collapse all child nodes
12823 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12825 collapseChildNodes : function(deep){
12826 var cs = this.childNodes;
12827 for(var i = 0, len = cs.length; i < len; i++) {
12828 cs[i].collapse(deep);
12833 * Disables this node
12835 disable : function(){
12836 this.disabled = true;
12838 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12839 this.ui.onDisableChange(this, true);
12841 this.fireEvent("disabledchange", this, true);
12845 * Enables this node
12847 enable : function(){
12848 this.disabled = false;
12849 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12850 this.ui.onDisableChange(this, false);
12852 this.fireEvent("disabledchange", this, false);
12856 renderChildren : function(suppressEvent){
12857 if(suppressEvent !== false){
12858 this.fireEvent("beforechildrenrendered", this);
12860 var cs = this.childNodes;
12861 for(var i = 0, len = cs.length; i < len; i++){
12862 cs[i].render(true);
12864 this.childrenRendered = true;
12868 sort : function(fn, scope){
12869 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12870 if(this.childrenRendered){
12871 var cs = this.childNodes;
12872 for(var i = 0, len = cs.length; i < len; i++){
12873 cs[i].render(true);
12879 render : function(bulkRender){
12880 this.ui.render(bulkRender);
12881 if(!this.rendered){
12882 this.rendered = true;
12884 this.expanded = false;
12885 this.expand(false, false);
12891 renderIndent : function(deep, refresh){
12893 this.ui.childIndent = null;
12895 this.ui.renderIndent();
12896 if(deep === true && this.childrenRendered){
12897 var cs = this.childNodes;
12898 for(var i = 0, len = cs.length; i < len; i++){
12899 cs[i].renderIndent(true, refresh);
12905 * Ext JS Library 1.1.1
12906 * Copyright(c) 2006-2007, Ext JS, LLC.
12908 * Originally Released Under LGPL - original licence link has changed is not relivant.
12911 * <script type="text/javascript">
12915 * @class Roo.tree.AsyncTreeNode
12916 * @extends Roo.tree.TreeNode
12917 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12919 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12921 Roo.tree.AsyncTreeNode = function(config){
12922 this.loaded = false;
12923 this.loading = false;
12924 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12926 * @event beforeload
12927 * Fires before this node is loaded, return false to cancel
12928 * @param {Node} this This node
12930 this.addEvents({'beforeload':true, 'load': true});
12933 * Fires when this node is loaded
12934 * @param {Node} this This node
12937 * The loader used by this node (defaults to using the tree's defined loader)
12942 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12943 expand : function(deep, anim, callback){
12944 if(this.loading){ // if an async load is already running, waiting til it's done
12946 var f = function(){
12947 if(!this.loading){ // done loading
12948 clearInterval(timer);
12949 this.expand(deep, anim, callback);
12951 }.createDelegate(this);
12952 timer = setInterval(f, 200);
12956 if(this.fireEvent("beforeload", this) === false){
12959 this.loading = true;
12960 this.ui.beforeLoad(this);
12961 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12963 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12967 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12971 * Returns true if this node is currently loading
12972 * @return {Boolean}
12974 isLoading : function(){
12975 return this.loading;
12978 loadComplete : function(deep, anim, callback){
12979 this.loading = false;
12980 this.loaded = true;
12981 this.ui.afterLoad(this);
12982 this.fireEvent("load", this);
12983 this.expand(deep, anim, callback);
12987 * Returns true if this node has been loaded
12988 * @return {Boolean}
12990 isLoaded : function(){
12991 return this.loaded;
12994 hasChildNodes : function(){
12995 if(!this.isLeaf() && !this.loaded){
12998 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13003 * Trigger a reload for this node
13004 * @param {Function} callback
13006 reload : function(callback){
13007 this.collapse(false, false);
13008 while(this.firstChild){
13009 this.removeChild(this.firstChild);
13011 this.childrenRendered = false;
13012 this.loaded = false;
13013 if(this.isHiddenRoot()){
13014 this.expanded = false;
13016 this.expand(false, false, callback);
13020 * Ext JS Library 1.1.1
13021 * Copyright(c) 2006-2007, Ext JS, LLC.
13023 * Originally Released Under LGPL - original licence link has changed is not relivant.
13026 * <script type="text/javascript">
13030 * @class Roo.tree.TreeNodeUI
13032 * @param {Object} node The node to render
13033 * The TreeNode UI implementation is separate from the
13034 * tree implementation. Unless you are customizing the tree UI,
13035 * you should never have to use this directly.
13037 Roo.tree.TreeNodeUI = function(node){
13039 this.rendered = false;
13040 this.animating = false;
13041 this.emptyIcon = Roo.BLANK_IMAGE_URL;
13044 Roo.tree.TreeNodeUI.prototype = {
13045 removeChild : function(node){
13047 this.ctNode.removeChild(node.ui.getEl());
13051 beforeLoad : function(){
13052 this.addClass("x-tree-node-loading");
13055 afterLoad : function(){
13056 this.removeClass("x-tree-node-loading");
13059 onTextChange : function(node, text, oldText){
13061 this.textNode.innerHTML = text;
13065 onDisableChange : function(node, state){
13066 this.disabled = state;
13068 this.addClass("x-tree-node-disabled");
13070 this.removeClass("x-tree-node-disabled");
13074 onSelectedChange : function(state){
13077 this.addClass("x-tree-selected");
13080 this.removeClass("x-tree-selected");
13084 onMove : function(tree, node, oldParent, newParent, index, refNode){
13085 this.childIndent = null;
13087 var targetNode = newParent.ui.getContainer();
13088 if(!targetNode){//target not rendered
13089 this.holder = document.createElement("div");
13090 this.holder.appendChild(this.wrap);
13093 var insertBefore = refNode ? refNode.ui.getEl() : null;
13095 targetNode.insertBefore(this.wrap, insertBefore);
13097 targetNode.appendChild(this.wrap);
13099 this.node.renderIndent(true);
13103 addClass : function(cls){
13105 Roo.fly(this.elNode).addClass(cls);
13109 removeClass : function(cls){
13111 Roo.fly(this.elNode).removeClass(cls);
13115 remove : function(){
13117 this.holder = document.createElement("div");
13118 this.holder.appendChild(this.wrap);
13122 fireEvent : function(){
13123 return this.node.fireEvent.apply(this.node, arguments);
13126 initEvents : function(){
13127 this.node.on("move", this.onMove, this);
13128 var E = Roo.EventManager;
13129 var a = this.anchor;
13131 var el = Roo.fly(a, '_treeui');
13133 if(Roo.isOpera){ // opera render bug ignores the CSS
13134 el.setStyle("text-decoration", "none");
13137 el.on("click", this.onClick, this);
13138 el.on("dblclick", this.onDblClick, this);
13141 Roo.EventManager.on(this.checkbox,
13142 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13145 el.on("contextmenu", this.onContextMenu, this);
13147 var icon = Roo.fly(this.iconNode);
13148 icon.on("click", this.onClick, this);
13149 icon.on("dblclick", this.onDblClick, this);
13150 icon.on("contextmenu", this.onContextMenu, this);
13151 E.on(this.ecNode, "click", this.ecClick, this, true);
13153 if(this.node.disabled){
13154 this.addClass("x-tree-node-disabled");
13156 if(this.node.hidden){
13157 this.addClass("x-tree-node-disabled");
13159 var ot = this.node.getOwnerTree();
13160 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13161 if(dd && (!this.node.isRoot || ot.rootVisible)){
13162 Roo.dd.Registry.register(this.elNode, {
13164 handles: this.getDDHandles(),
13170 getDDHandles : function(){
13171 return [this.iconNode, this.textNode];
13176 this.wrap.style.display = "none";
13182 this.wrap.style.display = "";
13186 onContextMenu : function(e){
13187 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13188 e.preventDefault();
13190 this.fireEvent("contextmenu", this.node, e);
13194 onClick : function(e){
13199 if(this.fireEvent("beforeclick", this.node, e) !== false){
13200 if(!this.disabled && this.node.attributes.href){
13201 this.fireEvent("click", this.node, e);
13204 e.preventDefault();
13209 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13210 this.node.toggle();
13213 this.fireEvent("click", this.node, e);
13219 onDblClick : function(e){
13220 e.preventDefault();
13225 this.toggleCheck();
13227 if(!this.animating && this.node.hasChildNodes()){
13228 this.node.toggle();
13230 this.fireEvent("dblclick", this.node, e);
13233 onCheckChange : function(){
13234 var checked = this.checkbox.checked;
13235 this.node.attributes.checked = checked;
13236 this.fireEvent('checkchange', this.node, checked);
13239 ecClick : function(e){
13240 if(!this.animating && this.node.hasChildNodes()){
13241 this.node.toggle();
13245 startDrop : function(){
13246 this.dropping = true;
13249 // delayed drop so the click event doesn't get fired on a drop
13250 endDrop : function(){
13251 setTimeout(function(){
13252 this.dropping = false;
13253 }.createDelegate(this), 50);
13256 expand : function(){
13257 this.updateExpandIcon();
13258 this.ctNode.style.display = "";
13261 focus : function(){
13262 if(!this.node.preventHScroll){
13263 try{this.anchor.focus();
13265 }else if(!Roo.isIE){
13267 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13268 var l = noscroll.scrollLeft;
13269 this.anchor.focus();
13270 noscroll.scrollLeft = l;
13275 toggleCheck : function(value){
13276 var cb = this.checkbox;
13278 cb.checked = (value === undefined ? !cb.checked : value);
13284 this.anchor.blur();
13288 animExpand : function(callback){
13289 var ct = Roo.get(this.ctNode);
13291 if(!this.node.hasChildNodes()){
13292 this.updateExpandIcon();
13293 this.ctNode.style.display = "";
13294 Roo.callback(callback);
13297 this.animating = true;
13298 this.updateExpandIcon();
13301 callback : function(){
13302 this.animating = false;
13303 Roo.callback(callback);
13306 duration: this.node.ownerTree.duration || .25
13310 highlight : function(){
13311 var tree = this.node.getOwnerTree();
13312 Roo.fly(this.wrap).highlight(
13313 tree.hlColor || "C3DAF9",
13314 {endColor: tree.hlBaseColor}
13318 collapse : function(){
13319 this.updateExpandIcon();
13320 this.ctNode.style.display = "none";
13323 animCollapse : function(callback){
13324 var ct = Roo.get(this.ctNode);
13325 ct.enableDisplayMode('block');
13328 this.animating = true;
13329 this.updateExpandIcon();
13332 callback : function(){
13333 this.animating = false;
13334 Roo.callback(callback);
13337 duration: this.node.ownerTree.duration || .25
13341 getContainer : function(){
13342 return this.ctNode;
13345 getEl : function(){
13349 appendDDGhost : function(ghostNode){
13350 ghostNode.appendChild(this.elNode.cloneNode(true));
13353 getDDRepairXY : function(){
13354 return Roo.lib.Dom.getXY(this.iconNode);
13357 onRender : function(){
13361 render : function(bulkRender){
13362 var n = this.node, a = n.attributes;
13363 var targetNode = n.parentNode ?
13364 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13366 if(!this.rendered){
13367 this.rendered = true;
13369 this.renderElements(n, a, targetNode, bulkRender);
13372 if(this.textNode.setAttributeNS){
13373 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13375 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13378 this.textNode.setAttribute("ext:qtip", a.qtip);
13380 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13383 }else if(a.qtipCfg){
13384 a.qtipCfg.target = Roo.id(this.textNode);
13385 Roo.QuickTips.register(a.qtipCfg);
13388 if(!this.node.expanded){
13389 this.updateExpandIcon();
13392 if(bulkRender === true) {
13393 targetNode.appendChild(this.wrap);
13398 renderElements : function(n, a, targetNode, bulkRender)
13400 // add some indent caching, this helps performance when rendering a large tree
13401 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13402 var t = n.getOwnerTree();
13403 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13404 if (typeof(n.attributes.html) != 'undefined') {
13405 txt = n.attributes.html;
13407 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13408 var cb = typeof a.checked == 'boolean';
13409 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13410 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13411 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13412 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13413 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13414 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13415 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13416 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13417 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13418 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13421 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13422 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13423 n.nextSibling.ui.getEl(), buf.join(""));
13425 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13428 this.elNode = this.wrap.childNodes[0];
13429 this.ctNode = this.wrap.childNodes[1];
13430 var cs = this.elNode.childNodes;
13431 this.indentNode = cs[0];
13432 this.ecNode = cs[1];
13433 this.iconNode = cs[2];
13436 this.checkbox = cs[3];
13439 this.anchor = cs[index];
13440 this.textNode = cs[index].firstChild;
13443 getAnchor : function(){
13444 return this.anchor;
13447 getTextEl : function(){
13448 return this.textNode;
13451 getIconEl : function(){
13452 return this.iconNode;
13455 isChecked : function(){
13456 return this.checkbox ? this.checkbox.checked : false;
13459 updateExpandIcon : function(){
13461 var n = this.node, c1, c2;
13462 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13463 var hasChild = n.hasChildNodes();
13467 c1 = "x-tree-node-collapsed";
13468 c2 = "x-tree-node-expanded";
13471 c1 = "x-tree-node-expanded";
13472 c2 = "x-tree-node-collapsed";
13475 this.removeClass("x-tree-node-leaf");
13476 this.wasLeaf = false;
13478 if(this.c1 != c1 || this.c2 != c2){
13479 Roo.fly(this.elNode).replaceClass(c1, c2);
13480 this.c1 = c1; this.c2 = c2;
13483 // this changes non-leafs into leafs if they have no children.
13484 // it's not very rational behaviour..
13486 if(!this.wasLeaf && this.node.leaf){
13487 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13490 this.wasLeaf = true;
13493 var ecc = "x-tree-ec-icon "+cls;
13494 if(this.ecc != ecc){
13495 this.ecNode.className = ecc;
13501 getChildIndent : function(){
13502 if(!this.childIndent){
13506 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13508 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13510 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13515 this.childIndent = buf.join("");
13517 return this.childIndent;
13520 renderIndent : function(){
13523 var p = this.node.parentNode;
13525 indent = p.ui.getChildIndent();
13527 if(this.indentMarkup != indent){ // don't rerender if not required
13528 this.indentNode.innerHTML = indent;
13529 this.indentMarkup = indent;
13531 this.updateExpandIcon();
13536 Roo.tree.RootTreeNodeUI = function(){
13537 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13539 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13540 render : function(){
13541 if(!this.rendered){
13542 var targetNode = this.node.ownerTree.innerCt.dom;
13543 this.node.expanded = true;
13544 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13545 this.wrap = this.ctNode = targetNode.firstChild;
13548 collapse : function(){
13550 expand : function(){
13554 * Ext JS Library 1.1.1
13555 * Copyright(c) 2006-2007, Ext JS, LLC.
13557 * Originally Released Under LGPL - original licence link has changed is not relivant.
13560 * <script type="text/javascript">
13563 * @class Roo.tree.TreeLoader
13564 * @extends Roo.util.Observable
13565 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13566 * nodes from a specified URL. The response must be a javascript Array definition
13567 * who's elements are node definition objects. eg:
13572 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13573 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13580 * The old style respose with just an array is still supported, but not recommended.
13583 * A server request is sent, and child nodes are loaded only when a node is expanded.
13584 * The loading node's id is passed to the server under the parameter name "node" to
13585 * enable the server to produce the correct child nodes.
13587 * To pass extra parameters, an event handler may be attached to the "beforeload"
13588 * event, and the parameters specified in the TreeLoader's baseParams property:
13590 myTreeLoader.on("beforeload", function(treeLoader, node) {
13591 this.baseParams.category = node.attributes.category;
13596 * This would pass an HTTP parameter called "category" to the server containing
13597 * the value of the Node's "category" attribute.
13599 * Creates a new Treeloader.
13600 * @param {Object} config A config object containing config properties.
13602 Roo.tree.TreeLoader = function(config){
13603 this.baseParams = {};
13604 this.requestMethod = "POST";
13605 Roo.apply(this, config);
13610 * @event beforeload
13611 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13612 * @param {Object} This TreeLoader object.
13613 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13614 * @param {Object} callback The callback function specified in the {@link #load} call.
13619 * Fires when the node has been successfuly loaded.
13620 * @param {Object} This TreeLoader object.
13621 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13622 * @param {Object} response The response object containing the data from the server.
13626 * @event loadexception
13627 * Fires if the network request failed.
13628 * @param {Object} This TreeLoader object.
13629 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13630 * @param {Object} response The response object containing the data from the server.
13632 loadexception : true,
13635 * Fires before a node is created, enabling you to return custom Node types
13636 * @param {Object} This TreeLoader object.
13637 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13642 Roo.tree.TreeLoader.superclass.constructor.call(this);
13645 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13647 * @cfg {String} dataUrl The URL from which to request a Json string which
13648 * specifies an array of node definition object representing the child nodes
13652 * @cfg {String} requestMethod either GET or POST
13653 * defaults to POST (due to BC)
13657 * @cfg {Object} baseParams (optional) An object containing properties which
13658 * specify HTTP parameters to be passed to each request for child nodes.
13661 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13662 * created by this loader. If the attributes sent by the server have an attribute in this object,
13663 * they take priority.
13666 * @cfg {Object} uiProviders (optional) An object containing properties which
13668 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13669 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13670 * <i>uiProvider</i> attribute of a returned child node is a string rather
13671 * than a reference to a TreeNodeUI implementation, this that string value
13672 * is used as a property name in the uiProviders object. You can define the provider named
13673 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13678 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13679 * child nodes before loading.
13681 clearOnLoad : true,
13684 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13685 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13686 * Grid query { data : [ .....] }
13691 * @cfg {String} queryParam (optional)
13692 * Name of the query as it will be passed on the querystring (defaults to 'node')
13693 * eg. the request will be ?node=[id]
13700 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13701 * This is called automatically when a node is expanded, but may be used to reload
13702 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13703 * @param {Roo.tree.TreeNode} node
13704 * @param {Function} callback
13706 load : function(node, callback){
13707 if(this.clearOnLoad){
13708 while(node.firstChild){
13709 node.removeChild(node.firstChild);
13712 if(node.attributes.children){ // preloaded json children
13713 var cs = node.attributes.children;
13714 for(var i = 0, len = cs.length; i < len; i++){
13715 node.appendChild(this.createNode(cs[i]));
13717 if(typeof callback == "function"){
13720 }else if(this.dataUrl){
13721 this.requestData(node, callback);
13725 getParams: function(node){
13726 var buf = [], bp = this.baseParams;
13727 for(var key in bp){
13728 if(typeof bp[key] != "function"){
13729 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13732 var n = this.queryParam === false ? 'node' : this.queryParam;
13733 buf.push(n + "=", encodeURIComponent(node.id));
13734 return buf.join("");
13737 requestData : function(node, callback){
13738 if(this.fireEvent("beforeload", this, node, callback) !== false){
13739 this.transId = Roo.Ajax.request({
13740 method:this.requestMethod,
13741 url: this.dataUrl||this.url,
13742 success: this.handleResponse,
13743 failure: this.handleFailure,
13745 argument: {callback: callback, node: node},
13746 params: this.getParams(node)
13749 // if the load is cancelled, make sure we notify
13750 // the node that we are done
13751 if(typeof callback == "function"){
13757 isLoading : function(){
13758 return this.transId ? true : false;
13761 abort : function(){
13762 if(this.isLoading()){
13763 Roo.Ajax.abort(this.transId);
13768 createNode : function(attr)
13770 // apply baseAttrs, nice idea Corey!
13771 if(this.baseAttrs){
13772 Roo.applyIf(attr, this.baseAttrs);
13774 if(this.applyLoader !== false){
13775 attr.loader = this;
13777 // uiProvider = depreciated..
13779 if(typeof(attr.uiProvider) == 'string'){
13780 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13781 /** eval:var:attr */ eval(attr.uiProvider);
13783 if(typeof(this.uiProviders['default']) != 'undefined') {
13784 attr.uiProvider = this.uiProviders['default'];
13787 this.fireEvent('create', this, attr);
13789 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13791 new Roo.tree.TreeNode(attr) :
13792 new Roo.tree.AsyncTreeNode(attr));
13795 processResponse : function(response, node, callback)
13797 var json = response.responseText;
13800 var o = Roo.decode(json);
13802 if (this.root === false && typeof(o.success) != undefined) {
13803 this.root = 'data'; // the default behaviour for list like data..
13806 if (this.root !== false && !o.success) {
13807 // it's a failure condition.
13808 var a = response.argument;
13809 this.fireEvent("loadexception", this, a.node, response);
13810 Roo.log("Load failed - should have a handler really");
13816 if (this.root !== false) {
13820 for(var i = 0, len = o.length; i < len; i++){
13821 var n = this.createNode(o[i]);
13823 node.appendChild(n);
13826 if(typeof callback == "function"){
13827 callback(this, node);
13830 this.handleFailure(response);
13834 handleResponse : function(response){
13835 this.transId = false;
13836 var a = response.argument;
13837 this.processResponse(response, a.node, a.callback);
13838 this.fireEvent("load", this, a.node, response);
13841 handleFailure : function(response)
13843 // should handle failure better..
13844 this.transId = false;
13845 var a = response.argument;
13846 this.fireEvent("loadexception", this, a.node, response);
13847 if(typeof a.callback == "function"){
13848 a.callback(this, a.node);
13853 * Ext JS Library 1.1.1
13854 * Copyright(c) 2006-2007, Ext JS, LLC.
13856 * Originally Released Under LGPL - original licence link has changed is not relivant.
13859 * <script type="text/javascript">
13863 * @class Roo.tree.TreeFilter
13864 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13865 * @param {TreePanel} tree
13866 * @param {Object} config (optional)
13868 Roo.tree.TreeFilter = function(tree, config){
13870 this.filtered = {};
13871 Roo.apply(this, config);
13874 Roo.tree.TreeFilter.prototype = {
13881 * Filter the data by a specific attribute.
13882 * @param {String/RegExp} value Either string that the attribute value
13883 * should start with or a RegExp to test against the attribute
13884 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13885 * @param {TreeNode} startNode (optional) The node to start the filter at.
13887 filter : function(value, attr, startNode){
13888 attr = attr || "text";
13890 if(typeof value == "string"){
13891 var vlen = value.length;
13892 // auto clear empty filter
13893 if(vlen == 0 && this.clearBlank){
13897 value = value.toLowerCase();
13899 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13901 }else if(value.exec){ // regex?
13903 return value.test(n.attributes[attr]);
13906 throw 'Illegal filter type, must be string or regex';
13908 this.filterBy(f, null, startNode);
13912 * Filter by a function. The passed function will be called with each
13913 * node in the tree (or from the startNode). If the function returns true, the node is kept
13914 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13915 * @param {Function} fn The filter function
13916 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13918 filterBy : function(fn, scope, startNode){
13919 startNode = startNode || this.tree.root;
13920 if(this.autoClear){
13923 var af = this.filtered, rv = this.reverse;
13924 var f = function(n){
13925 if(n == startNode){
13931 var m = fn.call(scope || n, n);
13939 startNode.cascade(f);
13942 if(typeof id != "function"){
13944 if(n && n.parentNode){
13945 n.parentNode.removeChild(n);
13953 * Clears the current filter. Note: with the "remove" option
13954 * set a filter cannot be cleared.
13956 clear : function(){
13958 var af = this.filtered;
13960 if(typeof id != "function"){
13967 this.filtered = {};
13972 * Ext JS Library 1.1.1
13973 * Copyright(c) 2006-2007, Ext JS, LLC.
13975 * Originally Released Under LGPL - original licence link has changed is not relivant.
13978 * <script type="text/javascript">
13983 * @class Roo.tree.TreeSorter
13984 * Provides sorting of nodes in a TreePanel
13986 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13987 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13988 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13989 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13990 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13991 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13993 * @param {TreePanel} tree
13994 * @param {Object} config
13996 Roo.tree.TreeSorter = function(tree, config){
13997 Roo.apply(this, config);
13998 tree.on("beforechildrenrendered", this.doSort, this);
13999 tree.on("append", this.updateSort, this);
14000 tree.on("insert", this.updateSort, this);
14002 var dsc = this.dir && this.dir.toLowerCase() == "desc";
14003 var p = this.property || "text";
14004 var sortType = this.sortType;
14005 var fs = this.folderSort;
14006 var cs = this.caseSensitive === true;
14007 var leafAttr = this.leafAttr || 'leaf';
14009 this.sortFn = function(n1, n2){
14011 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14014 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14018 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14019 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14021 return dsc ? +1 : -1;
14023 return dsc ? -1 : +1;
14030 Roo.tree.TreeSorter.prototype = {
14031 doSort : function(node){
14032 node.sort(this.sortFn);
14035 compareNodes : function(n1, n2){
14036 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14039 updateSort : function(tree, node){
14040 if(node.childrenRendered){
14041 this.doSort.defer(1, this, [node]);
14046 * Ext JS Library 1.1.1
14047 * Copyright(c) 2006-2007, Ext JS, LLC.
14049 * Originally Released Under LGPL - original licence link has changed is not relivant.
14052 * <script type="text/javascript">
14055 if(Roo.dd.DropZone){
14057 Roo.tree.TreeDropZone = function(tree, config){
14058 this.allowParentInsert = false;
14059 this.allowContainerDrop = false;
14060 this.appendOnly = false;
14061 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14063 this.lastInsertClass = "x-tree-no-status";
14064 this.dragOverData = {};
14067 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14068 ddGroup : "TreeDD",
14071 expandDelay : 1000,
14073 expandNode : function(node){
14074 if(node.hasChildNodes() && !node.isExpanded()){
14075 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14079 queueExpand : function(node){
14080 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14083 cancelExpand : function(){
14084 if(this.expandProcId){
14085 clearTimeout(this.expandProcId);
14086 this.expandProcId = false;
14090 isValidDropPoint : function(n, pt, dd, e, data){
14091 if(!n || !data){ return false; }
14092 var targetNode = n.node;
14093 var dropNode = data.node;
14094 // default drop rules
14095 if(!(targetNode && targetNode.isTarget && pt)){
14098 if(pt == "append" && targetNode.allowChildren === false){
14101 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14104 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14107 // reuse the object
14108 var overEvent = this.dragOverData;
14109 overEvent.tree = this.tree;
14110 overEvent.target = targetNode;
14111 overEvent.data = data;
14112 overEvent.point = pt;
14113 overEvent.source = dd;
14114 overEvent.rawEvent = e;
14115 overEvent.dropNode = dropNode;
14116 overEvent.cancel = false;
14117 var result = this.tree.fireEvent("nodedragover", overEvent);
14118 return overEvent.cancel === false && result !== false;
14121 getDropPoint : function(e, n, dd)
14125 return tn.allowChildren !== false ? "append" : false; // always append for root
14127 var dragEl = n.ddel;
14128 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14129 var y = Roo.lib.Event.getPageY(e);
14130 //var noAppend = tn.allowChildren === false || tn.isLeaf();
14132 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14133 var noAppend = tn.allowChildren === false;
14134 if(this.appendOnly || tn.parentNode.allowChildren === false){
14135 return noAppend ? false : "append";
14137 var noBelow = false;
14138 if(!this.allowParentInsert){
14139 noBelow = tn.hasChildNodes() && tn.isExpanded();
14141 var q = (b - t) / (noAppend ? 2 : 3);
14142 if(y >= t && y < (t + q)){
14144 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14151 onNodeEnter : function(n, dd, e, data)
14153 this.cancelExpand();
14156 onNodeOver : function(n, dd, e, data)
14159 var pt = this.getDropPoint(e, n, dd);
14162 // auto node expand check
14163 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14164 this.queueExpand(node);
14165 }else if(pt != "append"){
14166 this.cancelExpand();
14169 // set the insert point style on the target node
14170 var returnCls = this.dropNotAllowed;
14171 if(this.isValidDropPoint(n, pt, dd, e, data)){
14176 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14177 cls = "x-tree-drag-insert-above";
14178 }else if(pt == "below"){
14179 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14180 cls = "x-tree-drag-insert-below";
14182 returnCls = "x-tree-drop-ok-append";
14183 cls = "x-tree-drag-append";
14185 if(this.lastInsertClass != cls){
14186 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14187 this.lastInsertClass = cls;
14194 onNodeOut : function(n, dd, e, data){
14196 this.cancelExpand();
14197 this.removeDropIndicators(n);
14200 onNodeDrop : function(n, dd, e, data){
14201 var point = this.getDropPoint(e, n, dd);
14202 var targetNode = n.node;
14203 targetNode.ui.startDrop();
14204 if(!this.isValidDropPoint(n, point, dd, e, data)){
14205 targetNode.ui.endDrop();
14208 // first try to find the drop node
14209 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14212 target: targetNode,
14217 dropNode: dropNode,
14220 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14221 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14222 targetNode.ui.endDrop();
14225 // allow target changing
14226 targetNode = dropEvent.target;
14227 if(point == "append" && !targetNode.isExpanded()){
14228 targetNode.expand(false, null, function(){
14229 this.completeDrop(dropEvent);
14230 }.createDelegate(this));
14232 this.completeDrop(dropEvent);
14237 completeDrop : function(de){
14238 var ns = de.dropNode, p = de.point, t = de.target;
14239 if(!(ns instanceof Array)){
14243 for(var i = 0, len = ns.length; i < len; i++){
14246 t.parentNode.insertBefore(n, t);
14247 }else if(p == "below"){
14248 t.parentNode.insertBefore(n, t.nextSibling);
14254 if(this.tree.hlDrop){
14258 this.tree.fireEvent("nodedrop", de);
14261 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14262 if(this.tree.hlDrop){
14263 dropNode.ui.focus();
14264 dropNode.ui.highlight();
14266 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14269 getTree : function(){
14273 removeDropIndicators : function(n){
14276 Roo.fly(el).removeClass([
14277 "x-tree-drag-insert-above",
14278 "x-tree-drag-insert-below",
14279 "x-tree-drag-append"]);
14280 this.lastInsertClass = "_noclass";
14284 beforeDragDrop : function(target, e, id){
14285 this.cancelExpand();
14289 afterRepair : function(data){
14290 if(data && Roo.enableFx){
14291 data.node.ui.highlight();
14301 * Ext JS Library 1.1.1
14302 * Copyright(c) 2006-2007, Ext JS, LLC.
14304 * Originally Released Under LGPL - original licence link has changed is not relivant.
14307 * <script type="text/javascript">
14311 if(Roo.dd.DragZone){
14312 Roo.tree.TreeDragZone = function(tree, config){
14313 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14317 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14318 ddGroup : "TreeDD",
14320 onBeforeDrag : function(data, e){
14322 return n && n.draggable && !n.disabled;
14326 onInitDrag : function(e){
14327 var data = this.dragData;
14328 this.tree.getSelectionModel().select(data.node);
14329 this.proxy.update("");
14330 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14331 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14334 getRepairXY : function(e, data){
14335 return data.node.ui.getDDRepairXY();
14338 onEndDrag : function(data, e){
14339 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14344 onValidDrop : function(dd, e, id){
14345 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14349 beforeInvalidDrop : function(e, id){
14350 // this scrolls the original position back into view
14351 var sm = this.tree.getSelectionModel();
14352 sm.clearSelections();
14353 sm.select(this.dragData.node);
14358 * Ext JS Library 1.1.1
14359 * Copyright(c) 2006-2007, Ext JS, LLC.
14361 * Originally Released Under LGPL - original licence link has changed is not relivant.
14364 * <script type="text/javascript">
14367 * @class Roo.tree.TreeEditor
14368 * @extends Roo.Editor
14369 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14370 * as the editor field.
14372 * @param {Object} config (used to be the tree panel.)
14373 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14375 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14376 * @cfg {Roo.form.TextField|Object} field The field configuration
14380 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14383 if (oldconfig) { // old style..
14384 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14387 tree = config.tree;
14388 config.field = config.field || {};
14389 config.field.xtype = 'TextField';
14390 field = Roo.factory(config.field, Roo.form);
14392 config = config || {};
14397 * @event beforenodeedit
14398 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14399 * false from the handler of this event.
14400 * @param {Editor} this
14401 * @param {Roo.tree.Node} node
14403 "beforenodeedit" : true
14407 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14411 tree.on('beforeclick', this.beforeNodeClick, this);
14412 tree.getTreeEl().on('mousedown', this.hide, this);
14413 this.on('complete', this.updateNode, this);
14414 this.on('beforestartedit', this.fitToTree, this);
14415 this.on('startedit', this.bindScroll, this, {delay:10});
14416 this.on('specialkey', this.onSpecialKey, this);
14419 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14421 * @cfg {String} alignment
14422 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14428 * @cfg {Boolean} hideEl
14429 * True to hide the bound element while the editor is displayed (defaults to false)
14433 * @cfg {String} cls
14434 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14436 cls: "x-small-editor x-tree-editor",
14438 * @cfg {Boolean} shim
14439 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14445 * @cfg {Number} maxWidth
14446 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14447 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14448 * scroll and client offsets into account prior to each edit.
14455 fitToTree : function(ed, el){
14456 var td = this.tree.getTreeEl().dom, nd = el.dom;
14457 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14458 td.scrollLeft = nd.offsetLeft;
14462 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14463 this.setSize(w, '');
14465 return this.fireEvent('beforenodeedit', this, this.editNode);
14470 triggerEdit : function(node){
14471 this.completeEdit();
14472 this.editNode = node;
14473 this.startEdit(node.ui.textNode, node.text);
14477 bindScroll : function(){
14478 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14482 beforeNodeClick : function(node, e){
14483 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14484 this.lastClick = new Date();
14485 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14487 this.triggerEdit(node);
14494 updateNode : function(ed, value){
14495 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14496 this.editNode.setText(value);
14500 onHide : function(){
14501 Roo.tree.TreeEditor.superclass.onHide.call(this);
14503 this.editNode.ui.focus();
14508 onSpecialKey : function(field, e){
14509 var k = e.getKey();
14513 }else if(k == e.ENTER && !e.hasModifier()){
14515 this.completeEdit();
14518 });//<Script type="text/javascript">
14521 * Ext JS Library 1.1.1
14522 * Copyright(c) 2006-2007, Ext JS, LLC.
14524 * Originally Released Under LGPL - original licence link has changed is not relivant.
14527 * <script type="text/javascript">
14531 * Not documented??? - probably should be...
14534 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14535 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14537 renderElements : function(n, a, targetNode, bulkRender){
14538 //consel.log("renderElements?");
14539 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14541 var t = n.getOwnerTree();
14542 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14544 var cols = t.columns;
14545 var bw = t.borderWidth;
14547 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14548 var cb = typeof a.checked == "boolean";
14549 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14550 var colcls = 'x-t-' + tid + '-c0';
14552 '<li class="x-tree-node">',
14555 '<div class="x-tree-node-el ', a.cls,'">',
14557 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14560 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14561 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14562 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14563 (a.icon ? ' x-tree-node-inline-icon' : ''),
14564 (a.iconCls ? ' '+a.iconCls : ''),
14565 '" unselectable="on" />',
14566 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14567 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14569 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14570 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14571 '<span unselectable="on" qtip="' + tx + '">',
14575 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14576 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14578 for(var i = 1, len = cols.length; i < len; i++){
14580 colcls = 'x-t-' + tid + '-c' +i;
14581 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14582 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14583 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14589 '<div class="x-clear"></div></div>',
14590 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14593 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14594 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14595 n.nextSibling.ui.getEl(), buf.join(""));
14597 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14599 var el = this.wrap.firstChild;
14601 this.elNode = el.firstChild;
14602 this.ranchor = el.childNodes[1];
14603 this.ctNode = this.wrap.childNodes[1];
14604 var cs = el.firstChild.childNodes;
14605 this.indentNode = cs[0];
14606 this.ecNode = cs[1];
14607 this.iconNode = cs[2];
14610 this.checkbox = cs[3];
14613 this.anchor = cs[index];
14615 this.textNode = cs[index].firstChild;
14617 //el.on("click", this.onClick, this);
14618 //el.on("dblclick", this.onDblClick, this);
14621 // console.log(this);
14623 initEvents : function(){
14624 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14627 var a = this.ranchor;
14629 var el = Roo.get(a);
14631 if(Roo.isOpera){ // opera render bug ignores the CSS
14632 el.setStyle("text-decoration", "none");
14635 el.on("click", this.onClick, this);
14636 el.on("dblclick", this.onDblClick, this);
14637 el.on("contextmenu", this.onContextMenu, this);
14641 /*onSelectedChange : function(state){
14644 this.addClass("x-tree-selected");
14647 this.removeClass("x-tree-selected");
14650 addClass : function(cls){
14652 Roo.fly(this.elRow).addClass(cls);
14658 removeClass : function(cls){
14660 Roo.fly(this.elRow).removeClass(cls);
14666 });//<Script type="text/javascript">
14670 * Ext JS Library 1.1.1
14671 * Copyright(c) 2006-2007, Ext JS, LLC.
14673 * Originally Released Under LGPL - original licence link has changed is not relivant.
14676 * <script type="text/javascript">
14681 * @class Roo.tree.ColumnTree
14682 * @extends Roo.data.TreePanel
14683 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14684 * @cfg {int} borderWidth compined right/left border allowance
14686 * @param {String/HTMLElement/Element} el The container element
14687 * @param {Object} config
14689 Roo.tree.ColumnTree = function(el, config)
14691 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14695 * Fire this event on a container when it resizes
14696 * @param {int} w Width
14697 * @param {int} h Height
14701 this.on('resize', this.onResize, this);
14704 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14708 borderWidth: Roo.isBorderBox ? 0 : 2,
14711 render : function(){
14712 // add the header.....
14714 Roo.tree.ColumnTree.superclass.render.apply(this);
14716 this.el.addClass('x-column-tree');
14718 this.headers = this.el.createChild(
14719 {cls:'x-tree-headers'},this.innerCt.dom);
14721 var cols = this.columns, c;
14722 var totalWidth = 0;
14724 var len = cols.length;
14725 for(var i = 0; i < len; i++){
14727 totalWidth += c.width;
14728 this.headEls.push(this.headers.createChild({
14729 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14731 cls:'x-tree-hd-text',
14734 style:'width:'+(c.width-this.borderWidth)+'px;'
14737 this.headers.createChild({cls:'x-clear'});
14738 // prevent floats from wrapping when clipped
14739 this.headers.setWidth(totalWidth);
14740 //this.innerCt.setWidth(totalWidth);
14741 this.innerCt.setStyle({ overflow: 'auto' });
14742 this.onResize(this.width, this.height);
14746 onResize : function(w,h)
14751 this.innerCt.setWidth(this.width);
14752 this.innerCt.setHeight(this.height-20);
14755 var cols = this.columns, c;
14756 var totalWidth = 0;
14758 var len = cols.length;
14759 for(var i = 0; i < len; i++){
14761 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14762 // it's the expander..
14763 expEl = this.headEls[i];
14766 totalWidth += c.width;
14770 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14772 this.headers.setWidth(w-20);
14781 * Ext JS Library 1.1.1
14782 * Copyright(c) 2006-2007, Ext JS, LLC.
14784 * Originally Released Under LGPL - original licence link has changed is not relivant.
14787 * <script type="text/javascript">
14791 * @class Roo.menu.Menu
14792 * @extends Roo.util.Observable
14793 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14794 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14796 * Creates a new Menu
14797 * @param {Object} config Configuration options
14799 Roo.menu.Menu = function(config){
14801 Roo.menu.Menu.superclass.constructor.call(this, config);
14803 this.id = this.id || Roo.id();
14806 * @event beforeshow
14807 * Fires before this menu is displayed
14808 * @param {Roo.menu.Menu} this
14812 * @event beforehide
14813 * Fires before this menu is hidden
14814 * @param {Roo.menu.Menu} this
14819 * Fires after this menu is displayed
14820 * @param {Roo.menu.Menu} this
14825 * Fires after this menu is hidden
14826 * @param {Roo.menu.Menu} this
14831 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14832 * @param {Roo.menu.Menu} this
14833 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14834 * @param {Roo.EventObject} e
14839 * Fires when the mouse is hovering over this menu
14840 * @param {Roo.menu.Menu} this
14841 * @param {Roo.EventObject} e
14842 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14847 * Fires when the mouse exits this menu
14848 * @param {Roo.menu.Menu} this
14849 * @param {Roo.EventObject} e
14850 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14855 * Fires when a menu item contained in this menu is clicked
14856 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14857 * @param {Roo.EventObject} e
14861 if (this.registerMenu) {
14862 Roo.menu.MenuMgr.register(this);
14865 var mis = this.items;
14866 this.items = new Roo.util.MixedCollection();
14868 this.add.apply(this, mis);
14872 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14874 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14878 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14879 * for bottom-right shadow (defaults to "sides")
14883 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14884 * this menu (defaults to "tl-tr?")
14886 subMenuAlign : "tl-tr?",
14888 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14889 * relative to its element of origin (defaults to "tl-bl?")
14891 defaultAlign : "tl-bl?",
14893 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14895 allowOtherMenus : false,
14897 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14899 registerMenu : true,
14904 render : function(){
14908 var el = this.el = new Roo.Layer({
14910 shadow:this.shadow,
14912 parentEl: this.parentEl || document.body,
14916 this.keyNav = new Roo.menu.MenuNav(this);
14919 el.addClass("x-menu-plain");
14922 el.addClass(this.cls);
14924 // generic focus element
14925 this.focusEl = el.createChild({
14926 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14928 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14929 //disabling touch- as it's causing issues ..
14930 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14931 ul.on('click' , this.onClick, this);
14934 ul.on("mouseover", this.onMouseOver, this);
14935 ul.on("mouseout", this.onMouseOut, this);
14936 this.items.each(function(item){
14941 var li = document.createElement("li");
14942 li.className = "x-menu-list-item";
14943 ul.dom.appendChild(li);
14944 item.render(li, this);
14951 autoWidth : function(){
14952 var el = this.el, ul = this.ul;
14956 var w = this.width;
14959 }else if(Roo.isIE){
14960 el.setWidth(this.minWidth);
14961 var t = el.dom.offsetWidth; // force recalc
14962 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14967 delayAutoWidth : function(){
14970 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14972 this.awTask.delay(20);
14977 findTargetItem : function(e){
14978 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14979 if(t && t.menuItemId){
14980 return this.items.get(t.menuItemId);
14985 onClick : function(e){
14986 Roo.log("menu.onClick");
14987 var t = this.findTargetItem(e);
14992 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14993 if(t == this.activeItem && t.shouldDeactivate(e)){
14994 this.activeItem.deactivate();
14995 delete this.activeItem;
14999 this.setActiveItem(t, true);
15007 this.fireEvent("click", this, t, e);
15011 setActiveItem : function(item, autoExpand){
15012 if(item != this.activeItem){
15013 if(this.activeItem){
15014 this.activeItem.deactivate();
15016 this.activeItem = item;
15017 item.activate(autoExpand);
15018 }else if(autoExpand){
15024 tryActivate : function(start, step){
15025 var items = this.items;
15026 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15027 var item = items.get(i);
15028 if(!item.disabled && item.canActivate){
15029 this.setActiveItem(item, false);
15037 onMouseOver : function(e){
15039 if(t = this.findTargetItem(e)){
15040 if(t.canActivate && !t.disabled){
15041 this.setActiveItem(t, true);
15044 this.fireEvent("mouseover", this, e, t);
15048 onMouseOut : function(e){
15050 if(t = this.findTargetItem(e)){
15051 if(t == this.activeItem && t.shouldDeactivate(e)){
15052 this.activeItem.deactivate();
15053 delete this.activeItem;
15056 this.fireEvent("mouseout", this, e, t);
15060 * Read-only. Returns true if the menu is currently displayed, else false.
15063 isVisible : function(){
15064 return this.el && !this.hidden;
15068 * Displays this menu relative to another element
15069 * @param {String/HTMLElement/Roo.Element} element The element to align to
15070 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15071 * the element (defaults to this.defaultAlign)
15072 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15074 show : function(el, pos, parentMenu){
15075 this.parentMenu = parentMenu;
15079 this.fireEvent("beforeshow", this);
15080 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15084 * Displays this menu at a specific xy position
15085 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15086 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15088 showAt : function(xy, parentMenu, /* private: */_e){
15089 this.parentMenu = parentMenu;
15094 this.fireEvent("beforeshow", this);
15095 xy = this.el.adjustForConstraints(xy);
15099 this.hidden = false;
15101 this.fireEvent("show", this);
15104 focus : function(){
15106 this.doFocus.defer(50, this);
15110 doFocus : function(){
15112 this.focusEl.focus();
15117 * Hides this menu and optionally all parent menus
15118 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15120 hide : function(deep){
15121 if(this.el && this.isVisible()){
15122 this.fireEvent("beforehide", this);
15123 if(this.activeItem){
15124 this.activeItem.deactivate();
15125 this.activeItem = null;
15128 this.hidden = true;
15129 this.fireEvent("hide", this);
15131 if(deep === true && this.parentMenu){
15132 this.parentMenu.hide(true);
15137 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15138 * Any of the following are valid:
15140 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15141 * <li>An HTMLElement object which will be converted to a menu item</li>
15142 * <li>A menu item config object that will be created as a new menu item</li>
15143 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15144 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15149 var menu = new Roo.menu.Menu();
15151 // Create a menu item to add by reference
15152 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15154 // Add a bunch of items at once using different methods.
15155 // Only the last item added will be returned.
15156 var item = menu.add(
15157 menuItem, // add existing item by ref
15158 'Dynamic Item', // new TextItem
15159 '-', // new separator
15160 { text: 'Config Item' } // new item by config
15163 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15164 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15167 var a = arguments, l = a.length, item;
15168 for(var i = 0; i < l; i++){
15170 if ((typeof(el) == "object") && el.xtype && el.xns) {
15171 el = Roo.factory(el, Roo.menu);
15174 if(el.render){ // some kind of Item
15175 item = this.addItem(el);
15176 }else if(typeof el == "string"){ // string
15177 if(el == "separator" || el == "-"){
15178 item = this.addSeparator();
15180 item = this.addText(el);
15182 }else if(el.tagName || el.el){ // element
15183 item = this.addElement(el);
15184 }else if(typeof el == "object"){ // must be menu item config?
15185 item = this.addMenuItem(el);
15192 * Returns this menu's underlying {@link Roo.Element} object
15193 * @return {Roo.Element} The element
15195 getEl : function(){
15203 * Adds a separator bar to the menu
15204 * @return {Roo.menu.Item} The menu item that was added
15206 addSeparator : function(){
15207 return this.addItem(new Roo.menu.Separator());
15211 * Adds an {@link Roo.Element} object to the menu
15212 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15213 * @return {Roo.menu.Item} The menu item that was added
15215 addElement : function(el){
15216 return this.addItem(new Roo.menu.BaseItem(el));
15220 * Adds an existing object based on {@link Roo.menu.Item} to the menu
15221 * @param {Roo.menu.Item} item The menu item to add
15222 * @return {Roo.menu.Item} The menu item that was added
15224 addItem : function(item){
15225 this.items.add(item);
15227 var li = document.createElement("li");
15228 li.className = "x-menu-list-item";
15229 this.ul.dom.appendChild(li);
15230 item.render(li, this);
15231 this.delayAutoWidth();
15237 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15238 * @param {Object} config A MenuItem config object
15239 * @return {Roo.menu.Item} The menu item that was added
15241 addMenuItem : function(config){
15242 if(!(config instanceof Roo.menu.Item)){
15243 if(typeof config.checked == "boolean"){ // must be check menu item config?
15244 config = new Roo.menu.CheckItem(config);
15246 config = new Roo.menu.Item(config);
15249 return this.addItem(config);
15253 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15254 * @param {String} text The text to display in the menu item
15255 * @return {Roo.menu.Item} The menu item that was added
15257 addText : function(text){
15258 return this.addItem(new Roo.menu.TextItem({ text : text }));
15262 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15263 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15264 * @param {Roo.menu.Item} item The menu item to add
15265 * @return {Roo.menu.Item} The menu item that was added
15267 insert : function(index, item){
15268 this.items.insert(index, item);
15270 var li = document.createElement("li");
15271 li.className = "x-menu-list-item";
15272 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15273 item.render(li, this);
15274 this.delayAutoWidth();
15280 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15281 * @param {Roo.menu.Item} item The menu item to remove
15283 remove : function(item){
15284 this.items.removeKey(item.id);
15289 * Removes and destroys all items in the menu
15291 removeAll : function(){
15293 while(f = this.items.first()){
15299 // MenuNav is a private utility class used internally by the Menu
15300 Roo.menu.MenuNav = function(menu){
15301 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15302 this.scope = this.menu = menu;
15305 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15306 doRelay : function(e, h){
15307 var k = e.getKey();
15308 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15309 this.menu.tryActivate(0, 1);
15312 return h.call(this.scope || this, e, this.menu);
15315 up : function(e, m){
15316 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15317 m.tryActivate(m.items.length-1, -1);
15321 down : function(e, m){
15322 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15323 m.tryActivate(0, 1);
15327 right : function(e, m){
15329 m.activeItem.expandMenu(true);
15333 left : function(e, m){
15335 if(m.parentMenu && m.parentMenu.activeItem){
15336 m.parentMenu.activeItem.activate();
15340 enter : function(e, m){
15342 e.stopPropagation();
15343 m.activeItem.onClick(e);
15344 m.fireEvent("click", this, m.activeItem);
15350 * Ext JS Library 1.1.1
15351 * Copyright(c) 2006-2007, Ext JS, LLC.
15353 * Originally Released Under LGPL - original licence link has changed is not relivant.
15356 * <script type="text/javascript">
15360 * @class Roo.menu.MenuMgr
15361 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15364 Roo.menu.MenuMgr = function(){
15365 var menus, active, groups = {}, attached = false, lastShow = new Date();
15367 // private - called when first menu is created
15370 active = new Roo.util.MixedCollection();
15371 Roo.get(document).addKeyListener(27, function(){
15372 if(active.length > 0){
15379 function hideAll(){
15380 if(active && active.length > 0){
15381 var c = active.clone();
15382 c.each(function(m){
15389 function onHide(m){
15391 if(active.length < 1){
15392 Roo.get(document).un("mousedown", onMouseDown);
15398 function onShow(m){
15399 var last = active.last();
15400 lastShow = new Date();
15403 Roo.get(document).on("mousedown", onMouseDown);
15407 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15408 m.parentMenu.activeChild = m;
15409 }else if(last && last.isVisible()){
15410 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15415 function onBeforeHide(m){
15417 m.activeChild.hide();
15419 if(m.autoHideTimer){
15420 clearTimeout(m.autoHideTimer);
15421 delete m.autoHideTimer;
15426 function onBeforeShow(m){
15427 var pm = m.parentMenu;
15428 if(!pm && !m.allowOtherMenus){
15430 }else if(pm && pm.activeChild && active != m){
15431 pm.activeChild.hide();
15436 function onMouseDown(e){
15437 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15443 function onBeforeCheck(mi, state){
15445 var g = groups[mi.group];
15446 for(var i = 0, l = g.length; i < l; i++){
15448 g[i].setChecked(false);
15457 * Hides all menus that are currently visible
15459 hideAll : function(){
15464 register : function(menu){
15468 menus[menu.id] = menu;
15469 menu.on("beforehide", onBeforeHide);
15470 menu.on("hide", onHide);
15471 menu.on("beforeshow", onBeforeShow);
15472 menu.on("show", onShow);
15473 var g = menu.group;
15474 if(g && menu.events["checkchange"]){
15478 groups[g].push(menu);
15479 menu.on("checkchange", onCheck);
15484 * Returns a {@link Roo.menu.Menu} object
15485 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15486 * be used to generate and return a new Menu instance.
15488 get : function(menu){
15489 if(typeof menu == "string"){ // menu id
15490 return menus[menu];
15491 }else if(menu.events){ // menu instance
15493 }else if(typeof menu.length == 'number'){ // array of menu items?
15494 return new Roo.menu.Menu({items:menu});
15495 }else{ // otherwise, must be a config
15496 return new Roo.menu.Menu(menu);
15501 unregister : function(menu){
15502 delete menus[menu.id];
15503 menu.un("beforehide", onBeforeHide);
15504 menu.un("hide", onHide);
15505 menu.un("beforeshow", onBeforeShow);
15506 menu.un("show", onShow);
15507 var g = menu.group;
15508 if(g && menu.events["checkchange"]){
15509 groups[g].remove(menu);
15510 menu.un("checkchange", onCheck);
15515 registerCheckable : function(menuItem){
15516 var g = menuItem.group;
15521 groups[g].push(menuItem);
15522 menuItem.on("beforecheckchange", onBeforeCheck);
15527 unregisterCheckable : function(menuItem){
15528 var g = menuItem.group;
15530 groups[g].remove(menuItem);
15531 menuItem.un("beforecheckchange", onBeforeCheck);
15537 * Ext JS Library 1.1.1
15538 * Copyright(c) 2006-2007, Ext JS, LLC.
15540 * Originally Released Under LGPL - original licence link has changed is not relivant.
15543 * <script type="text/javascript">
15548 * @class Roo.menu.BaseItem
15549 * @extends Roo.Component
15550 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15551 * management and base configuration options shared by all menu components.
15553 * Creates a new BaseItem
15554 * @param {Object} config Configuration options
15556 Roo.menu.BaseItem = function(config){
15557 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15562 * Fires when this item is clicked
15563 * @param {Roo.menu.BaseItem} this
15564 * @param {Roo.EventObject} e
15569 * Fires when this item is activated
15570 * @param {Roo.menu.BaseItem} this
15574 * @event deactivate
15575 * Fires when this item is deactivated
15576 * @param {Roo.menu.BaseItem} this
15582 this.on("click", this.handler, this.scope, true);
15586 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15588 * @cfg {Function} handler
15589 * A function that will handle the click event of this menu item (defaults to undefined)
15592 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15594 canActivate : false,
15597 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15602 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15604 activeClass : "x-menu-item-active",
15606 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15608 hideOnClick : true,
15610 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15615 ctype: "Roo.menu.BaseItem",
15618 actionMode : "container",
15621 render : function(container, parentMenu){
15622 this.parentMenu = parentMenu;
15623 Roo.menu.BaseItem.superclass.render.call(this, container);
15624 this.container.menuItemId = this.id;
15628 onRender : function(container, position){
15629 this.el = Roo.get(this.el);
15630 container.dom.appendChild(this.el.dom);
15634 onClick : function(e){
15635 if(!this.disabled && this.fireEvent("click", this, e) !== false
15636 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15637 this.handleClick(e);
15644 activate : function(){
15648 var li = this.container;
15649 li.addClass(this.activeClass);
15650 this.region = li.getRegion().adjust(2, 2, -2, -2);
15651 this.fireEvent("activate", this);
15656 deactivate : function(){
15657 this.container.removeClass(this.activeClass);
15658 this.fireEvent("deactivate", this);
15662 shouldDeactivate : function(e){
15663 return !this.region || !this.region.contains(e.getPoint());
15667 handleClick : function(e){
15668 if(this.hideOnClick){
15669 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15674 expandMenu : function(autoActivate){
15679 hideMenu : function(){
15684 * Ext JS Library 1.1.1
15685 * Copyright(c) 2006-2007, Ext JS, LLC.
15687 * Originally Released Under LGPL - original licence link has changed is not relivant.
15690 * <script type="text/javascript">
15694 * @class Roo.menu.Adapter
15695 * @extends Roo.menu.BaseItem
15696 * 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.
15697 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15699 * Creates a new Adapter
15700 * @param {Object} config Configuration options
15702 Roo.menu.Adapter = function(component, config){
15703 Roo.menu.Adapter.superclass.constructor.call(this, config);
15704 this.component = component;
15706 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15708 canActivate : true,
15711 onRender : function(container, position){
15712 this.component.render(container);
15713 this.el = this.component.getEl();
15717 activate : function(){
15721 this.component.focus();
15722 this.fireEvent("activate", this);
15727 deactivate : function(){
15728 this.fireEvent("deactivate", this);
15732 disable : function(){
15733 this.component.disable();
15734 Roo.menu.Adapter.superclass.disable.call(this);
15738 enable : function(){
15739 this.component.enable();
15740 Roo.menu.Adapter.superclass.enable.call(this);
15744 * Ext JS Library 1.1.1
15745 * Copyright(c) 2006-2007, Ext JS, LLC.
15747 * Originally Released Under LGPL - original licence link has changed is not relivant.
15750 * <script type="text/javascript">
15754 * @class Roo.menu.TextItem
15755 * @extends Roo.menu.BaseItem
15756 * Adds a static text string to a menu, usually used as either a heading or group separator.
15757 * Note: old style constructor with text is still supported.
15760 * Creates a new TextItem
15761 * @param {Object} cfg Configuration
15763 Roo.menu.TextItem = function(cfg){
15764 if (typeof(cfg) == 'string') {
15767 Roo.apply(this,cfg);
15770 Roo.menu.TextItem.superclass.constructor.call(this);
15773 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15775 * @cfg {Boolean} text Text to show on item.
15780 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15782 hideOnClick : false,
15784 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15786 itemCls : "x-menu-text",
15789 onRender : function(){
15790 var s = document.createElement("span");
15791 s.className = this.itemCls;
15792 s.innerHTML = this.text;
15794 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15798 * Ext JS Library 1.1.1
15799 * Copyright(c) 2006-2007, Ext JS, LLC.
15801 * Originally Released Under LGPL - original licence link has changed is not relivant.
15804 * <script type="text/javascript">
15808 * @class Roo.menu.Separator
15809 * @extends Roo.menu.BaseItem
15810 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15811 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15813 * @param {Object} config Configuration options
15815 Roo.menu.Separator = function(config){
15816 Roo.menu.Separator.superclass.constructor.call(this, config);
15819 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15821 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15823 itemCls : "x-menu-sep",
15825 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15827 hideOnClick : false,
15830 onRender : function(li){
15831 var s = document.createElement("span");
15832 s.className = this.itemCls;
15833 s.innerHTML = " ";
15835 li.addClass("x-menu-sep-li");
15836 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15840 * Ext JS Library 1.1.1
15841 * Copyright(c) 2006-2007, Ext JS, LLC.
15843 * Originally Released Under LGPL - original licence link has changed is not relivant.
15846 * <script type="text/javascript">
15849 * @class Roo.menu.Item
15850 * @extends Roo.menu.BaseItem
15851 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15852 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15853 * activation and click handling.
15855 * Creates a new Item
15856 * @param {Object} config Configuration options
15858 Roo.menu.Item = function(config){
15859 Roo.menu.Item.superclass.constructor.call(this, config);
15861 this.menu = Roo.menu.MenuMgr.get(this.menu);
15864 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15867 * @cfg {String} text
15868 * The text to show on the menu item.
15872 * @cfg {String} HTML to render in menu
15873 * The text to show on the menu item (HTML version).
15877 * @cfg {String} icon
15878 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15882 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15884 itemCls : "x-menu-item",
15886 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15888 canActivate : true,
15890 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15893 // doc'd in BaseItem
15897 ctype: "Roo.menu.Item",
15900 onRender : function(container, position){
15901 var el = document.createElement("a");
15902 el.hideFocus = true;
15903 el.unselectable = "on";
15904 el.href = this.href || "#";
15905 if(this.hrefTarget){
15906 el.target = this.hrefTarget;
15908 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15910 var html = this.html.length ? this.html : String.format('{0}',this.text);
15912 el.innerHTML = String.format(
15913 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15914 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15916 Roo.menu.Item.superclass.onRender.call(this, container, position);
15920 * Sets the text to display in this menu item
15921 * @param {String} text The text to display
15922 * @param {Boolean} isHTML true to indicate text is pure html.
15924 setText : function(text, isHTML){
15932 var html = this.html.length ? this.html : String.format('{0}',this.text);
15934 this.el.update(String.format(
15935 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15936 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15937 this.parentMenu.autoWidth();
15942 handleClick : function(e){
15943 if(!this.href){ // if no link defined, stop the event automatically
15946 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15950 activate : function(autoExpand){
15951 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15961 shouldDeactivate : function(e){
15962 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15963 if(this.menu && this.menu.isVisible()){
15964 return !this.menu.getEl().getRegion().contains(e.getPoint());
15972 deactivate : function(){
15973 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15978 expandMenu : function(autoActivate){
15979 if(!this.disabled && this.menu){
15980 clearTimeout(this.hideTimer);
15981 delete this.hideTimer;
15982 if(!this.menu.isVisible() && !this.showTimer){
15983 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15984 }else if (this.menu.isVisible() && autoActivate){
15985 this.menu.tryActivate(0, 1);
15991 deferExpand : function(autoActivate){
15992 delete this.showTimer;
15993 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15995 this.menu.tryActivate(0, 1);
16000 hideMenu : function(){
16001 clearTimeout(this.showTimer);
16002 delete this.showTimer;
16003 if(!this.hideTimer && this.menu && this.menu.isVisible()){
16004 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16009 deferHide : function(){
16010 delete this.hideTimer;
16015 * Ext JS Library 1.1.1
16016 * Copyright(c) 2006-2007, Ext JS, LLC.
16018 * Originally Released Under LGPL - original licence link has changed is not relivant.
16021 * <script type="text/javascript">
16025 * @class Roo.menu.CheckItem
16026 * @extends Roo.menu.Item
16027 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16029 * Creates a new CheckItem
16030 * @param {Object} config Configuration options
16032 Roo.menu.CheckItem = function(config){
16033 Roo.menu.CheckItem.superclass.constructor.call(this, config);
16036 * @event beforecheckchange
16037 * Fires before the checked value is set, providing an opportunity to cancel if needed
16038 * @param {Roo.menu.CheckItem} this
16039 * @param {Boolean} checked The new checked value that will be set
16041 "beforecheckchange" : true,
16043 * @event checkchange
16044 * Fires after the checked value has been set
16045 * @param {Roo.menu.CheckItem} this
16046 * @param {Boolean} checked The checked value that was set
16048 "checkchange" : true
16050 if(this.checkHandler){
16051 this.on('checkchange', this.checkHandler, this.scope);
16054 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16056 * @cfg {String} group
16057 * All check items with the same group name will automatically be grouped into a single-select
16058 * radio button group (defaults to '')
16061 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16063 itemCls : "x-menu-item x-menu-check-item",
16065 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16067 groupClass : "x-menu-group-item",
16070 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
16071 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16072 * initialized with checked = true will be rendered as checked.
16077 ctype: "Roo.menu.CheckItem",
16080 onRender : function(c){
16081 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16083 this.el.addClass(this.groupClass);
16085 Roo.menu.MenuMgr.registerCheckable(this);
16087 this.checked = false;
16088 this.setChecked(true, true);
16093 destroy : function(){
16095 Roo.menu.MenuMgr.unregisterCheckable(this);
16097 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16101 * Set the checked state of this item
16102 * @param {Boolean} checked The new checked value
16103 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16105 setChecked : function(state, suppressEvent){
16106 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16107 if(this.container){
16108 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16110 this.checked = state;
16111 if(suppressEvent !== true){
16112 this.fireEvent("checkchange", this, state);
16118 handleClick : function(e){
16119 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16120 this.setChecked(!this.checked);
16122 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16126 * Ext JS Library 1.1.1
16127 * Copyright(c) 2006-2007, Ext JS, LLC.
16129 * Originally Released Under LGPL - original licence link has changed is not relivant.
16132 * <script type="text/javascript">
16136 * @class Roo.menu.DateItem
16137 * @extends Roo.menu.Adapter
16138 * A menu item that wraps the {@link Roo.DatPicker} component.
16140 * Creates a new DateItem
16141 * @param {Object} config Configuration options
16143 Roo.menu.DateItem = function(config){
16144 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16145 /** The Roo.DatePicker object @type Roo.DatePicker */
16146 this.picker = this.component;
16147 this.addEvents({select: true});
16149 this.picker.on("render", function(picker){
16150 picker.getEl().swallowEvent("click");
16151 picker.container.addClass("x-menu-date-item");
16154 this.picker.on("select", this.onSelect, this);
16157 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16159 onSelect : function(picker, date){
16160 this.fireEvent("select", this, date, picker);
16161 Roo.menu.DateItem.superclass.handleClick.call(this);
16165 * Ext JS Library 1.1.1
16166 * Copyright(c) 2006-2007, Ext JS, LLC.
16168 * Originally Released Under LGPL - original licence link has changed is not relivant.
16171 * <script type="text/javascript">
16175 * @class Roo.menu.ColorItem
16176 * @extends Roo.menu.Adapter
16177 * A menu item that wraps the {@link Roo.ColorPalette} component.
16179 * Creates a new ColorItem
16180 * @param {Object} config Configuration options
16182 Roo.menu.ColorItem = function(config){
16183 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16184 /** The Roo.ColorPalette object @type Roo.ColorPalette */
16185 this.palette = this.component;
16186 this.relayEvents(this.palette, ["select"]);
16187 if(this.selectHandler){
16188 this.on('select', this.selectHandler, this.scope);
16191 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16193 * Ext JS Library 1.1.1
16194 * Copyright(c) 2006-2007, Ext JS, LLC.
16196 * Originally Released Under LGPL - original licence link has changed is not relivant.
16199 * <script type="text/javascript">
16204 * @class Roo.menu.DateMenu
16205 * @extends Roo.menu.Menu
16206 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16208 * Creates a new DateMenu
16209 * @param {Object} config Configuration options
16211 Roo.menu.DateMenu = function(config){
16212 Roo.menu.DateMenu.superclass.constructor.call(this, config);
16214 var di = new Roo.menu.DateItem(config);
16217 * The {@link Roo.DatePicker} instance for this DateMenu
16220 this.picker = di.picker;
16223 * @param {DatePicker} picker
16224 * @param {Date} date
16226 this.relayEvents(di, ["select"]);
16227 this.on('beforeshow', function(){
16229 this.picker.hideMonthPicker(false);
16233 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16237 * Ext JS Library 1.1.1
16238 * Copyright(c) 2006-2007, Ext JS, LLC.
16240 * Originally Released Under LGPL - original licence link has changed is not relivant.
16243 * <script type="text/javascript">
16248 * @class Roo.menu.ColorMenu
16249 * @extends Roo.menu.Menu
16250 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16252 * Creates a new ColorMenu
16253 * @param {Object} config Configuration options
16255 Roo.menu.ColorMenu = function(config){
16256 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16258 var ci = new Roo.menu.ColorItem(config);
16261 * The {@link Roo.ColorPalette} instance for this ColorMenu
16262 * @type ColorPalette
16264 this.palette = ci.palette;
16267 * @param {ColorPalette} palette
16268 * @param {String} color
16270 this.relayEvents(ci, ["select"]);
16272 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16274 * Ext JS Library 1.1.1
16275 * Copyright(c) 2006-2007, Ext JS, LLC.
16277 * Originally Released Under LGPL - original licence link has changed is not relivant.
16280 * <script type="text/javascript">
16284 * @class Roo.form.TextItem
16285 * @extends Roo.BoxComponent
16286 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16288 * Creates a new TextItem
16289 * @param {Object} config Configuration options
16291 Roo.form.TextItem = function(config){
16292 Roo.form.TextItem.superclass.constructor.call(this, config);
16295 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
16298 * @cfg {String} tag the tag for this item (default div)
16302 * @cfg {String} html the content for this item
16306 getAutoCreate : function()
16319 onRender : function(ct, position)
16321 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16324 var cfg = this.getAutoCreate();
16326 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16328 if (!cfg.name.length) {
16331 this.el = ct.createChild(cfg, position);
16337 * Ext JS Library 1.1.1
16338 * Copyright(c) 2006-2007, Ext JS, LLC.
16340 * Originally Released Under LGPL - original licence link has changed is not relivant.
16343 * <script type="text/javascript">
16347 * @class Roo.form.Field
16348 * @extends Roo.BoxComponent
16349 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16351 * Creates a new Field
16352 * @param {Object} config Configuration options
16354 Roo.form.Field = function(config){
16355 Roo.form.Field.superclass.constructor.call(this, config);
16358 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16360 * @cfg {String} fieldLabel Label to use when rendering a form.
16363 * @cfg {String} qtip Mouse over tip
16367 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16369 invalidClass : "x-form-invalid",
16371 * @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")
16373 invalidText : "The value in this field is invalid",
16375 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16377 focusClass : "x-form-focus",
16379 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16380 automatic validation (defaults to "keyup").
16382 validationEvent : "keyup",
16384 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16386 validateOnBlur : true,
16388 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16390 validationDelay : 250,
16392 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16393 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16395 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16397 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16399 fieldClass : "x-form-field",
16401 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16404 ----------- ----------------------------------------------------------------------
16405 qtip Display a quick tip when the user hovers over the field
16406 title Display a default browser title attribute popup
16407 under Add a block div beneath the field containing the error text
16408 side Add an error icon to the right of the field with a popup on hover
16409 [element id] Add the error text directly to the innerHTML of the specified element
16412 msgTarget : 'qtip',
16414 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16419 * @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.
16424 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16429 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16431 inputType : undefined,
16434 * @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).
16436 tabIndex : undefined,
16439 isFormField : true,
16444 * @property {Roo.Element} fieldEl
16445 * Element Containing the rendered Field (with label etc.)
16448 * @cfg {Mixed} value A value to initialize this field with.
16453 * @cfg {String} name The field's HTML name attribute.
16456 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16459 loadedValue : false,
16463 initComponent : function(){
16464 Roo.form.Field.superclass.initComponent.call(this);
16468 * Fires when this field receives input focus.
16469 * @param {Roo.form.Field} this
16474 * Fires when this field loses input focus.
16475 * @param {Roo.form.Field} this
16479 * @event specialkey
16480 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16481 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16482 * @param {Roo.form.Field} this
16483 * @param {Roo.EventObject} e The event object
16488 * Fires just before the field blurs if the field value has changed.
16489 * @param {Roo.form.Field} this
16490 * @param {Mixed} newValue The new value
16491 * @param {Mixed} oldValue The original value
16496 * Fires after the field has been marked as invalid.
16497 * @param {Roo.form.Field} this
16498 * @param {String} msg The validation message
16503 * Fires after the field has been validated with no errors.
16504 * @param {Roo.form.Field} this
16509 * Fires after the key up
16510 * @param {Roo.form.Field} this
16511 * @param {Roo.EventObject} e The event Object
16518 * Returns the name attribute of the field if available
16519 * @return {String} name The field name
16521 getName: function(){
16522 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16526 onRender : function(ct, position){
16527 Roo.form.Field.superclass.onRender.call(this, ct, position);
16529 var cfg = this.getAutoCreate();
16531 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16533 if (!cfg.name.length) {
16536 if(this.inputType){
16537 cfg.type = this.inputType;
16539 this.el = ct.createChild(cfg, position);
16541 var type = this.el.dom.type;
16543 if(type == 'password'){
16546 this.el.addClass('x-form-'+type);
16549 this.el.dom.readOnly = true;
16551 if(this.tabIndex !== undefined){
16552 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16555 this.el.addClass([this.fieldClass, this.cls]);
16560 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16561 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16562 * @return {Roo.form.Field} this
16564 applyTo : function(target){
16565 this.allowDomMove = false;
16566 this.el = Roo.get(target);
16567 this.render(this.el.dom.parentNode);
16572 initValue : function(){
16573 if(this.value !== undefined){
16574 this.setValue(this.value);
16575 }else if(this.el.dom.value.length > 0){
16576 this.setValue(this.el.dom.value);
16581 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16582 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16584 isDirty : function() {
16585 if(this.disabled) {
16588 return String(this.getValue()) !== String(this.originalValue);
16592 * stores the current value in loadedValue
16594 resetHasChanged : function()
16596 this.loadedValue = String(this.getValue());
16599 * checks the current value against the 'loaded' value.
16600 * Note - will return false if 'resetHasChanged' has not been called first.
16602 hasChanged : function()
16604 if(this.disabled || this.readOnly) {
16607 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16613 afterRender : function(){
16614 Roo.form.Field.superclass.afterRender.call(this);
16619 fireKey : function(e){
16620 //Roo.log('field ' + e.getKey());
16621 if(e.isNavKeyPress()){
16622 this.fireEvent("specialkey", this, e);
16627 * Resets the current field value to the originally loaded value and clears any validation messages
16629 reset : function(){
16630 this.setValue(this.resetValue);
16631 this.originalValue = this.getValue();
16632 this.clearInvalid();
16636 initEvents : function(){
16637 // safari killled keypress - so keydown is now used..
16638 this.el.on("keydown" , this.fireKey, this);
16639 this.el.on("focus", this.onFocus, this);
16640 this.el.on("blur", this.onBlur, this);
16641 this.el.relayEvent('keyup', this);
16643 // reference to original value for reset
16644 this.originalValue = this.getValue();
16645 this.resetValue = this.getValue();
16649 onFocus : function(){
16650 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16651 this.el.addClass(this.focusClass);
16653 if(!this.hasFocus){
16654 this.hasFocus = true;
16655 this.startValue = this.getValue();
16656 this.fireEvent("focus", this);
16660 beforeBlur : Roo.emptyFn,
16663 onBlur : function(){
16665 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16666 this.el.removeClass(this.focusClass);
16668 this.hasFocus = false;
16669 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16672 var v = this.getValue();
16673 if(String(v) !== String(this.startValue)){
16674 this.fireEvent('change', this, v, this.startValue);
16676 this.fireEvent("blur", this);
16680 * Returns whether or not the field value is currently valid
16681 * @param {Boolean} preventMark True to disable marking the field invalid
16682 * @return {Boolean} True if the value is valid, else false
16684 isValid : function(preventMark){
16688 var restore = this.preventMark;
16689 this.preventMark = preventMark === true;
16690 var v = this.validateValue(this.processValue(this.getRawValue()));
16691 this.preventMark = restore;
16696 * Validates the field value
16697 * @return {Boolean} True if the value is valid, else false
16699 validate : function(){
16700 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16701 this.clearInvalid();
16707 processValue : function(value){
16712 // Subclasses should provide the validation implementation by overriding this
16713 validateValue : function(value){
16718 * Mark this field as invalid
16719 * @param {String} msg The validation message
16721 markInvalid : function(msg){
16722 if(!this.rendered || this.preventMark){ // not rendered
16726 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16728 obj.el.addClass(this.invalidClass);
16729 msg = msg || this.invalidText;
16730 switch(this.msgTarget){
16732 obj.el.dom.qtip = msg;
16733 obj.el.dom.qclass = 'x-form-invalid-tip';
16734 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16735 Roo.QuickTips.enable();
16739 this.el.dom.title = msg;
16743 var elp = this.el.findParent('.x-form-element', 5, true);
16744 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16745 this.errorEl.setWidth(elp.getWidth(true)-20);
16747 this.errorEl.update(msg);
16748 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16751 if(!this.errorIcon){
16752 var elp = this.el.findParent('.x-form-element', 5, true);
16753 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16755 this.alignErrorIcon();
16756 this.errorIcon.dom.qtip = msg;
16757 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16758 this.errorIcon.show();
16759 this.on('resize', this.alignErrorIcon, this);
16762 var t = Roo.getDom(this.msgTarget);
16764 t.style.display = this.msgDisplay;
16767 this.fireEvent('invalid', this, msg);
16771 alignErrorIcon : function(){
16772 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16776 * Clear any invalid styles/messages for this field
16778 clearInvalid : function(){
16779 if(!this.rendered || this.preventMark){ // not rendered
16782 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16784 obj.el.removeClass(this.invalidClass);
16785 switch(this.msgTarget){
16787 obj.el.dom.qtip = '';
16790 this.el.dom.title = '';
16794 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16798 if(this.errorIcon){
16799 this.errorIcon.dom.qtip = '';
16800 this.errorIcon.hide();
16801 this.un('resize', this.alignErrorIcon, this);
16805 var t = Roo.getDom(this.msgTarget);
16807 t.style.display = 'none';
16810 this.fireEvent('valid', this);
16814 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16815 * @return {Mixed} value The field value
16817 getRawValue : function(){
16818 var v = this.el.getValue();
16824 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16825 * @return {Mixed} value The field value
16827 getValue : function(){
16828 var v = this.el.getValue();
16834 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16835 * @param {Mixed} value The value to set
16837 setRawValue : function(v){
16838 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16842 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16843 * @param {Mixed} value The value to set
16845 setValue : function(v){
16848 this.el.dom.value = (v === null || v === undefined ? '' : v);
16853 adjustSize : function(w, h){
16854 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16855 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16859 adjustWidth : function(tag, w){
16860 tag = tag.toLowerCase();
16861 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16862 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16863 if(tag == 'input'){
16866 if(tag == 'textarea'){
16869 }else if(Roo.isOpera){
16870 if(tag == 'input'){
16873 if(tag == 'textarea'){
16883 // anything other than normal should be considered experimental
16884 Roo.form.Field.msgFx = {
16886 show: function(msgEl, f){
16887 msgEl.setDisplayed('block');
16890 hide : function(msgEl, f){
16891 msgEl.setDisplayed(false).update('');
16896 show: function(msgEl, f){
16897 msgEl.slideIn('t', {stopFx:true});
16900 hide : function(msgEl, f){
16901 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16906 show: function(msgEl, f){
16907 msgEl.fixDisplay();
16908 msgEl.alignTo(f.el, 'tl-tr');
16909 msgEl.slideIn('l', {stopFx:true});
16912 hide : function(msgEl, f){
16913 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16918 * Ext JS Library 1.1.1
16919 * Copyright(c) 2006-2007, Ext JS, LLC.
16921 * Originally Released Under LGPL - original licence link has changed is not relivant.
16924 * <script type="text/javascript">
16929 * @class Roo.form.TextField
16930 * @extends Roo.form.Field
16931 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16932 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16934 * Creates a new TextField
16935 * @param {Object} config Configuration options
16937 Roo.form.TextField = function(config){
16938 Roo.form.TextField.superclass.constructor.call(this, config);
16942 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16943 * according to the default logic, but this event provides a hook for the developer to apply additional
16944 * logic at runtime to resize the field if needed.
16945 * @param {Roo.form.Field} this This text field
16946 * @param {Number} width The new field width
16952 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16954 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16958 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16962 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16966 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16970 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16974 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16976 disableKeyFilter : false,
16978 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16982 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16986 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16988 maxLength : Number.MAX_VALUE,
16990 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16992 minLengthText : "The minimum length for this field is {0}",
16994 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16996 maxLengthText : "The maximum length for this field is {0}",
16998 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17000 selectOnFocus : false,
17002 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
17004 allowLeadingSpace : false,
17006 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17008 blankText : "This field is required",
17010 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17011 * If available, this function will be called only after the basic validators all return true, and will be passed the
17012 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17016 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17017 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17018 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
17022 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17026 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17032 initEvents : function()
17034 if (this.emptyText) {
17035 this.el.attr('placeholder', this.emptyText);
17038 Roo.form.TextField.superclass.initEvents.call(this);
17039 if(this.validationEvent == 'keyup'){
17040 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17041 this.el.on('keyup', this.filterValidation, this);
17043 else if(this.validationEvent !== false){
17044 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17047 if(this.selectOnFocus){
17048 this.on("focus", this.preFocus, this);
17050 if (!this.allowLeadingSpace) {
17051 this.on('blur', this.cleanLeadingSpace, this);
17054 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17055 this.el.on("keypress", this.filterKeys, this);
17058 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
17059 this.el.on("click", this.autoSize, this);
17061 if(this.el.is('input[type=password]') && Roo.isSafari){
17062 this.el.on('keydown', this.SafariOnKeyDown, this);
17066 processValue : function(value){
17067 if(this.stripCharsRe){
17068 var newValue = value.replace(this.stripCharsRe, '');
17069 if(newValue !== value){
17070 this.setRawValue(newValue);
17077 filterValidation : function(e){
17078 if(!e.isNavKeyPress()){
17079 this.validationTask.delay(this.validationDelay);
17084 onKeyUp : function(e){
17085 if(!e.isNavKeyPress()){
17089 // private - clean the leading white space
17090 cleanLeadingSpace : function(e)
17092 if ( this.inputType == 'file') {
17096 this.setValue((this.getValue() + '').replace(/^\s+/,''));
17099 * Resets the current field value to the originally-loaded value and clears any validation messages.
17102 reset : function(){
17103 Roo.form.TextField.superclass.reset.call(this);
17107 preFocus : function(){
17109 if(this.selectOnFocus){
17110 this.el.dom.select();
17116 filterKeys : function(e){
17117 var k = e.getKey();
17118 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17121 var c = e.getCharCode(), cc = String.fromCharCode(c);
17122 if(Roo.isIE && (e.isSpecialKey() || !cc)){
17125 if(!this.maskRe.test(cc)){
17130 setValue : function(v){
17132 Roo.form.TextField.superclass.setValue.apply(this, arguments);
17138 * Validates a value according to the field's validation rules and marks the field as invalid
17139 * if the validation fails
17140 * @param {Mixed} value The value to validate
17141 * @return {Boolean} True if the value is valid, else false
17143 validateValue : function(value){
17144 if(value.length < 1) { // if it's blank
17145 if(this.allowBlank){
17146 this.clearInvalid();
17149 this.markInvalid(this.blankText);
17153 if(value.length < this.minLength){
17154 this.markInvalid(String.format(this.minLengthText, this.minLength));
17157 if(value.length > this.maxLength){
17158 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17162 var vt = Roo.form.VTypes;
17163 if(!vt[this.vtype](value, this)){
17164 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17168 if(typeof this.validator == "function"){
17169 var msg = this.validator(value);
17171 this.markInvalid(msg);
17175 if(this.regex && !this.regex.test(value)){
17176 this.markInvalid(this.regexText);
17183 * Selects text in this field
17184 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17185 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17187 selectText : function(start, end){
17188 var v = this.getRawValue();
17190 start = start === undefined ? 0 : start;
17191 end = end === undefined ? v.length : end;
17192 var d = this.el.dom;
17193 if(d.setSelectionRange){
17194 d.setSelectionRange(start, end);
17195 }else if(d.createTextRange){
17196 var range = d.createTextRange();
17197 range.moveStart("character", start);
17198 range.moveEnd("character", v.length-end);
17205 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17206 * This only takes effect if grow = true, and fires the autosize event.
17208 autoSize : function(){
17209 if(!this.grow || !this.rendered){
17213 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17216 var v = el.dom.value;
17217 var d = document.createElement('div');
17218 d.appendChild(document.createTextNode(v));
17222 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17223 this.el.setWidth(w);
17224 this.fireEvent("autosize", this, w);
17228 SafariOnKeyDown : function(event)
17230 // this is a workaround for a password hang bug on chrome/ webkit.
17232 var isSelectAll = false;
17234 if(this.el.dom.selectionEnd > 0){
17235 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17237 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17238 event.preventDefault();
17243 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17245 event.preventDefault();
17246 // this is very hacky as keydown always get's upper case.
17248 var cc = String.fromCharCode(event.getCharCode());
17251 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
17259 * Ext JS Library 1.1.1
17260 * Copyright(c) 2006-2007, Ext JS, LLC.
17262 * Originally Released Under LGPL - original licence link has changed is not relivant.
17265 * <script type="text/javascript">
17269 * @class Roo.form.Hidden
17270 * @extends Roo.form.TextField
17271 * Simple Hidden element used on forms
17273 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17276 * Creates a new Hidden form element.
17277 * @param {Object} config Configuration options
17282 // easy hidden field...
17283 Roo.form.Hidden = function(config){
17284 Roo.form.Hidden.superclass.constructor.call(this, config);
17287 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17289 inputType: 'hidden',
17292 labelSeparator: '',
17294 itemCls : 'x-form-item-display-none'
17302 * Ext JS Library 1.1.1
17303 * Copyright(c) 2006-2007, Ext JS, LLC.
17305 * Originally Released Under LGPL - original licence link has changed is not relivant.
17308 * <script type="text/javascript">
17312 * @class Roo.form.TriggerField
17313 * @extends Roo.form.TextField
17314 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17315 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17316 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17317 * for which you can provide a custom implementation. For example:
17319 var trigger = new Roo.form.TriggerField();
17320 trigger.onTriggerClick = myTriggerFn;
17321 trigger.applyTo('my-field');
17324 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17325 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17326 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17327 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17329 * Create a new TriggerField.
17330 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17331 * to the base TextField)
17333 Roo.form.TriggerField = function(config){
17334 this.mimicing = false;
17335 Roo.form.TriggerField.superclass.constructor.call(this, config);
17338 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17340 * @cfg {String} triggerClass A CSS class to apply to the trigger
17343 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17344 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17346 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17348 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17352 /** @cfg {Boolean} grow @hide */
17353 /** @cfg {Number} growMin @hide */
17354 /** @cfg {Number} growMax @hide */
17360 autoSize: Roo.emptyFn,
17364 deferHeight : true,
17367 actionMode : 'wrap',
17369 onResize : function(w, h){
17370 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17371 if(typeof w == 'number'){
17372 var x = w - this.trigger.getWidth();
17373 this.el.setWidth(this.adjustWidth('input', x));
17374 this.trigger.setStyle('left', x+'px');
17379 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17382 getResizeEl : function(){
17387 getPositionEl : function(){
17392 alignErrorIcon : function(){
17393 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17397 onRender : function(ct, position){
17398 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17399 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17400 this.trigger = this.wrap.createChild(this.triggerConfig ||
17401 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17402 if(this.hideTrigger){
17403 this.trigger.setDisplayed(false);
17405 this.initTrigger();
17407 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17412 initTrigger : function(){
17413 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17414 this.trigger.addClassOnOver('x-form-trigger-over');
17415 this.trigger.addClassOnClick('x-form-trigger-click');
17419 onDestroy : function(){
17421 this.trigger.removeAllListeners();
17422 this.trigger.remove();
17425 this.wrap.remove();
17427 Roo.form.TriggerField.superclass.onDestroy.call(this);
17431 onFocus : function(){
17432 Roo.form.TriggerField.superclass.onFocus.call(this);
17433 if(!this.mimicing){
17434 this.wrap.addClass('x-trigger-wrap-focus');
17435 this.mimicing = true;
17436 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17437 if(this.monitorTab){
17438 this.el.on("keydown", this.checkTab, this);
17444 checkTab : function(e){
17445 if(e.getKey() == e.TAB){
17446 this.triggerBlur();
17451 onBlur : function(){
17456 mimicBlur : function(e, t){
17457 if(!this.wrap.contains(t) && this.validateBlur()){
17458 this.triggerBlur();
17463 triggerBlur : function(){
17464 this.mimicing = false;
17465 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17466 if(this.monitorTab){
17467 this.el.un("keydown", this.checkTab, this);
17469 this.wrap.removeClass('x-trigger-wrap-focus');
17470 Roo.form.TriggerField.superclass.onBlur.call(this);
17474 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17475 validateBlur : function(e, t){
17480 onDisable : function(){
17481 Roo.form.TriggerField.superclass.onDisable.call(this);
17483 this.wrap.addClass('x-item-disabled');
17488 onEnable : function(){
17489 Roo.form.TriggerField.superclass.onEnable.call(this);
17491 this.wrap.removeClass('x-item-disabled');
17496 onShow : function(){
17497 var ae = this.getActionEl();
17500 ae.dom.style.display = '';
17501 ae.dom.style.visibility = 'visible';
17507 onHide : function(){
17508 var ae = this.getActionEl();
17509 ae.dom.style.display = 'none';
17513 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17514 * by an implementing function.
17516 * @param {EventObject} e
17518 onTriggerClick : Roo.emptyFn
17521 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17522 // to be extended by an implementing class. For an example of implementing this class, see the custom
17523 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17524 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17525 initComponent : function(){
17526 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17528 this.triggerConfig = {
17529 tag:'span', cls:'x-form-twin-triggers', cn:[
17530 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17531 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17535 getTrigger : function(index){
17536 return this.triggers[index];
17539 initTrigger : function(){
17540 var ts = this.trigger.select('.x-form-trigger', true);
17541 this.wrap.setStyle('overflow', 'hidden');
17542 var triggerField = this;
17543 ts.each(function(t, all, index){
17544 t.hide = function(){
17545 var w = triggerField.wrap.getWidth();
17546 this.dom.style.display = 'none';
17547 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17549 t.show = function(){
17550 var w = triggerField.wrap.getWidth();
17551 this.dom.style.display = '';
17552 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17554 var triggerIndex = 'Trigger'+(index+1);
17556 if(this['hide'+triggerIndex]){
17557 t.dom.style.display = 'none';
17559 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17560 t.addClassOnOver('x-form-trigger-over');
17561 t.addClassOnClick('x-form-trigger-click');
17563 this.triggers = ts.elements;
17566 onTrigger1Click : Roo.emptyFn,
17567 onTrigger2Click : Roo.emptyFn
17570 * Ext JS Library 1.1.1
17571 * Copyright(c) 2006-2007, Ext JS, LLC.
17573 * Originally Released Under LGPL - original licence link has changed is not relivant.
17576 * <script type="text/javascript">
17580 * @class Roo.form.TextArea
17581 * @extends Roo.form.TextField
17582 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17583 * support for auto-sizing.
17585 * Creates a new TextArea
17586 * @param {Object} config Configuration options
17588 Roo.form.TextArea = function(config){
17589 Roo.form.TextArea.superclass.constructor.call(this, config);
17590 // these are provided exchanges for backwards compat
17591 // minHeight/maxHeight were replaced by growMin/growMax to be
17592 // compatible with TextField growing config values
17593 if(this.minHeight !== undefined){
17594 this.growMin = this.minHeight;
17596 if(this.maxHeight !== undefined){
17597 this.growMax = this.maxHeight;
17601 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17603 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17607 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17611 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17612 * in the field (equivalent to setting overflow: hidden, defaults to false)
17614 preventScrollbars: false,
17616 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17617 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17621 onRender : function(ct, position){
17623 this.defaultAutoCreate = {
17625 style:"width:300px;height:60px;",
17626 autocomplete: "new-password"
17629 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17631 this.textSizeEl = Roo.DomHelper.append(document.body, {
17632 tag: "pre", cls: "x-form-grow-sizer"
17634 if(this.preventScrollbars){
17635 this.el.setStyle("overflow", "hidden");
17637 this.el.setHeight(this.growMin);
17641 onDestroy : function(){
17642 if(this.textSizeEl){
17643 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17645 Roo.form.TextArea.superclass.onDestroy.call(this);
17649 onKeyUp : function(e){
17650 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17656 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17657 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17659 autoSize : function(){
17660 if(!this.grow || !this.textSizeEl){
17664 var v = el.dom.value;
17665 var ts = this.textSizeEl;
17668 ts.appendChild(document.createTextNode(v));
17671 Roo.fly(ts).setWidth(this.el.getWidth());
17673 v = "  ";
17676 v = v.replace(/\n/g, '<p> </p>');
17678 v += " \n ";
17681 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17682 if(h != this.lastHeight){
17683 this.lastHeight = h;
17684 this.el.setHeight(h);
17685 this.fireEvent("autosize", this, h);
17690 * Ext JS Library 1.1.1
17691 * Copyright(c) 2006-2007, Ext JS, LLC.
17693 * Originally Released Under LGPL - original licence link has changed is not relivant.
17696 * <script type="text/javascript">
17701 * @class Roo.form.NumberField
17702 * @extends Roo.form.TextField
17703 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17705 * Creates a new NumberField
17706 * @param {Object} config Configuration options
17708 Roo.form.NumberField = function(config){
17709 Roo.form.NumberField.superclass.constructor.call(this, config);
17712 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17714 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17716 fieldClass: "x-form-field x-form-num-field",
17718 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17720 allowDecimals : true,
17722 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17724 decimalSeparator : ".",
17726 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17728 decimalPrecision : 2,
17730 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17732 allowNegative : true,
17734 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17736 minValue : Number.NEGATIVE_INFINITY,
17738 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17740 maxValue : Number.MAX_VALUE,
17742 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17744 minText : "The minimum value for this field is {0}",
17746 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17748 maxText : "The maximum value for this field is {0}",
17750 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17751 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17753 nanText : "{0} is not a valid number",
17756 initEvents : function(){
17757 Roo.form.NumberField.superclass.initEvents.call(this);
17758 var allowed = "0123456789";
17759 if(this.allowDecimals){
17760 allowed += this.decimalSeparator;
17762 if(this.allowNegative){
17765 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17766 var keyPress = function(e){
17767 var k = e.getKey();
17768 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17771 var c = e.getCharCode();
17772 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17776 this.el.on("keypress", keyPress, this);
17780 validateValue : function(value){
17781 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17784 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17787 var num = this.parseValue(value);
17789 this.markInvalid(String.format(this.nanText, value));
17792 if(num < this.minValue){
17793 this.markInvalid(String.format(this.minText, this.minValue));
17796 if(num > this.maxValue){
17797 this.markInvalid(String.format(this.maxText, this.maxValue));
17803 getValue : function(){
17804 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17808 parseValue : function(value){
17809 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17810 return isNaN(value) ? '' : value;
17814 fixPrecision : function(value){
17815 var nan = isNaN(value);
17816 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17817 return nan ? '' : value;
17819 return parseFloat(value).toFixed(this.decimalPrecision);
17822 setValue : function(v){
17823 v = this.fixPrecision(v);
17824 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17828 decimalPrecisionFcn : function(v){
17829 return Math.floor(v);
17832 beforeBlur : function(){
17833 var v = this.parseValue(this.getRawValue());
17840 * Ext JS Library 1.1.1
17841 * Copyright(c) 2006-2007, Ext JS, LLC.
17843 * Originally Released Under LGPL - original licence link has changed is not relivant.
17846 * <script type="text/javascript">
17850 * @class Roo.form.DateField
17851 * @extends Roo.form.TriggerField
17852 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17854 * Create a new DateField
17855 * @param {Object} config
17857 Roo.form.DateField = function(config)
17859 Roo.form.DateField.superclass.constructor.call(this, config);
17865 * Fires when a date is selected
17866 * @param {Roo.form.DateField} combo This combo box
17867 * @param {Date} date The date selected
17874 if(typeof this.minValue == "string") {
17875 this.minValue = this.parseDate(this.minValue);
17877 if(typeof this.maxValue == "string") {
17878 this.maxValue = this.parseDate(this.maxValue);
17880 this.ddMatch = null;
17881 if(this.disabledDates){
17882 var dd = this.disabledDates;
17884 for(var i = 0; i < dd.length; i++){
17886 if(i != dd.length-1) {
17890 this.ddMatch = new RegExp(re + ")");
17894 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17896 * @cfg {String} format
17897 * The default date format string which can be overriden for localization support. The format must be
17898 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17902 * @cfg {String} altFormats
17903 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17904 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17906 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17908 * @cfg {Array} disabledDays
17909 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17911 disabledDays : null,
17913 * @cfg {String} disabledDaysText
17914 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17916 disabledDaysText : "Disabled",
17918 * @cfg {Array} disabledDates
17919 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17920 * expression so they are very powerful. Some examples:
17922 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17923 * <li>["03/08", "09/16"] would disable those days for every year</li>
17924 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17925 * <li>["03/../2006"] would disable every day in March 2006</li>
17926 * <li>["^03"] would disable every day in every March</li>
17928 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17929 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17931 disabledDates : null,
17933 * @cfg {String} disabledDatesText
17934 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17936 disabledDatesText : "Disabled",
17938 * @cfg {Date/String} minValue
17939 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17940 * valid format (defaults to null).
17944 * @cfg {Date/String} maxValue
17945 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17946 * valid format (defaults to null).
17950 * @cfg {String} minText
17951 * The error text to display when the date in the cell is before minValue (defaults to
17952 * 'The date in this field must be after {minValue}').
17954 minText : "The date in this field must be equal to or after {0}",
17956 * @cfg {String} maxText
17957 * The error text to display when the date in the cell is after maxValue (defaults to
17958 * 'The date in this field must be before {maxValue}').
17960 maxText : "The date in this field must be equal to or before {0}",
17962 * @cfg {String} invalidText
17963 * The error text to display when the date in the field is invalid (defaults to
17964 * '{value} is not a valid date - it must be in the format {format}').
17966 invalidText : "{0} is not a valid date - it must be in the format {1}",
17968 * @cfg {String} triggerClass
17969 * An additional CSS class used to style the trigger button. The trigger will always get the
17970 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17971 * which displays a calendar icon).
17973 triggerClass : 'x-form-date-trigger',
17977 * @cfg {Boolean} useIso
17978 * if enabled, then the date field will use a hidden field to store the
17979 * real value as iso formated date. default (false)
17983 * @cfg {String/Object} autoCreate
17984 * A DomHelper element spec, or true for a default element spec (defaults to
17985 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17988 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17991 hiddenField: false,
17993 onRender : function(ct, position)
17995 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17997 //this.el.dom.removeAttribute('name');
17998 Roo.log("Changing name?");
17999 this.el.dom.setAttribute('name', this.name + '____hidden___' );
18000 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18002 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18003 // prevent input submission
18004 this.hiddenName = this.name;
18011 validateValue : function(value)
18013 value = this.formatDate(value);
18014 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18015 Roo.log('super failed');
18018 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18021 var svalue = value;
18022 value = this.parseDate(value);
18024 Roo.log('parse date failed' + svalue);
18025 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18028 var time = value.getTime();
18029 if(this.minValue && time < this.minValue.getTime()){
18030 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18033 if(this.maxValue && time > this.maxValue.getTime()){
18034 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18037 if(this.disabledDays){
18038 var day = value.getDay();
18039 for(var i = 0; i < this.disabledDays.length; i++) {
18040 if(day === this.disabledDays[i]){
18041 this.markInvalid(this.disabledDaysText);
18046 var fvalue = this.formatDate(value);
18047 if(this.ddMatch && this.ddMatch.test(fvalue)){
18048 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18055 // Provides logic to override the default TriggerField.validateBlur which just returns true
18056 validateBlur : function(){
18057 return !this.menu || !this.menu.isVisible();
18060 getName: function()
18062 // returns hidden if it's set..
18063 if (!this.rendered) {return ''};
18064 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
18069 * Returns the current date value of the date field.
18070 * @return {Date} The date value
18072 getValue : function(){
18074 return this.hiddenField ?
18075 this.hiddenField.value :
18076 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18080 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18081 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18082 * (the default format used is "m/d/y").
18085 //All of these calls set the same date value (May 4, 2006)
18087 //Pass a date object:
18088 var dt = new Date('5/4/06');
18089 dateField.setValue(dt);
18091 //Pass a date string (default format):
18092 dateField.setValue('5/4/06');
18094 //Pass a date string (custom format):
18095 dateField.format = 'Y-m-d';
18096 dateField.setValue('2006-5-4');
18098 * @param {String/Date} date The date or valid date string
18100 setValue : function(date){
18101 if (this.hiddenField) {
18102 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18104 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18105 // make sure the value field is always stored as a date..
18106 this.value = this.parseDate(date);
18112 parseDate : function(value){
18113 if(!value || value instanceof Date){
18116 var v = Date.parseDate(value, this.format);
18117 if (!v && this.useIso) {
18118 v = Date.parseDate(value, 'Y-m-d');
18120 if(!v && this.altFormats){
18121 if(!this.altFormatsArray){
18122 this.altFormatsArray = this.altFormats.split("|");
18124 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18125 v = Date.parseDate(value, this.altFormatsArray[i]);
18132 formatDate : function(date, fmt){
18133 return (!date || !(date instanceof Date)) ?
18134 date : date.dateFormat(fmt || this.format);
18139 select: function(m, d){
18142 this.fireEvent('select', this, d);
18144 show : function(){ // retain focus styling
18148 this.focus.defer(10, this);
18149 var ml = this.menuListeners;
18150 this.menu.un("select", ml.select, this);
18151 this.menu.un("show", ml.show, this);
18152 this.menu.un("hide", ml.hide, this);
18157 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18158 onTriggerClick : function(){
18162 if(this.menu == null){
18163 this.menu = new Roo.menu.DateMenu();
18165 Roo.apply(this.menu.picker, {
18166 showClear: this.allowBlank,
18167 minDate : this.minValue,
18168 maxDate : this.maxValue,
18169 disabledDatesRE : this.ddMatch,
18170 disabledDatesText : this.disabledDatesText,
18171 disabledDays : this.disabledDays,
18172 disabledDaysText : this.disabledDaysText,
18173 format : this.useIso ? 'Y-m-d' : this.format,
18174 minText : String.format(this.minText, this.formatDate(this.minValue)),
18175 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18177 this.menu.on(Roo.apply({}, this.menuListeners, {
18180 this.menu.picker.setValue(this.getValue() || new Date());
18181 this.menu.show(this.el, "tl-bl?");
18184 beforeBlur : function(){
18185 var v = this.parseDate(this.getRawValue());
18195 isDirty : function() {
18196 if(this.disabled) {
18200 if(typeof(this.startValue) === 'undefined'){
18204 return String(this.getValue()) !== String(this.startValue);
18208 cleanLeadingSpace : function(e)
18215 * Ext JS Library 1.1.1
18216 * Copyright(c) 2006-2007, Ext JS, LLC.
18218 * Originally Released Under LGPL - original licence link has changed is not relivant.
18221 * <script type="text/javascript">
18225 * @class Roo.form.MonthField
18226 * @extends Roo.form.TriggerField
18227 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18229 * Create a new MonthField
18230 * @param {Object} config
18232 Roo.form.MonthField = function(config){
18234 Roo.form.MonthField.superclass.constructor.call(this, config);
18240 * Fires when a date is selected
18241 * @param {Roo.form.MonthFieeld} combo This combo box
18242 * @param {Date} date The date selected
18249 if(typeof this.minValue == "string") {
18250 this.minValue = this.parseDate(this.minValue);
18252 if(typeof this.maxValue == "string") {
18253 this.maxValue = this.parseDate(this.maxValue);
18255 this.ddMatch = null;
18256 if(this.disabledDates){
18257 var dd = this.disabledDates;
18259 for(var i = 0; i < dd.length; i++){
18261 if(i != dd.length-1) {
18265 this.ddMatch = new RegExp(re + ")");
18269 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
18271 * @cfg {String} format
18272 * The default date format string which can be overriden for localization support. The format must be
18273 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18277 * @cfg {String} altFormats
18278 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18279 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18281 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18283 * @cfg {Array} disabledDays
18284 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18286 disabledDays : [0,1,2,3,4,5,6],
18288 * @cfg {String} disabledDaysText
18289 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18291 disabledDaysText : "Disabled",
18293 * @cfg {Array} disabledDates
18294 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18295 * expression so they are very powerful. Some examples:
18297 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18298 * <li>["03/08", "09/16"] would disable those days for every year</li>
18299 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18300 * <li>["03/../2006"] would disable every day in March 2006</li>
18301 * <li>["^03"] would disable every day in every March</li>
18303 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18304 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18306 disabledDates : null,
18308 * @cfg {String} disabledDatesText
18309 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18311 disabledDatesText : "Disabled",
18313 * @cfg {Date/String} minValue
18314 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18315 * valid format (defaults to null).
18319 * @cfg {Date/String} maxValue
18320 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18321 * valid format (defaults to null).
18325 * @cfg {String} minText
18326 * The error text to display when the date in the cell is before minValue (defaults to
18327 * 'The date in this field must be after {minValue}').
18329 minText : "The date in this field must be equal to or after {0}",
18331 * @cfg {String} maxTextf
18332 * The error text to display when the date in the cell is after maxValue (defaults to
18333 * 'The date in this field must be before {maxValue}').
18335 maxText : "The date in this field must be equal to or before {0}",
18337 * @cfg {String} invalidText
18338 * The error text to display when the date in the field is invalid (defaults to
18339 * '{value} is not a valid date - it must be in the format {format}').
18341 invalidText : "{0} is not a valid date - it must be in the format {1}",
18343 * @cfg {String} triggerClass
18344 * An additional CSS class used to style the trigger button. The trigger will always get the
18345 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18346 * which displays a calendar icon).
18348 triggerClass : 'x-form-date-trigger',
18352 * @cfg {Boolean} useIso
18353 * if enabled, then the date field will use a hidden field to store the
18354 * real value as iso formated date. default (true)
18358 * @cfg {String/Object} autoCreate
18359 * A DomHelper element spec, or true for a default element spec (defaults to
18360 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18363 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18366 hiddenField: false,
18368 hideMonthPicker : false,
18370 onRender : function(ct, position)
18372 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18374 this.el.dom.removeAttribute('name');
18375 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18377 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18378 // prevent input submission
18379 this.hiddenName = this.name;
18386 validateValue : function(value)
18388 value = this.formatDate(value);
18389 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18392 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18395 var svalue = value;
18396 value = this.parseDate(value);
18398 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18401 var time = value.getTime();
18402 if(this.minValue && time < this.minValue.getTime()){
18403 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18406 if(this.maxValue && time > this.maxValue.getTime()){
18407 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18410 /*if(this.disabledDays){
18411 var day = value.getDay();
18412 for(var i = 0; i < this.disabledDays.length; i++) {
18413 if(day === this.disabledDays[i]){
18414 this.markInvalid(this.disabledDaysText);
18420 var fvalue = this.formatDate(value);
18421 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18422 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18430 // Provides logic to override the default TriggerField.validateBlur which just returns true
18431 validateBlur : function(){
18432 return !this.menu || !this.menu.isVisible();
18436 * Returns the current date value of the date field.
18437 * @return {Date} The date value
18439 getValue : function(){
18443 return this.hiddenField ?
18444 this.hiddenField.value :
18445 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18449 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18450 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18451 * (the default format used is "m/d/y").
18454 //All of these calls set the same date value (May 4, 2006)
18456 //Pass a date object:
18457 var dt = new Date('5/4/06');
18458 monthField.setValue(dt);
18460 //Pass a date string (default format):
18461 monthField.setValue('5/4/06');
18463 //Pass a date string (custom format):
18464 monthField.format = 'Y-m-d';
18465 monthField.setValue('2006-5-4');
18467 * @param {String/Date} date The date or valid date string
18469 setValue : function(date){
18470 Roo.log('month setValue' + date);
18471 // can only be first of month..
18473 var val = this.parseDate(date);
18475 if (this.hiddenField) {
18476 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18478 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18479 this.value = this.parseDate(date);
18483 parseDate : function(value){
18484 if(!value || value instanceof Date){
18485 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18488 var v = Date.parseDate(value, this.format);
18489 if (!v && this.useIso) {
18490 v = Date.parseDate(value, 'Y-m-d');
18494 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18498 if(!v && this.altFormats){
18499 if(!this.altFormatsArray){
18500 this.altFormatsArray = this.altFormats.split("|");
18502 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18503 v = Date.parseDate(value, this.altFormatsArray[i]);
18510 formatDate : function(date, fmt){
18511 return (!date || !(date instanceof Date)) ?
18512 date : date.dateFormat(fmt || this.format);
18517 select: function(m, d){
18519 this.fireEvent('select', this, d);
18521 show : function(){ // retain focus styling
18525 this.focus.defer(10, this);
18526 var ml = this.menuListeners;
18527 this.menu.un("select", ml.select, this);
18528 this.menu.un("show", ml.show, this);
18529 this.menu.un("hide", ml.hide, this);
18533 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18534 onTriggerClick : function(){
18538 if(this.menu == null){
18539 this.menu = new Roo.menu.DateMenu();
18543 Roo.apply(this.menu.picker, {
18545 showClear: this.allowBlank,
18546 minDate : this.minValue,
18547 maxDate : this.maxValue,
18548 disabledDatesRE : this.ddMatch,
18549 disabledDatesText : this.disabledDatesText,
18551 format : this.useIso ? 'Y-m-d' : this.format,
18552 minText : String.format(this.minText, this.formatDate(this.minValue)),
18553 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18556 this.menu.on(Roo.apply({}, this.menuListeners, {
18564 // hide month picker get's called when we called by 'before hide';
18566 var ignorehide = true;
18567 p.hideMonthPicker = function(disableAnim){
18571 if(this.monthPicker){
18572 Roo.log("hideMonthPicker called");
18573 if(disableAnim === true){
18574 this.monthPicker.hide();
18576 this.monthPicker.slideOut('t', {duration:.2});
18577 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18578 p.fireEvent("select", this, this.value);
18584 Roo.log('picker set value');
18585 Roo.log(this.getValue());
18586 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18587 m.show(this.el, 'tl-bl?');
18588 ignorehide = false;
18589 // this will trigger hideMonthPicker..
18592 // hidden the day picker
18593 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18599 p.showMonthPicker.defer(100, p);
18605 beforeBlur : function(){
18606 var v = this.parseDate(this.getRawValue());
18612 /** @cfg {Boolean} grow @hide */
18613 /** @cfg {Number} growMin @hide */
18614 /** @cfg {Number} growMax @hide */
18621 * Ext JS Library 1.1.1
18622 * Copyright(c) 2006-2007, Ext JS, LLC.
18624 * Originally Released Under LGPL - original licence link has changed is not relivant.
18627 * <script type="text/javascript">
18632 * @class Roo.form.ComboBox
18633 * @extends Roo.form.TriggerField
18634 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18636 * Create a new ComboBox.
18637 * @param {Object} config Configuration options
18639 Roo.form.ComboBox = function(config){
18640 Roo.form.ComboBox.superclass.constructor.call(this, config);
18644 * Fires when the dropdown list is expanded
18645 * @param {Roo.form.ComboBox} combo This combo box
18650 * Fires when the dropdown list is collapsed
18651 * @param {Roo.form.ComboBox} combo This combo box
18655 * @event beforeselect
18656 * Fires before a list item is selected. Return false to cancel the selection.
18657 * @param {Roo.form.ComboBox} combo This combo box
18658 * @param {Roo.data.Record} record The data record returned from the underlying store
18659 * @param {Number} index The index of the selected item in the dropdown list
18661 'beforeselect' : true,
18664 * Fires when a list item is selected
18665 * @param {Roo.form.ComboBox} combo This combo box
18666 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18667 * @param {Number} index The index of the selected item in the dropdown list
18671 * @event beforequery
18672 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18673 * The event object passed has these properties:
18674 * @param {Roo.form.ComboBox} combo This combo box
18675 * @param {String} query The query
18676 * @param {Boolean} forceAll true to force "all" query
18677 * @param {Boolean} cancel true to cancel the query
18678 * @param {Object} e The query event object
18680 'beforequery': true,
18683 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18684 * @param {Roo.form.ComboBox} combo This combo box
18689 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18690 * @param {Roo.form.ComboBox} combo This combo box
18691 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18697 if(this.transform){
18698 this.allowDomMove = false;
18699 var s = Roo.getDom(this.transform);
18700 if(!this.hiddenName){
18701 this.hiddenName = s.name;
18704 this.mode = 'local';
18705 var d = [], opts = s.options;
18706 for(var i = 0, len = opts.length;i < len; i++){
18708 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18710 this.value = value;
18712 d.push([value, o.text]);
18714 this.store = new Roo.data.SimpleStore({
18716 fields: ['value', 'text'],
18719 this.valueField = 'value';
18720 this.displayField = 'text';
18722 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18723 if(!this.lazyRender){
18724 this.target = true;
18725 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18726 s.parentNode.removeChild(s); // remove it
18727 this.render(this.el.parentNode);
18729 s.parentNode.removeChild(s); // remove it
18734 this.store = Roo.factory(this.store, Roo.data);
18737 this.selectedIndex = -1;
18738 if(this.mode == 'local'){
18739 if(config.queryDelay === undefined){
18740 this.queryDelay = 10;
18742 if(config.minChars === undefined){
18748 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18750 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18753 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18754 * rendering into an Roo.Editor, defaults to false)
18757 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18758 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18761 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18764 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18765 * the dropdown list (defaults to undefined, with no header element)
18769 * @cfg {String/Roo.Template} tpl The template to use to render the output
18773 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18775 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18777 listWidth: undefined,
18779 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18780 * mode = 'remote' or 'text' if mode = 'local')
18782 displayField: undefined,
18784 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18785 * mode = 'remote' or 'value' if mode = 'local').
18786 * Note: use of a valueField requires the user make a selection
18787 * in order for a value to be mapped.
18789 valueField: undefined,
18793 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18794 * field's data value (defaults to the underlying DOM element's name)
18796 hiddenName: undefined,
18798 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18802 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18804 selectedClass: 'x-combo-selected',
18806 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18807 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18808 * which displays a downward arrow icon).
18810 triggerClass : 'x-form-arrow-trigger',
18812 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18816 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18817 * anchor positions (defaults to 'tl-bl')
18819 listAlign: 'tl-bl?',
18821 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18825 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18826 * query specified by the allQuery config option (defaults to 'query')
18828 triggerAction: 'query',
18830 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18831 * (defaults to 4, does not apply if editable = false)
18835 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18836 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18840 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18841 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18845 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18846 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18850 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18851 * when editable = true (defaults to false)
18853 selectOnFocus:false,
18855 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18857 queryParam: 'query',
18859 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18860 * when mode = 'remote' (defaults to 'Loading...')
18862 loadingText: 'Loading...',
18864 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18868 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18872 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18873 * traditional select (defaults to true)
18877 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18881 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18885 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18886 * listWidth has a higher value)
18890 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18891 * allow the user to set arbitrary text into the field (defaults to false)
18893 forceSelection:false,
18895 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18896 * if typeAhead = true (defaults to 250)
18898 typeAheadDelay : 250,
18900 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18901 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18903 valueNotFoundText : undefined,
18905 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18907 blockFocus : false,
18910 * @cfg {Boolean} disableClear Disable showing of clear button.
18912 disableClear : false,
18914 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18916 alwaysQuery : false,
18922 // element that contains real text value.. (when hidden is used..)
18925 onRender : function(ct, position)
18927 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18929 if(this.hiddenName){
18930 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18932 this.hiddenField.value =
18933 this.hiddenValue !== undefined ? this.hiddenValue :
18934 this.value !== undefined ? this.value : '';
18936 // prevent input submission
18937 this.el.dom.removeAttribute('name');
18943 this.el.dom.setAttribute('autocomplete', 'off');
18946 var cls = 'x-combo-list';
18948 this.list = new Roo.Layer({
18949 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18952 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18953 this.list.setWidth(lw);
18954 this.list.swallowEvent('mousewheel');
18955 this.assetHeight = 0;
18958 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18959 this.assetHeight += this.header.getHeight();
18962 this.innerList = this.list.createChild({cls:cls+'-inner'});
18963 this.innerList.on('mouseover', this.onViewOver, this);
18964 this.innerList.on('mousemove', this.onViewMove, this);
18965 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18967 if(this.allowBlank && !this.pageSize && !this.disableClear){
18968 this.footer = this.list.createChild({cls:cls+'-ft'});
18969 this.pageTb = new Roo.Toolbar(this.footer);
18973 this.footer = this.list.createChild({cls:cls+'-ft'});
18974 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18975 {pageSize: this.pageSize});
18979 if (this.pageTb && this.allowBlank && !this.disableClear) {
18981 this.pageTb.add(new Roo.Toolbar.Fill(), {
18982 cls: 'x-btn-icon x-btn-clear',
18984 handler: function()
18987 _this.clearValue();
18988 _this.onSelect(false, -1);
18993 this.assetHeight += this.footer.getHeight();
18998 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19001 this.view = new Roo.View(this.innerList, this.tpl, {
19004 selectedClass: this.selectedClass
19007 this.view.on('click', this.onViewClick, this);
19009 this.store.on('beforeload', this.onBeforeLoad, this);
19010 this.store.on('load', this.onLoad, this);
19011 this.store.on('loadexception', this.onLoadException, this);
19013 if(this.resizable){
19014 this.resizer = new Roo.Resizable(this.list, {
19015 pinned:true, handles:'se'
19017 this.resizer.on('resize', function(r, w, h){
19018 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19019 this.listWidth = w;
19020 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19021 this.restrictHeight();
19023 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19025 if(!this.editable){
19026 this.editable = true;
19027 this.setEditable(false);
19031 if (typeof(this.events.add.listeners) != 'undefined') {
19033 this.addicon = this.wrap.createChild(
19034 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
19036 this.addicon.on('click', function(e) {
19037 this.fireEvent('add', this);
19040 if (typeof(this.events.edit.listeners) != 'undefined') {
19042 this.editicon = this.wrap.createChild(
19043 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
19044 if (this.addicon) {
19045 this.editicon.setStyle('margin-left', '40px');
19047 this.editicon.on('click', function(e) {
19049 // we fire even if inothing is selected..
19050 this.fireEvent('edit', this, this.lastData );
19060 initEvents : function(){
19061 Roo.form.ComboBox.superclass.initEvents.call(this);
19063 this.keyNav = new Roo.KeyNav(this.el, {
19064 "up" : function(e){
19065 this.inKeyMode = true;
19069 "down" : function(e){
19070 if(!this.isExpanded()){
19071 this.onTriggerClick();
19073 this.inKeyMode = true;
19078 "enter" : function(e){
19079 this.onViewClick();
19083 "esc" : function(e){
19087 "tab" : function(e){
19088 this.onViewClick(false);
19089 this.fireEvent("specialkey", this, e);
19095 doRelay : function(foo, bar, hname){
19096 if(hname == 'down' || this.scope.isExpanded()){
19097 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19104 this.queryDelay = Math.max(this.queryDelay || 10,
19105 this.mode == 'local' ? 10 : 250);
19106 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19107 if(this.typeAhead){
19108 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19110 if(this.editable !== false){
19111 this.el.on("keyup", this.onKeyUp, this);
19113 if(this.forceSelection){
19114 this.on('blur', this.doForce, this);
19118 onDestroy : function(){
19120 this.view.setStore(null);
19121 this.view.el.removeAllListeners();
19122 this.view.el.remove();
19123 this.view.purgeListeners();
19126 this.list.destroy();
19129 this.store.un('beforeload', this.onBeforeLoad, this);
19130 this.store.un('load', this.onLoad, this);
19131 this.store.un('loadexception', this.onLoadException, this);
19133 Roo.form.ComboBox.superclass.onDestroy.call(this);
19137 fireKey : function(e){
19138 if(e.isNavKeyPress() && !this.list.isVisible()){
19139 this.fireEvent("specialkey", this, e);
19144 onResize: function(w, h){
19145 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19147 if(typeof w != 'number'){
19148 // we do not handle it!?!?
19151 var tw = this.trigger.getWidth();
19152 tw += this.addicon ? this.addicon.getWidth() : 0;
19153 tw += this.editicon ? this.editicon.getWidth() : 0;
19155 this.el.setWidth( this.adjustWidth('input', x));
19157 this.trigger.setStyle('left', x+'px');
19159 if(this.list && this.listWidth === undefined){
19160 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19161 this.list.setWidth(lw);
19162 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19170 * Allow or prevent the user from directly editing the field text. If false is passed,
19171 * the user will only be able to select from the items defined in the dropdown list. This method
19172 * is the runtime equivalent of setting the 'editable' config option at config time.
19173 * @param {Boolean} value True to allow the user to directly edit the field text
19175 setEditable : function(value){
19176 if(value == this.editable){
19179 this.editable = value;
19181 this.el.dom.setAttribute('readOnly', true);
19182 this.el.on('mousedown', this.onTriggerClick, this);
19183 this.el.addClass('x-combo-noedit');
19185 this.el.dom.setAttribute('readOnly', false);
19186 this.el.un('mousedown', this.onTriggerClick, this);
19187 this.el.removeClass('x-combo-noedit');
19192 onBeforeLoad : function(){
19193 if(!this.hasFocus){
19196 this.innerList.update(this.loadingText ?
19197 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19198 this.restrictHeight();
19199 this.selectedIndex = -1;
19203 onLoad : function(){
19204 if(!this.hasFocus){
19207 if(this.store.getCount() > 0){
19209 this.restrictHeight();
19210 if(this.lastQuery == this.allQuery){
19212 this.el.dom.select();
19214 if(!this.selectByValue(this.value, true)){
19215 this.select(0, true);
19219 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19220 this.taTask.delay(this.typeAheadDelay);
19224 this.onEmptyResults();
19229 onLoadException : function()
19232 Roo.log(this.store.reader.jsonData);
19233 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19234 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19240 onTypeAhead : function(){
19241 if(this.store.getCount() > 0){
19242 var r = this.store.getAt(0);
19243 var newValue = r.data[this.displayField];
19244 var len = newValue.length;
19245 var selStart = this.getRawValue().length;
19246 if(selStart != len){
19247 this.setRawValue(newValue);
19248 this.selectText(selStart, newValue.length);
19254 onSelect : function(record, index){
19255 if(this.fireEvent('beforeselect', this, record, index) !== false){
19256 this.setFromData(index > -1 ? record.data : false);
19258 this.fireEvent('select', this, record, index);
19263 * Returns the currently selected field value or empty string if no value is set.
19264 * @return {String} value The selected value
19266 getValue : function(){
19267 if(this.valueField){
19268 return typeof this.value != 'undefined' ? this.value : '';
19270 return Roo.form.ComboBox.superclass.getValue.call(this);
19274 * Clears any text/value currently set in the field
19276 clearValue : function(){
19277 if(this.hiddenField){
19278 this.hiddenField.value = '';
19281 this.setRawValue('');
19282 this.lastSelectionText = '';
19287 * Sets the specified value into the field. If the value finds a match, the corresponding record text
19288 * will be displayed in the field. If the value does not match the data value of an existing item,
19289 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19290 * Otherwise the field will be blank (although the value will still be set).
19291 * @param {String} value The value to match
19293 setValue : function(v){
19295 if(this.valueField){
19296 var r = this.findRecord(this.valueField, v);
19298 text = r.data[this.displayField];
19299 }else if(this.valueNotFoundText !== undefined){
19300 text = this.valueNotFoundText;
19303 this.lastSelectionText = text;
19304 if(this.hiddenField){
19305 this.hiddenField.value = v;
19307 Roo.form.ComboBox.superclass.setValue.call(this, text);
19311 * @property {Object} the last set data for the element
19316 * Sets the value of the field based on a object which is related to the record format for the store.
19317 * @param {Object} value the value to set as. or false on reset?
19319 setFromData : function(o){
19320 var dv = ''; // display value
19321 var vv = ''; // value value..
19323 if (this.displayField) {
19324 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19326 // this is an error condition!!!
19327 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19330 if(this.valueField){
19331 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19333 if(this.hiddenField){
19334 this.hiddenField.value = vv;
19336 this.lastSelectionText = dv;
19337 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19341 // no hidden field.. - we store the value in 'value', but still display
19342 // display field!!!!
19343 this.lastSelectionText = dv;
19344 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19350 reset : function(){
19351 // overridden so that last data is reset..
19352 this.setValue(this.resetValue);
19353 this.originalValue = this.getValue();
19354 this.clearInvalid();
19355 this.lastData = false;
19357 this.view.clearSelections();
19361 findRecord : function(prop, value){
19363 if(this.store.getCount() > 0){
19364 this.store.each(function(r){
19365 if(r.data[prop] == value){
19375 getName: function()
19377 // returns hidden if it's set..
19378 if (!this.rendered) {return ''};
19379 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19383 onViewMove : function(e, t){
19384 this.inKeyMode = false;
19388 onViewOver : function(e, t){
19389 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19392 var item = this.view.findItemFromChild(t);
19394 var index = this.view.indexOf(item);
19395 this.select(index, false);
19400 onViewClick : function(doFocus)
19402 var index = this.view.getSelectedIndexes()[0];
19403 var r = this.store.getAt(index);
19405 this.onSelect(r, index);
19407 if(doFocus !== false && !this.blockFocus){
19413 restrictHeight : function(){
19414 this.innerList.dom.style.height = '';
19415 var inner = this.innerList.dom;
19416 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19417 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19418 this.list.beginUpdate();
19419 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19420 this.list.alignTo(this.el, this.listAlign);
19421 this.list.endUpdate();
19425 onEmptyResults : function(){
19430 * Returns true if the dropdown list is expanded, else false.
19432 isExpanded : function(){
19433 return this.list.isVisible();
19437 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19438 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19439 * @param {String} value The data value of the item to select
19440 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19441 * selected item if it is not currently in view (defaults to true)
19442 * @return {Boolean} True if the value matched an item in the list, else false
19444 selectByValue : function(v, scrollIntoView){
19445 if(v !== undefined && v !== null){
19446 var r = this.findRecord(this.valueField || this.displayField, v);
19448 this.select(this.store.indexOf(r), scrollIntoView);
19456 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19457 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19458 * @param {Number} index The zero-based index of the list item to select
19459 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19460 * selected item if it is not currently in view (defaults to true)
19462 select : function(index, scrollIntoView){
19463 this.selectedIndex = index;
19464 this.view.select(index);
19465 if(scrollIntoView !== false){
19466 var el = this.view.getNode(index);
19468 this.innerList.scrollChildIntoView(el, false);
19474 selectNext : function(){
19475 var ct = this.store.getCount();
19477 if(this.selectedIndex == -1){
19479 }else if(this.selectedIndex < ct-1){
19480 this.select(this.selectedIndex+1);
19486 selectPrev : function(){
19487 var ct = this.store.getCount();
19489 if(this.selectedIndex == -1){
19491 }else if(this.selectedIndex != 0){
19492 this.select(this.selectedIndex-1);
19498 onKeyUp : function(e){
19499 if(this.editable !== false && !e.isSpecialKey()){
19500 this.lastKey = e.getKey();
19501 this.dqTask.delay(this.queryDelay);
19506 validateBlur : function(){
19507 return !this.list || !this.list.isVisible();
19511 initQuery : function(){
19512 this.doQuery(this.getRawValue());
19516 doForce : function(){
19517 if(this.el.dom.value.length > 0){
19518 this.el.dom.value =
19519 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19525 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19526 * query allowing the query action to be canceled if needed.
19527 * @param {String} query The SQL query to execute
19528 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19529 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19530 * saved in the current store (defaults to false)
19532 doQuery : function(q, forceAll){
19533 if(q === undefined || q === null){
19538 forceAll: forceAll,
19542 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19546 forceAll = qe.forceAll;
19547 if(forceAll === true || (q.length >= this.minChars)){
19548 if(this.lastQuery != q || this.alwaysQuery){
19549 this.lastQuery = q;
19550 if(this.mode == 'local'){
19551 this.selectedIndex = -1;
19553 this.store.clearFilter();
19555 this.store.filter(this.displayField, q);
19559 this.store.baseParams[this.queryParam] = q;
19561 params: this.getParams(q)
19566 this.selectedIndex = -1;
19573 getParams : function(q){
19575 //p[this.queryParam] = q;
19578 p.limit = this.pageSize;
19584 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19586 collapse : function(){
19587 if(!this.isExpanded()){
19591 Roo.get(document).un('mousedown', this.collapseIf, this);
19592 Roo.get(document).un('mousewheel', this.collapseIf, this);
19593 if (!this.editable) {
19594 Roo.get(document).un('keydown', this.listKeyPress, this);
19596 this.fireEvent('collapse', this);
19600 collapseIf : function(e){
19601 if(!e.within(this.wrap) && !e.within(this.list)){
19607 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19609 expand : function(){
19610 if(this.isExpanded() || !this.hasFocus){
19613 this.list.alignTo(this.el, this.listAlign);
19615 Roo.get(document).on('mousedown', this.collapseIf, this);
19616 Roo.get(document).on('mousewheel', this.collapseIf, this);
19617 if (!this.editable) {
19618 Roo.get(document).on('keydown', this.listKeyPress, this);
19621 this.fireEvent('expand', this);
19625 // Implements the default empty TriggerField.onTriggerClick function
19626 onTriggerClick : function(){
19630 if(this.isExpanded()){
19632 if (!this.blockFocus) {
19637 this.hasFocus = true;
19638 if(this.triggerAction == 'all') {
19639 this.doQuery(this.allQuery, true);
19641 this.doQuery(this.getRawValue());
19643 if (!this.blockFocus) {
19648 listKeyPress : function(e)
19650 //Roo.log('listkeypress');
19651 // scroll to first matching element based on key pres..
19652 if (e.isSpecialKey()) {
19655 var k = String.fromCharCode(e.getKey()).toUpperCase();
19658 var csel = this.view.getSelectedNodes();
19659 var cselitem = false;
19661 var ix = this.view.indexOf(csel[0]);
19662 cselitem = this.store.getAt(ix);
19663 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19669 this.store.each(function(v) {
19671 // start at existing selection.
19672 if (cselitem.id == v.id) {
19678 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19679 match = this.store.indexOf(v);
19684 if (match === false) {
19685 return true; // no more action?
19688 this.view.select(match);
19689 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19690 sn.scrollIntoView(sn.dom.parentNode, false);
19694 * @cfg {Boolean} grow
19698 * @cfg {Number} growMin
19702 * @cfg {Number} growMax
19710 * Copyright(c) 2010-2012, Roo J Solutions Limited
19717 * @class Roo.form.ComboBoxArray
19718 * @extends Roo.form.TextField
19719 * A facebook style adder... for lists of email / people / countries etc...
19720 * pick multiple items from a combo box, and shows each one.
19722 * Fred [x] Brian [x] [Pick another |v]
19725 * For this to work: it needs various extra information
19726 * - normal combo problay has
19728 * + displayField, valueField
19730 * For our purpose...
19733 * If we change from 'extends' to wrapping...
19740 * Create a new ComboBoxArray.
19741 * @param {Object} config Configuration options
19745 Roo.form.ComboBoxArray = function(config)
19749 * @event beforeremove
19750 * Fires before remove the value from the list
19751 * @param {Roo.form.ComboBoxArray} _self This combo box array
19752 * @param {Roo.form.ComboBoxArray.Item} item removed item
19754 'beforeremove' : true,
19757 * Fires when remove the value from the list
19758 * @param {Roo.form.ComboBoxArray} _self This combo box array
19759 * @param {Roo.form.ComboBoxArray.Item} item removed item
19766 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19768 this.items = new Roo.util.MixedCollection(false);
19770 // construct the child combo...
19780 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19783 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19788 // behavies liek a hiddne field
19789 inputType: 'hidden',
19791 * @cfg {Number} width The width of the box that displays the selected element
19798 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19802 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19804 hiddenName : false,
19807 // private the array of items that are displayed..
19809 // private - the hidden field el.
19811 // private - the filed el..
19814 //validateValue : function() { return true; }, // all values are ok!
19815 //onAddClick: function() { },
19817 onRender : function(ct, position)
19820 // create the standard hidden element
19821 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19824 // give fake names to child combo;
19825 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19826 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19828 this.combo = Roo.factory(this.combo, Roo.form);
19829 this.combo.onRender(ct, position);
19830 if (typeof(this.combo.width) != 'undefined') {
19831 this.combo.onResize(this.combo.width,0);
19834 this.combo.initEvents();
19836 // assigned so form know we need to do this..
19837 this.store = this.combo.store;
19838 this.valueField = this.combo.valueField;
19839 this.displayField = this.combo.displayField ;
19842 this.combo.wrap.addClass('x-cbarray-grp');
19844 var cbwrap = this.combo.wrap.createChild(
19845 {tag: 'div', cls: 'x-cbarray-cb'},
19850 this.hiddenEl = this.combo.wrap.createChild({
19851 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19853 this.el = this.combo.wrap.createChild({
19854 tag: 'input', type:'hidden' , name: this.name, value : ''
19856 // this.el.dom.removeAttribute("name");
19859 this.outerWrap = this.combo.wrap;
19860 this.wrap = cbwrap;
19862 this.outerWrap.setWidth(this.width);
19863 this.outerWrap.dom.removeChild(this.el.dom);
19865 this.wrap.dom.appendChild(this.el.dom);
19866 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19867 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19869 this.combo.trigger.setStyle('position','relative');
19870 this.combo.trigger.setStyle('left', '0px');
19871 this.combo.trigger.setStyle('top', '2px');
19873 this.combo.el.setStyle('vertical-align', 'text-bottom');
19875 //this.trigger.setStyle('vertical-align', 'top');
19877 // this should use the code from combo really... on('add' ....)
19881 this.adder = this.outerWrap.createChild(
19882 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19884 this.adder.on('click', function(e) {
19885 _t.fireEvent('adderclick', this, e);
19889 //this.adder.on('click', this.onAddClick, _t);
19892 this.combo.on('select', function(cb, rec, ix) {
19893 this.addItem(rec.data);
19896 cb.el.dom.value = '';
19897 //cb.lastData = rec.data;
19906 getName: function()
19908 // returns hidden if it's set..
19909 if (!this.rendered) {return ''};
19910 return this.hiddenName ? this.hiddenName : this.name;
19915 onResize: function(w, h){
19918 // not sure if this is needed..
19919 //this.combo.onResize(w,h);
19921 if(typeof w != 'number'){
19922 // we do not handle it!?!?
19925 var tw = this.combo.trigger.getWidth();
19926 tw += this.addicon ? this.addicon.getWidth() : 0;
19927 tw += this.editicon ? this.editicon.getWidth() : 0;
19929 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19931 this.combo.trigger.setStyle('left', '0px');
19933 if(this.list && this.listWidth === undefined){
19934 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19935 this.list.setWidth(lw);
19936 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19943 addItem: function(rec)
19945 var valueField = this.combo.valueField;
19946 var displayField = this.combo.displayField;
19948 if (this.items.indexOfKey(rec[valueField]) > -1) {
19949 //console.log("GOT " + rec.data.id);
19953 var x = new Roo.form.ComboBoxArray.Item({
19954 //id : rec[this.idField],
19956 displayField : displayField ,
19957 tipField : displayField ,
19961 this.items.add(rec[valueField],x);
19962 // add it before the element..
19963 this.updateHiddenEl();
19964 x.render(this.outerWrap, this.wrap.dom);
19965 // add the image handler..
19968 updateHiddenEl : function()
19971 if (!this.hiddenEl) {
19975 var idField = this.combo.valueField;
19977 this.items.each(function(f) {
19978 ar.push(f.data[idField]);
19980 this.hiddenEl.dom.value = ar.join(',');
19986 this.items.clear();
19988 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19992 this.el.dom.value = '';
19993 if (this.hiddenEl) {
19994 this.hiddenEl.dom.value = '';
19998 getValue: function()
20000 return this.hiddenEl ? this.hiddenEl.dom.value : '';
20002 setValue: function(v) // not a valid action - must use addItems..
20007 if (this.store.isLocal && (typeof(v) == 'string')) {
20008 // then we can use the store to find the values..
20009 // comma seperated at present.. this needs to allow JSON based encoding..
20010 this.hiddenEl.value = v;
20012 Roo.each(v.split(','), function(k) {
20013 Roo.log("CHECK " + this.valueField + ',' + k);
20014 var li = this.store.query(this.valueField, k);
20019 add[this.valueField] = k;
20020 add[this.displayField] = li.item(0).data[this.displayField];
20026 if (typeof(v) == 'object' ) {
20027 // then let's assume it's an array of objects..
20028 Roo.each(v, function(l) {
20036 setFromData: function(v)
20038 // this recieves an object, if setValues is called.
20040 this.el.dom.value = v[this.displayField];
20041 this.hiddenEl.dom.value = v[this.valueField];
20042 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20045 var kv = v[this.valueField];
20046 var dv = v[this.displayField];
20047 kv = typeof(kv) != 'string' ? '' : kv;
20048 dv = typeof(dv) != 'string' ? '' : dv;
20051 var keys = kv.split(',');
20052 var display = dv.split(',');
20053 for (var i = 0 ; i < keys.length; i++) {
20056 add[this.valueField] = keys[i];
20057 add[this.displayField] = display[i];
20065 * Validates the combox array value
20066 * @return {Boolean} True if the value is valid, else false
20068 validate : function(){
20069 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20070 this.clearInvalid();
20076 validateValue : function(value){
20077 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20085 isDirty : function() {
20086 if(this.disabled) {
20091 var d = Roo.decode(String(this.originalValue));
20093 return String(this.getValue()) !== String(this.originalValue);
20096 var originalValue = [];
20098 for (var i = 0; i < d.length; i++){
20099 originalValue.push(d[i][this.valueField]);
20102 return String(this.getValue()) !== String(originalValue.join(','));
20111 * @class Roo.form.ComboBoxArray.Item
20112 * @extends Roo.BoxComponent
20113 * A selected item in the list
20114 * Fred [x] Brian [x] [Pick another |v]
20117 * Create a new item.
20118 * @param {Object} config Configuration options
20121 Roo.form.ComboBoxArray.Item = function(config) {
20122 config.id = Roo.id();
20123 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20126 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20129 displayField : false,
20133 defaultAutoCreate : {
20135 cls: 'x-cbarray-item',
20142 src : Roo.BLANK_IMAGE_URL ,
20150 onRender : function(ct, position)
20152 Roo.form.Field.superclass.onRender.call(this, ct, position);
20155 var cfg = this.getAutoCreate();
20156 this.el = ct.createChild(cfg, position);
20159 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20161 this.el.child('div').dom.innerHTML = this.cb.renderer ?
20162 this.cb.renderer(this.data) :
20163 String.format('{0}',this.data[this.displayField]);
20166 this.el.child('div').dom.setAttribute('qtip',
20167 String.format('{0}',this.data[this.tipField])
20170 this.el.child('img').on('click', this.remove, this);
20174 remove : function()
20176 if(this.cb.disabled){
20180 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20181 this.cb.items.remove(this);
20182 this.el.child('img').un('click', this.remove, this);
20184 this.cb.updateHiddenEl();
20186 this.cb.fireEvent('remove', this.cb, this);
20191 * RooJS Library 1.1.1
20192 * Copyright(c) 2008-2011 Alan Knowles
20199 * @class Roo.form.ComboNested
20200 * @extends Roo.form.ComboBox
20201 * A combobox for that allows selection of nested items in a list,
20216 * Create a new ComboNested
20217 * @param {Object} config Configuration options
20219 Roo.form.ComboNested = function(config){
20220 Roo.form.ComboCheck.superclass.constructor.call(this, config);
20221 // should verify some data...
20223 // hiddenName = required..
20224 // displayField = required
20225 // valudField == required
20226 var req= [ 'hiddenName', 'displayField', 'valueField' ];
20228 Roo.each(req, function(e) {
20229 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20230 throw "Roo.form.ComboNested : missing value for: " + e;
20237 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20240 * @config {Number} max Number of columns to show
20245 list : null, // the outermost div..
20246 innerLists : null, // the
20250 onRender : function(ct, position)
20252 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20254 if(this.hiddenName){
20255 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
20257 this.hiddenField.value =
20258 this.hiddenValue !== undefined ? this.hiddenValue :
20259 this.value !== undefined ? this.value : '';
20261 // prevent input submission
20262 this.el.dom.removeAttribute('name');
20268 this.el.dom.setAttribute('autocomplete', 'off');
20271 var cls = 'x-combo-list';
20273 this.list = new Roo.Layer({
20274 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20277 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20278 this.list.setWidth(lw);
20279 this.list.swallowEvent('mousewheel');
20280 this.assetHeight = 0;
20283 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20284 this.assetHeight += this.header.getHeight();
20286 this.innerLists = [];
20289 for (var i =0 ; i < this.maxColumns; i++) {
20290 this.onRenderList( cls, i);
20293 // always needs footer, as we are going to have an 'OK' button.
20294 this.footer = this.list.createChild({cls:cls+'-ft'});
20295 this.pageTb = new Roo.Toolbar(this.footer);
20300 handler: function()
20306 if ( this.allowBlank && !this.disableClear) {
20308 this.pageTb.add(new Roo.Toolbar.Fill(), {
20309 cls: 'x-btn-icon x-btn-clear',
20311 handler: function()
20314 _this.clearValue();
20315 _this.onSelect(false, -1);
20320 this.assetHeight += this.footer.getHeight();
20324 onRenderList : function ( cls, i)
20327 var lw = Math.floor(
20328 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20331 this.list.setWidth(lw); // default to '1'
20333 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20334 //il.on('mouseover', this.onViewOver, this, { list: i });
20335 //il.on('mousemove', this.onViewMove, this, { list: i });
20337 il.setStyle({ 'overflow-x' : 'hidden'});
20340 this.tpl = new Roo.Template({
20341 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20342 isEmpty: function (value, allValues) {
20344 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20345 return dl ? 'has-children' : 'no-children'
20350 var store = this.store;
20352 store = new Roo.data.SimpleStore({
20353 //fields : this.store.reader.meta.fields,
20354 reader : this.store.reader,
20358 this.stores[i] = store;
20362 var view = this.views[i] = new Roo.View(
20368 selectedClass: this.selectedClass
20371 view.getEl().setWidth(lw);
20372 view.getEl().setStyle({
20373 position: i < 1 ? 'relative' : 'absolute',
20375 left: (i * lw ) + 'px',
20376 display : i > 0 ? 'none' : 'block'
20378 view.on('selectionchange', this.onSelectChange, this, {list : i });
20379 view.on('dblclick', this.onDoubleClick, this, {list : i });
20380 //view.on('click', this.onViewClick, this, { list : i });
20382 store.on('beforeload', this.onBeforeLoad, this);
20383 store.on('load', this.onLoad, this, { list : i});
20384 store.on('loadexception', this.onLoadException, this);
20386 // hide the other vies..
20391 onResize : function() {},
20393 restrictHeight : function()
20396 Roo.each(this.innerLists, function(il,i) {
20397 var el = this.views[i].getEl();
20398 el.dom.style.height = '';
20399 var inner = el.dom;
20400 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20401 // only adjust heights on other ones..
20404 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20405 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20406 mh = Math.max(el.getHeight(), mh);
20412 this.list.beginUpdate();
20413 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20414 this.list.alignTo(this.el, this.listAlign);
20415 this.list.endUpdate();
20420 // -- store handlers..
20422 onBeforeLoad : function()
20424 if(!this.hasFocus){
20427 this.innerLists[0].update(this.loadingText ?
20428 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20429 this.restrictHeight();
20430 this.selectedIndex = -1;
20433 onLoad : function(a,b,c,d)
20436 if(!this.hasFocus){
20440 if(this.store.getCount() > 0) {
20442 this.restrictHeight();
20444 this.onEmptyResults();
20447 this.stores[1].loadData([]);
20448 this.stores[2].loadData([]);
20457 onLoadException : function()
20460 Roo.log(this.store.reader.jsonData);
20461 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20462 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20470 onSelectChange : function (view, sels, opts )
20472 var ix = view.getSelectedIndexes();
20475 if (opts.list > this.maxColumns - 2) {
20477 this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20482 this.setFromData({});
20483 this.stores[opts.list+1].loadData( [] );
20487 var rec = view.store.getAt(ix[0]);
20488 this.setFromData(rec.data);
20490 var lw = Math.floor(
20491 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20493 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20494 var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20495 this.stores[opts.list+1].loadData( data );
20496 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20497 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20498 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20499 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20501 onDoubleClick : function()
20503 this.collapse(); //??
20508 findRecord : function (prop,value)
20510 return this.findRecordInStore(this.store, prop,value);
20514 findRecordInStore : function(store, prop, value)
20516 var cstore = new Roo.data.SimpleStore({
20517 //fields : this.store.reader.meta.fields, // we need array reader.. for
20518 reader : this.store.reader,
20522 var record = false;
20523 if(store.getCount() > 0){
20524 store.each(function(r){
20525 if(r.data[prop] == value){
20529 if (r.data.cn && r.data.cn.length) {
20530 cstore.loadData( r.data.cn);
20531 var cret = _this.findRecordInStore(cstore, prop, value);
20532 if (cret !== false) {
20549 * Ext JS Library 1.1.1
20550 * Copyright(c) 2006-2007, Ext JS, LLC.
20552 * Originally Released Under LGPL - original licence link has changed is not relivant.
20555 * <script type="text/javascript">
20558 * @class Roo.form.Checkbox
20559 * @extends Roo.form.Field
20560 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20562 * Creates a new Checkbox
20563 * @param {Object} config Configuration options
20565 Roo.form.Checkbox = function(config){
20566 Roo.form.Checkbox.superclass.constructor.call(this, config);
20570 * Fires when the checkbox is checked or unchecked.
20571 * @param {Roo.form.Checkbox} this This checkbox
20572 * @param {Boolean} checked The new checked value
20578 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20580 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20582 focusClass : undefined,
20584 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20586 fieldClass: "x-form-field",
20588 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20592 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20593 * {tag: "input", type: "checkbox", autocomplete: "off"})
20595 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20597 * @cfg {String} boxLabel The text that appears beside the checkbox
20601 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20605 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20607 valueOff: '0', // value when not checked..
20609 actionMode : 'viewEl',
20612 itemCls : 'x-menu-check-item x-form-item',
20613 groupClass : 'x-menu-group-item',
20614 inputType : 'hidden',
20617 inSetChecked: false, // check that we are not calling self...
20619 inputElement: false, // real input element?
20620 basedOn: false, // ????
20622 isFormField: true, // not sure where this is needed!!!!
20624 onResize : function(){
20625 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20626 if(!this.boxLabel){
20627 this.el.alignTo(this.wrap, 'c-c');
20631 initEvents : function(){
20632 Roo.form.Checkbox.superclass.initEvents.call(this);
20633 this.el.on("click", this.onClick, this);
20634 this.el.on("change", this.onClick, this);
20638 getResizeEl : function(){
20642 getPositionEl : function(){
20647 onRender : function(ct, position){
20648 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20650 if(this.inputValue !== undefined){
20651 this.el.dom.value = this.inputValue;
20654 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20655 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20656 var viewEl = this.wrap.createChild({
20657 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20658 this.viewEl = viewEl;
20659 this.wrap.on('click', this.onClick, this);
20661 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20662 this.el.on('propertychange', this.setFromHidden, this); //ie
20667 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20668 // viewEl.on('click', this.onClick, this);
20670 //if(this.checked){
20671 this.setChecked(this.checked);
20673 //this.checked = this.el.dom;
20679 initValue : Roo.emptyFn,
20682 * Returns the checked state of the checkbox.
20683 * @return {Boolean} True if checked, else false
20685 getValue : function(){
20687 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20689 return this.valueOff;
20694 onClick : function(){
20695 if (this.disabled) {
20698 this.setChecked(!this.checked);
20700 //if(this.el.dom.checked != this.checked){
20701 // this.setValue(this.el.dom.checked);
20706 * Sets the checked state of the checkbox.
20707 * On is always based on a string comparison between inputValue and the param.
20708 * @param {Boolean/String} value - the value to set
20709 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20711 setValue : function(v,suppressEvent){
20714 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20715 //if(this.el && this.el.dom){
20716 // this.el.dom.checked = this.checked;
20717 // this.el.dom.defaultChecked = this.checked;
20719 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20720 //this.fireEvent("check", this, this.checked);
20723 setChecked : function(state,suppressEvent)
20725 if (this.inSetChecked) {
20726 this.checked = state;
20732 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20734 this.checked = state;
20735 if(suppressEvent !== true){
20736 this.fireEvent('check', this, state);
20738 this.inSetChecked = true;
20739 this.el.dom.value = state ? this.inputValue : this.valueOff;
20740 this.inSetChecked = false;
20743 // handle setting of hidden value by some other method!!?!?
20744 setFromHidden: function()
20749 //console.log("SET FROM HIDDEN");
20750 //alert('setFrom hidden');
20751 this.setValue(this.el.dom.value);
20754 onDestroy : function()
20757 Roo.get(this.viewEl).remove();
20760 Roo.form.Checkbox.superclass.onDestroy.call(this);
20763 setBoxLabel : function(str)
20765 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20770 * Ext JS Library 1.1.1
20771 * Copyright(c) 2006-2007, Ext JS, LLC.
20773 * Originally Released Under LGPL - original licence link has changed is not relivant.
20776 * <script type="text/javascript">
20780 * @class Roo.form.Radio
20781 * @extends Roo.form.Checkbox
20782 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20783 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20785 * Creates a new Radio
20786 * @param {Object} config Configuration options
20788 Roo.form.Radio = function(){
20789 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20791 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20792 inputType: 'radio',
20795 * If this radio is part of a group, it will return the selected value
20798 getGroupValue : function(){
20799 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20803 onRender : function(ct, position){
20804 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20806 if(this.inputValue !== undefined){
20807 this.el.dom.value = this.inputValue;
20810 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20811 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20812 //var viewEl = this.wrap.createChild({
20813 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20814 //this.viewEl = viewEl;
20815 //this.wrap.on('click', this.onClick, this);
20817 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20818 //this.el.on('propertychange', this.setFromHidden, this); //ie
20823 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20824 // viewEl.on('click', this.onClick, this);
20827 this.el.dom.checked = 'checked' ;
20833 });//<script type="text/javascript">
20836 * Based Ext JS Library 1.1.1
20837 * Copyright(c) 2006-2007, Ext JS, LLC.
20843 * @class Roo.HtmlEditorCore
20844 * @extends Roo.Component
20845 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20847 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20850 Roo.HtmlEditorCore = function(config){
20853 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20858 * @event initialize
20859 * Fires when the editor is fully initialized (including the iframe)
20860 * @param {Roo.HtmlEditorCore} this
20865 * Fires when the editor is first receives the focus. Any insertion must wait
20866 * until after this event.
20867 * @param {Roo.HtmlEditorCore} this
20871 * @event beforesync
20872 * Fires before the textarea is updated with content from the editor iframe. Return false
20873 * to cancel the sync.
20874 * @param {Roo.HtmlEditorCore} this
20875 * @param {String} html
20879 * @event beforepush
20880 * Fires before the iframe editor is updated with content from the textarea. Return false
20881 * to cancel the push.
20882 * @param {Roo.HtmlEditorCore} this
20883 * @param {String} html
20888 * Fires when the textarea is updated with content from the editor iframe.
20889 * @param {Roo.HtmlEditorCore} this
20890 * @param {String} html
20895 * Fires when the iframe editor is updated with content from the textarea.
20896 * @param {Roo.HtmlEditorCore} this
20897 * @param {String} html
20902 * @event editorevent
20903 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20904 * @param {Roo.HtmlEditorCore} this
20910 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20912 // defaults : white / black...
20913 this.applyBlacklists();
20920 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20924 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20930 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20935 * @cfg {Number} height (in pixels)
20939 * @cfg {Number} width (in pixels)
20944 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20947 stylesheets: false,
20952 // private properties
20953 validationEvent : false,
20955 initialized : false,
20957 sourceEditMode : false,
20958 onFocus : Roo.emptyFn,
20960 hideMode:'offsets',
20964 // blacklist + whitelisted elements..
20971 * Protected method that will not generally be called directly. It
20972 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20973 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20975 getDocMarkup : function(){
20979 // inherit styels from page...??
20980 if (this.stylesheets === false) {
20982 Roo.get(document.head).select('style').each(function(node) {
20983 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20986 Roo.get(document.head).select('link').each(function(node) {
20987 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20990 } else if (!this.stylesheets.length) {
20992 st = '<style type="text/css">' +
20993 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20996 st = '<style type="text/css">' +
21001 st += '<style type="text/css">' +
21002 'IMG { cursor: pointer } ' +
21005 var cls = 'roo-htmleditor-body';
21007 if(this.bodyCls.length){
21008 cls += ' ' + this.bodyCls;
21011 return '<html><head>' + st +
21012 //<style type="text/css">' +
21013 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21015 ' </head><body class="' + cls + '"></body></html>';
21019 onRender : function(ct, position)
21022 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21023 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21026 this.el.dom.style.border = '0 none';
21027 this.el.dom.setAttribute('tabIndex', -1);
21028 this.el.addClass('x-hidden hide');
21032 if(Roo.isIE){ // fix IE 1px bogus margin
21033 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21037 this.frameId = Roo.id();
21041 var iframe = this.owner.wrap.createChild({
21043 cls: 'form-control', // bootstrap..
21045 name: this.frameId,
21046 frameBorder : 'no',
21047 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21052 this.iframe = iframe.dom;
21054 this.assignDocWin();
21056 this.doc.designMode = 'on';
21059 this.doc.write(this.getDocMarkup());
21063 var task = { // must defer to wait for browser to be ready
21065 //console.log("run task?" + this.doc.readyState);
21066 this.assignDocWin();
21067 if(this.doc.body || this.doc.readyState == 'complete'){
21069 this.doc.designMode="on";
21073 Roo.TaskMgr.stop(task);
21074 this.initEditor.defer(10, this);
21081 Roo.TaskMgr.start(task);
21086 onResize : function(w, h)
21088 Roo.log('resize: ' +w + ',' + h );
21089 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21093 if(typeof w == 'number'){
21095 this.iframe.style.width = w + 'px';
21097 if(typeof h == 'number'){
21099 this.iframe.style.height = h + 'px';
21101 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21108 * Toggles the editor between standard and source edit mode.
21109 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21111 toggleSourceEdit : function(sourceEditMode){
21113 this.sourceEditMode = sourceEditMode === true;
21115 if(this.sourceEditMode){
21117 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21120 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21121 //this.iframe.className = '';
21124 //this.setSize(this.owner.wrap.getSize());
21125 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21132 * Protected method that will not generally be called directly. If you need/want
21133 * custom HTML cleanup, this is the method you should override.
21134 * @param {String} html The HTML to be cleaned
21135 * return {String} The cleaned HTML
21137 cleanHtml : function(html){
21138 html = String(html);
21139 if(html.length > 5){
21140 if(Roo.isSafari){ // strip safari nonsense
21141 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21144 if(html == ' '){
21151 * HTML Editor -> Textarea
21152 * Protected method that will not generally be called directly. Syncs the contents
21153 * of the editor iframe with the textarea.
21155 syncValue : function(){
21156 if(this.initialized){
21157 var bd = (this.doc.body || this.doc.documentElement);
21158 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21159 var html = bd.innerHTML;
21161 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21162 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21164 html = '<div style="'+m[0]+'">' + html + '</div>';
21167 html = this.cleanHtml(html);
21168 // fix up the special chars.. normaly like back quotes in word...
21169 // however we do not want to do this with chinese..
21170 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21172 var cc = match.charCodeAt();
21174 // Get the character value, handling surrogate pairs
21175 if (match.length == 2) {
21176 // It's a surrogate pair, calculate the Unicode code point
21177 var high = match.charCodeAt(0) - 0xD800;
21178 var low = match.charCodeAt(1) - 0xDC00;
21179 cc = (high * 0x400) + low + 0x10000;
21181 (cc >= 0x4E00 && cc < 0xA000 ) ||
21182 (cc >= 0x3400 && cc < 0x4E00 ) ||
21183 (cc >= 0xf900 && cc < 0xfb00 )
21188 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21189 return "&#" + cc + ";";
21196 if(this.owner.fireEvent('beforesync', this, html) !== false){
21197 this.el.dom.value = html;
21198 this.owner.fireEvent('sync', this, html);
21204 * Protected method that will not generally be called directly. Pushes the value of the textarea
21205 * into the iframe editor.
21207 pushValue : function(){
21208 if(this.initialized){
21209 var v = this.el.dom.value.trim();
21211 // if(v.length < 1){
21215 if(this.owner.fireEvent('beforepush', this, v) !== false){
21216 var d = (this.doc.body || this.doc.documentElement);
21218 this.cleanUpPaste();
21219 this.el.dom.value = d.innerHTML;
21220 this.owner.fireEvent('push', this, v);
21226 deferFocus : function(){
21227 this.focus.defer(10, this);
21231 focus : function(){
21232 if(this.win && !this.sourceEditMode){
21239 assignDocWin: function()
21241 var iframe = this.iframe;
21244 this.doc = iframe.contentWindow.document;
21245 this.win = iframe.contentWindow;
21247 // if (!Roo.get(this.frameId)) {
21250 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21251 // this.win = Roo.get(this.frameId).dom.contentWindow;
21253 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21257 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21258 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21263 initEditor : function(){
21264 //console.log("INIT EDITOR");
21265 this.assignDocWin();
21269 this.doc.designMode="on";
21271 this.doc.write(this.getDocMarkup());
21274 var dbody = (this.doc.body || this.doc.documentElement);
21275 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21276 // this copies styles from the containing element into thsi one..
21277 // not sure why we need all of this..
21278 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21280 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21281 //ss['background-attachment'] = 'fixed'; // w3c
21282 dbody.bgProperties = 'fixed'; // ie
21283 //Roo.DomHelper.applyStyles(dbody, ss);
21284 Roo.EventManager.on(this.doc, {
21285 //'mousedown': this.onEditorEvent,
21286 'mouseup': this.onEditorEvent,
21287 'dblclick': this.onEditorEvent,
21288 'click': this.onEditorEvent,
21289 'keyup': this.onEditorEvent,
21294 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21296 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21297 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21299 this.initialized = true;
21301 this.owner.fireEvent('initialize', this);
21306 onDestroy : function(){
21312 //for (var i =0; i < this.toolbars.length;i++) {
21313 // // fixme - ask toolbars for heights?
21314 // this.toolbars[i].onDestroy();
21317 //this.wrap.dom.innerHTML = '';
21318 //this.wrap.remove();
21323 onFirstFocus : function(){
21325 this.assignDocWin();
21328 this.activated = true;
21331 if(Roo.isGecko){ // prevent silly gecko errors
21333 var s = this.win.getSelection();
21334 if(!s.focusNode || s.focusNode.nodeType != 3){
21335 var r = s.getRangeAt(0);
21336 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21341 this.execCmd('useCSS', true);
21342 this.execCmd('styleWithCSS', false);
21345 this.owner.fireEvent('activate', this);
21349 adjustFont: function(btn){
21350 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21351 //if(Roo.isSafari){ // safari
21354 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21355 if(Roo.isSafari){ // safari
21356 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21357 v = (v < 10) ? 10 : v;
21358 v = (v > 48) ? 48 : v;
21359 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21364 v = Math.max(1, v+adjust);
21366 this.execCmd('FontSize', v );
21369 onEditorEvent : function(e)
21371 this.owner.fireEvent('editorevent', this, e);
21372 // this.updateToolbar();
21373 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21376 insertTag : function(tg)
21378 // could be a bit smarter... -> wrap the current selected tRoo..
21379 if (tg.toLowerCase() == 'span' ||
21380 tg.toLowerCase() == 'code' ||
21381 tg.toLowerCase() == 'sup' ||
21382 tg.toLowerCase() == 'sub'
21385 range = this.createRange(this.getSelection());
21386 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21387 wrappingNode.appendChild(range.extractContents());
21388 range.insertNode(wrappingNode);
21395 this.execCmd("formatblock", tg);
21399 insertText : function(txt)
21403 var range = this.createRange();
21404 range.deleteContents();
21405 //alert(Sender.getAttribute('label'));
21407 range.insertNode(this.doc.createTextNode(txt));
21413 * Executes a Midas editor command on the editor document and performs necessary focus and
21414 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21415 * @param {String} cmd The Midas command
21416 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21418 relayCmd : function(cmd, value){
21420 this.execCmd(cmd, value);
21421 this.owner.fireEvent('editorevent', this);
21422 //this.updateToolbar();
21423 this.owner.deferFocus();
21427 * Executes a Midas editor command directly on the editor document.
21428 * For visual commands, you should use {@link #relayCmd} instead.
21429 * <b>This should only be called after the editor is initialized.</b>
21430 * @param {String} cmd The Midas command
21431 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21433 execCmd : function(cmd, value){
21434 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21441 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21443 * @param {String} text | dom node..
21445 insertAtCursor : function(text)
21448 if(!this.activated){
21454 var r = this.doc.selection.createRange();
21465 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21469 // from jquery ui (MIT licenced)
21471 var win = this.win;
21473 if (win.getSelection && win.getSelection().getRangeAt) {
21474 range = win.getSelection().getRangeAt(0);
21475 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21476 range.insertNode(node);
21477 } else if (win.document.selection && win.document.selection.createRange) {
21478 // no firefox support
21479 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21480 win.document.selection.createRange().pasteHTML(txt);
21482 // no firefox support
21483 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21484 this.execCmd('InsertHTML', txt);
21493 mozKeyPress : function(e){
21495 var c = e.getCharCode(), cmd;
21498 c = String.fromCharCode(c).toLowerCase();
21512 this.cleanUpPaste.defer(100, this);
21520 e.preventDefault();
21528 fixKeys : function(){ // load time branching for fastest keydown performance
21530 return function(e){
21531 var k = e.getKey(), r;
21534 r = this.doc.selection.createRange();
21537 r.pasteHTML('    ');
21544 r = this.doc.selection.createRange();
21546 var target = r.parentElement();
21547 if(!target || target.tagName.toLowerCase() != 'li'){
21549 r.pasteHTML('<br />');
21555 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21556 this.cleanUpPaste.defer(100, this);
21562 }else if(Roo.isOpera){
21563 return function(e){
21564 var k = e.getKey();
21568 this.execCmd('InsertHTML','    ');
21571 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21572 this.cleanUpPaste.defer(100, this);
21577 }else if(Roo.isSafari){
21578 return function(e){
21579 var k = e.getKey();
21583 this.execCmd('InsertText','\t');
21587 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21588 this.cleanUpPaste.defer(100, this);
21596 getAllAncestors: function()
21598 var p = this.getSelectedNode();
21601 a.push(p); // push blank onto stack..
21602 p = this.getParentElement();
21606 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21610 a.push(this.doc.body);
21614 lastSelNode : false,
21617 getSelection : function()
21619 this.assignDocWin();
21620 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21623 getSelectedNode: function()
21625 // this may only work on Gecko!!!
21627 // should we cache this!!!!
21632 var range = this.createRange(this.getSelection()).cloneRange();
21635 var parent = range.parentElement();
21637 var testRange = range.duplicate();
21638 testRange.moveToElementText(parent);
21639 if (testRange.inRange(range)) {
21642 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21645 parent = parent.parentElement;
21650 // is ancestor a text element.
21651 var ac = range.commonAncestorContainer;
21652 if (ac.nodeType == 3) {
21653 ac = ac.parentNode;
21656 var ar = ac.childNodes;
21659 var other_nodes = [];
21660 var has_other_nodes = false;
21661 for (var i=0;i<ar.length;i++) {
21662 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21665 // fullly contained node.
21667 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21672 // probably selected..
21673 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21674 other_nodes.push(ar[i]);
21678 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21683 has_other_nodes = true;
21685 if (!nodes.length && other_nodes.length) {
21686 nodes= other_nodes;
21688 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21694 createRange: function(sel)
21696 // this has strange effects when using with
21697 // top toolbar - not sure if it's a great idea.
21698 //this.editor.contentWindow.focus();
21699 if (typeof sel != "undefined") {
21701 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21703 return this.doc.createRange();
21706 return this.doc.createRange();
21709 getParentElement: function()
21712 this.assignDocWin();
21713 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21715 var range = this.createRange(sel);
21718 var p = range.commonAncestorContainer;
21719 while (p.nodeType == 3) { // text node
21730 * Range intersection.. the hard stuff...
21734 * [ -- selected range --- ]
21738 * if end is before start or hits it. fail.
21739 * if start is after end or hits it fail.
21741 * if either hits (but other is outside. - then it's not
21747 // @see http://www.thismuchiknow.co.uk/?p=64.
21748 rangeIntersectsNode : function(range, node)
21750 var nodeRange = node.ownerDocument.createRange();
21752 nodeRange.selectNode(node);
21754 nodeRange.selectNodeContents(node);
21757 var rangeStartRange = range.cloneRange();
21758 rangeStartRange.collapse(true);
21760 var rangeEndRange = range.cloneRange();
21761 rangeEndRange.collapse(false);
21763 var nodeStartRange = nodeRange.cloneRange();
21764 nodeStartRange.collapse(true);
21766 var nodeEndRange = nodeRange.cloneRange();
21767 nodeEndRange.collapse(false);
21769 return rangeStartRange.compareBoundaryPoints(
21770 Range.START_TO_START, nodeEndRange) == -1 &&
21771 rangeEndRange.compareBoundaryPoints(
21772 Range.START_TO_START, nodeStartRange) == 1;
21776 rangeCompareNode : function(range, node)
21778 var nodeRange = node.ownerDocument.createRange();
21780 nodeRange.selectNode(node);
21782 nodeRange.selectNodeContents(node);
21786 range.collapse(true);
21788 nodeRange.collapse(true);
21790 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21791 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21793 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21795 var nodeIsBefore = ss == 1;
21796 var nodeIsAfter = ee == -1;
21798 if (nodeIsBefore && nodeIsAfter) {
21801 if (!nodeIsBefore && nodeIsAfter) {
21802 return 1; //right trailed.
21805 if (nodeIsBefore && !nodeIsAfter) {
21806 return 2; // left trailed.
21812 // private? - in a new class?
21813 cleanUpPaste : function()
21815 // cleans up the whole document..
21816 Roo.log('cleanuppaste');
21818 this.cleanUpChildren(this.doc.body);
21819 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21820 if (clean != this.doc.body.innerHTML) {
21821 this.doc.body.innerHTML = clean;
21826 cleanWordChars : function(input) {// change the chars to hex code
21827 var he = Roo.HtmlEditorCore;
21829 var output = input;
21830 Roo.each(he.swapCodes, function(sw) {
21831 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21833 output = output.replace(swapper, sw[1]);
21840 cleanUpChildren : function (n)
21842 if (!n.childNodes.length) {
21845 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21846 this.cleanUpChild(n.childNodes[i]);
21853 cleanUpChild : function (node)
21856 //console.log(node);
21857 if (node.nodeName == "#text") {
21858 // clean up silly Windows -- stuff?
21861 if (node.nodeName == "#comment") {
21862 node.parentNode.removeChild(node);
21863 // clean up silly Windows -- stuff?
21866 var lcname = node.tagName.toLowerCase();
21867 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21868 // whitelist of tags..
21870 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21872 node.parentNode.removeChild(node);
21877 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21879 // spans with no attributes - just remove them..
21880 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21881 remove_keep_children = true;
21884 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21885 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21887 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21888 // remove_keep_children = true;
21891 if (remove_keep_children) {
21892 this.cleanUpChildren(node);
21893 // inserts everything just before this node...
21894 while (node.childNodes.length) {
21895 var cn = node.childNodes[0];
21896 node.removeChild(cn);
21897 node.parentNode.insertBefore(cn, node);
21899 node.parentNode.removeChild(node);
21903 if (!node.attributes || !node.attributes.length) {
21908 this.cleanUpChildren(node);
21912 function cleanAttr(n,v)
21915 if (v.match(/^\./) || v.match(/^\//)) {
21918 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21921 if (v.match(/^#/)) {
21924 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21925 node.removeAttribute(n);
21929 var cwhite = this.cwhite;
21930 var cblack = this.cblack;
21932 function cleanStyle(n,v)
21934 if (v.match(/expression/)) { //XSS?? should we even bother..
21935 node.removeAttribute(n);
21939 var parts = v.split(/;/);
21942 Roo.each(parts, function(p) {
21943 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21947 var l = p.split(':').shift().replace(/\s+/g,'');
21948 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21950 if ( cwhite.length && cblack.indexOf(l) > -1) {
21951 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21952 //node.removeAttribute(n);
21956 // only allow 'c whitelisted system attributes'
21957 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21958 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21959 //node.removeAttribute(n);
21969 if (clean.length) {
21970 node.setAttribute(n, clean.join(';'));
21972 node.removeAttribute(n);
21978 for (var i = node.attributes.length-1; i > -1 ; i--) {
21979 var a = node.attributes[i];
21982 if (a.name.toLowerCase().substr(0,2)=='on') {
21983 node.removeAttribute(a.name);
21986 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21987 node.removeAttribute(a.name);
21990 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21991 cleanAttr(a.name,a.value); // fixme..
21994 if (a.name == 'style') {
21995 cleanStyle(a.name,a.value);
21998 /// clean up MS crap..
21999 // tecnically this should be a list of valid class'es..
22002 if (a.name == 'class') {
22003 if (a.value.match(/^Mso/)) {
22004 node.removeAttribute('class');
22007 if (a.value.match(/^body$/)) {
22008 node.removeAttribute('class');
22019 this.cleanUpChildren(node);
22025 * Clean up MS wordisms...
22027 cleanWord : function(node)
22030 this.cleanWord(this.doc.body);
22035 node.nodeName == 'SPAN' &&
22036 !node.hasAttributes() &&
22037 node.childNodes.length == 1 &&
22038 node.firstChild.nodeName == "#text"
22040 var textNode = node.firstChild;
22041 node.removeChild(textNode);
22042 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22043 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22045 node.parentNode.insertBefore(textNode, node);
22046 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22047 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22049 node.parentNode.removeChild(node);
22052 if (node.nodeName == "#text") {
22053 // clean up silly Windows -- stuff?
22056 if (node.nodeName == "#comment") {
22057 node.parentNode.removeChild(node);
22058 // clean up silly Windows -- stuff?
22062 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22063 node.parentNode.removeChild(node);
22066 //Roo.log(node.tagName);
22067 // remove - but keep children..
22068 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22069 //Roo.log('-- removed');
22070 while (node.childNodes.length) {
22071 var cn = node.childNodes[0];
22072 node.removeChild(cn);
22073 node.parentNode.insertBefore(cn, node);
22074 // move node to parent - and clean it..
22075 this.cleanWord(cn);
22077 node.parentNode.removeChild(node);
22078 /// no need to iterate chidlren = it's got none..
22079 //this.iterateChildren(node, this.cleanWord);
22083 if (node.className.length) {
22085 var cn = node.className.split(/\W+/);
22087 Roo.each(cn, function(cls) {
22088 if (cls.match(/Mso[a-zA-Z]+/)) {
22093 node.className = cna.length ? cna.join(' ') : '';
22095 node.removeAttribute("class");
22099 if (node.hasAttribute("lang")) {
22100 node.removeAttribute("lang");
22103 if (node.hasAttribute("style")) {
22105 var styles = node.getAttribute("style").split(";");
22107 Roo.each(styles, function(s) {
22108 if (!s.match(/:/)) {
22111 var kv = s.split(":");
22112 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22115 // what ever is left... we allow.
22118 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22119 if (!nstyle.length) {
22120 node.removeAttribute('style');
22123 this.iterateChildren(node, this.cleanWord);
22129 * iterateChildren of a Node, calling fn each time, using this as the scole..
22130 * @param {DomNode} node node to iterate children of.
22131 * @param {Function} fn method of this class to call on each item.
22133 iterateChildren : function(node, fn)
22135 if (!node.childNodes.length) {
22138 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22139 fn.call(this, node.childNodes[i])
22145 * cleanTableWidths.
22147 * Quite often pasting from word etc.. results in tables with column and widths.
22148 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22151 cleanTableWidths : function(node)
22156 this.cleanTableWidths(this.doc.body);
22161 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22164 Roo.log(node.tagName);
22165 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22166 this.iterateChildren(node, this.cleanTableWidths);
22169 if (node.hasAttribute('width')) {
22170 node.removeAttribute('width');
22174 if (node.hasAttribute("style")) {
22177 var styles = node.getAttribute("style").split(";");
22179 Roo.each(styles, function(s) {
22180 if (!s.match(/:/)) {
22183 var kv = s.split(":");
22184 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22187 // what ever is left... we allow.
22190 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22191 if (!nstyle.length) {
22192 node.removeAttribute('style');
22196 this.iterateChildren(node, this.cleanTableWidths);
22204 domToHTML : function(currentElement, depth, nopadtext) {
22206 depth = depth || 0;
22207 nopadtext = nopadtext || false;
22209 if (!currentElement) {
22210 return this.domToHTML(this.doc.body);
22213 //Roo.log(currentElement);
22215 var allText = false;
22216 var nodeName = currentElement.nodeName;
22217 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22219 if (nodeName == '#text') {
22221 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22226 if (nodeName != 'BODY') {
22229 // Prints the node tagName, such as <A>, <IMG>, etc
22232 for(i = 0; i < currentElement.attributes.length;i++) {
22234 var aname = currentElement.attributes.item(i).name;
22235 if (!currentElement.attributes.item(i).value.length) {
22238 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22241 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22250 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22253 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22258 // Traverse the tree
22260 var currentElementChild = currentElement.childNodes.item(i);
22261 var allText = true;
22262 var innerHTML = '';
22264 while (currentElementChild) {
22265 // Formatting code (indent the tree so it looks nice on the screen)
22266 var nopad = nopadtext;
22267 if (lastnode == 'SPAN') {
22271 if (currentElementChild.nodeName == '#text') {
22272 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22273 toadd = nopadtext ? toadd : toadd.trim();
22274 if (!nopad && toadd.length > 80) {
22275 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22277 innerHTML += toadd;
22280 currentElementChild = currentElement.childNodes.item(i);
22286 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22288 // Recursively traverse the tree structure of the child node
22289 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22290 lastnode = currentElementChild.nodeName;
22292 currentElementChild=currentElement.childNodes.item(i);
22298 // The remaining code is mostly for formatting the tree
22299 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22304 ret+= "</"+tagName+">";
22310 applyBlacklists : function()
22312 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22313 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22317 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22318 if (b.indexOf(tag) > -1) {
22321 this.white.push(tag);
22325 Roo.each(w, function(tag) {
22326 if (b.indexOf(tag) > -1) {
22329 if (this.white.indexOf(tag) > -1) {
22332 this.white.push(tag);
22337 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22338 if (w.indexOf(tag) > -1) {
22341 this.black.push(tag);
22345 Roo.each(b, function(tag) {
22346 if (w.indexOf(tag) > -1) {
22349 if (this.black.indexOf(tag) > -1) {
22352 this.black.push(tag);
22357 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22358 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22362 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22363 if (b.indexOf(tag) > -1) {
22366 this.cwhite.push(tag);
22370 Roo.each(w, function(tag) {
22371 if (b.indexOf(tag) > -1) {
22374 if (this.cwhite.indexOf(tag) > -1) {
22377 this.cwhite.push(tag);
22382 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22383 if (w.indexOf(tag) > -1) {
22386 this.cblack.push(tag);
22390 Roo.each(b, function(tag) {
22391 if (w.indexOf(tag) > -1) {
22394 if (this.cblack.indexOf(tag) > -1) {
22397 this.cblack.push(tag);
22402 setStylesheets : function(stylesheets)
22404 if(typeof(stylesheets) == 'string'){
22405 Roo.get(this.iframe.contentDocument.head).createChild({
22407 rel : 'stylesheet',
22416 Roo.each(stylesheets, function(s) {
22421 Roo.get(_this.iframe.contentDocument.head).createChild({
22423 rel : 'stylesheet',
22432 removeStylesheets : function()
22436 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22441 setStyle : function(style)
22443 Roo.get(this.iframe.contentDocument.head).createChild({
22452 // hide stuff that is not compatible
22466 * @event specialkey
22470 * @cfg {String} fieldClass @hide
22473 * @cfg {String} focusClass @hide
22476 * @cfg {String} autoCreate @hide
22479 * @cfg {String} inputType @hide
22482 * @cfg {String} invalidClass @hide
22485 * @cfg {String} invalidText @hide
22488 * @cfg {String} msgFx @hide
22491 * @cfg {String} validateOnBlur @hide
22495 Roo.HtmlEditorCore.white = [
22496 'area', 'br', 'img', 'input', 'hr', 'wbr',
22498 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22499 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22500 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22501 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22502 'table', 'ul', 'xmp',
22504 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22507 'dir', 'menu', 'ol', 'ul', 'dl',
22513 Roo.HtmlEditorCore.black = [
22514 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22516 'base', 'basefont', 'bgsound', 'blink', 'body',
22517 'frame', 'frameset', 'head', 'html', 'ilayer',
22518 'iframe', 'layer', 'link', 'meta', 'object',
22519 'script', 'style' ,'title', 'xml' // clean later..
22521 Roo.HtmlEditorCore.clean = [
22522 'script', 'style', 'title', 'xml'
22524 Roo.HtmlEditorCore.remove = [
22529 Roo.HtmlEditorCore.ablack = [
22533 Roo.HtmlEditorCore.aclean = [
22534 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22538 Roo.HtmlEditorCore.pwhite= [
22539 'http', 'https', 'mailto'
22542 // white listed style attributes.
22543 Roo.HtmlEditorCore.cwhite= [
22544 // 'text-align', /// default is to allow most things..
22550 // black listed style attributes.
22551 Roo.HtmlEditorCore.cblack= [
22552 // 'font-size' -- this can be set by the project
22556 Roo.HtmlEditorCore.swapCodes =[
22567 //<script type="text/javascript">
22570 * Ext JS Library 1.1.1
22571 * Copyright(c) 2006-2007, Ext JS, LLC.
22577 Roo.form.HtmlEditor = function(config){
22581 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22583 if (!this.toolbars) {
22584 this.toolbars = [];
22586 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22592 * @class Roo.form.HtmlEditor
22593 * @extends Roo.form.Field
22594 * Provides a lightweight HTML Editor component.
22596 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22598 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22599 * supported by this editor.</b><br/><br/>
22600 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22601 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22603 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22605 * @cfg {Boolean} clearUp
22609 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22614 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22619 * @cfg {Number} height (in pixels)
22623 * @cfg {Number} width (in pixels)
22628 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22631 stylesheets: false,
22635 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22640 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22646 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22651 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22659 // private properties
22660 validationEvent : false,
22662 initialized : false,
22665 onFocus : Roo.emptyFn,
22667 hideMode:'offsets',
22669 actionMode : 'container', // defaults to hiding it...
22671 defaultAutoCreate : { // modified by initCompnoent..
22673 style:"width:500px;height:300px;",
22674 autocomplete: "new-password"
22678 initComponent : function(){
22681 * @event initialize
22682 * Fires when the editor is fully initialized (including the iframe)
22683 * @param {HtmlEditor} this
22688 * Fires when the editor is first receives the focus. Any insertion must wait
22689 * until after this event.
22690 * @param {HtmlEditor} this
22694 * @event beforesync
22695 * Fires before the textarea is updated with content from the editor iframe. Return false
22696 * to cancel the sync.
22697 * @param {HtmlEditor} this
22698 * @param {String} html
22702 * @event beforepush
22703 * Fires before the iframe editor is updated with content from the textarea. Return false
22704 * to cancel the push.
22705 * @param {HtmlEditor} this
22706 * @param {String} html
22711 * Fires when the textarea is updated with content from the editor iframe.
22712 * @param {HtmlEditor} this
22713 * @param {String} html
22718 * Fires when the iframe editor is updated with content from the textarea.
22719 * @param {HtmlEditor} this
22720 * @param {String} html
22724 * @event editmodechange
22725 * Fires when the editor switches edit modes
22726 * @param {HtmlEditor} this
22727 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22729 editmodechange: true,
22731 * @event editorevent
22732 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22733 * @param {HtmlEditor} this
22737 * @event firstfocus
22738 * Fires when on first focus - needed by toolbars..
22739 * @param {HtmlEditor} this
22744 * Auto save the htmlEditor value as a file into Events
22745 * @param {HtmlEditor} this
22749 * @event savedpreview
22750 * preview the saved version of htmlEditor
22751 * @param {HtmlEditor} this
22753 savedpreview: true,
22756 * @event stylesheetsclick
22757 * Fires when press the Sytlesheets button
22758 * @param {Roo.HtmlEditorCore} this
22760 stylesheetsclick: true
22762 this.defaultAutoCreate = {
22764 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22765 autocomplete: "new-password"
22770 * Protected method that will not generally be called directly. It
22771 * is called when the editor creates its toolbar. Override this method if you need to
22772 * add custom toolbar buttons.
22773 * @param {HtmlEditor} editor
22775 createToolbar : function(editor){
22776 Roo.log("create toolbars");
22777 if (!editor.toolbars || !editor.toolbars.length) {
22778 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22781 for (var i =0 ; i < editor.toolbars.length;i++) {
22782 editor.toolbars[i] = Roo.factory(
22783 typeof(editor.toolbars[i]) == 'string' ?
22784 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22785 Roo.form.HtmlEditor);
22786 editor.toolbars[i].init(editor);
22794 onRender : function(ct, position)
22797 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22799 this.wrap = this.el.wrap({
22800 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22803 this.editorcore.onRender(ct, position);
22805 if (this.resizable) {
22806 this.resizeEl = new Roo.Resizable(this.wrap, {
22810 minHeight : this.height,
22811 height: this.height,
22812 handles : this.resizable,
22815 resize : function(r, w, h) {
22816 _t.onResize(w,h); // -something
22822 this.createToolbar(this);
22826 this.setSize(this.wrap.getSize());
22828 if (this.resizeEl) {
22829 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22830 // should trigger onReize..
22833 this.keyNav = new Roo.KeyNav(this.el, {
22835 "tab" : function(e){
22836 e.preventDefault();
22838 var value = this.getValue();
22840 var start = this.el.dom.selectionStart;
22841 var end = this.el.dom.selectionEnd;
22845 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22846 this.el.dom.setSelectionRange(end + 1, end + 1);
22850 var f = value.substring(0, start).split("\t");
22852 if(f.pop().length != 0){
22856 this.setValue(f.join("\t") + value.substring(end));
22857 this.el.dom.setSelectionRange(start - 1, start - 1);
22861 "home" : function(e){
22862 e.preventDefault();
22864 var curr = this.el.dom.selectionStart;
22865 var lines = this.getValue().split("\n");
22872 this.el.dom.setSelectionRange(0, 0);
22878 for (var i = 0; i < lines.length;i++) {
22879 pos += lines[i].length;
22889 pos -= lines[i].length;
22895 this.el.dom.setSelectionRange(pos, pos);
22899 this.el.dom.selectionStart = pos;
22900 this.el.dom.selectionEnd = curr;
22903 "end" : function(e){
22904 e.preventDefault();
22906 var curr = this.el.dom.selectionStart;
22907 var lines = this.getValue().split("\n");
22914 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22920 for (var i = 0; i < lines.length;i++) {
22922 pos += lines[i].length;
22936 this.el.dom.setSelectionRange(pos, pos);
22940 this.el.dom.selectionStart = curr;
22941 this.el.dom.selectionEnd = pos;
22946 doRelay : function(foo, bar, hname){
22947 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22953 // if(this.autosave && this.w){
22954 // this.autoSaveFn = setInterval(this.autosave, 1000);
22959 onResize : function(w, h)
22961 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22966 if(typeof w == 'number'){
22967 var aw = w - this.wrap.getFrameWidth('lr');
22968 this.el.setWidth(this.adjustWidth('textarea', aw));
22971 if(typeof h == 'number'){
22973 for (var i =0; i < this.toolbars.length;i++) {
22974 // fixme - ask toolbars for heights?
22975 tbh += this.toolbars[i].tb.el.getHeight();
22976 if (this.toolbars[i].footer) {
22977 tbh += this.toolbars[i].footer.el.getHeight();
22984 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22985 ah -= 5; // knock a few pixes off for look..
22987 this.el.setHeight(this.adjustWidth('textarea', ah));
22991 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22992 this.editorcore.onResize(ew,eh);
22997 * Toggles the editor between standard and source edit mode.
22998 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23000 toggleSourceEdit : function(sourceEditMode)
23002 this.editorcore.toggleSourceEdit(sourceEditMode);
23004 if(this.editorcore.sourceEditMode){
23005 Roo.log('editor - showing textarea');
23008 // Roo.log(this.syncValue());
23009 this.editorcore.syncValue();
23010 this.el.removeClass('x-hidden');
23011 this.el.dom.removeAttribute('tabIndex');
23014 for (var i = 0; i < this.toolbars.length; i++) {
23015 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23016 this.toolbars[i].tb.hide();
23017 this.toolbars[i].footer.hide();
23022 Roo.log('editor - hiding textarea');
23024 // Roo.log(this.pushValue());
23025 this.editorcore.pushValue();
23027 this.el.addClass('x-hidden');
23028 this.el.dom.setAttribute('tabIndex', -1);
23030 for (var i = 0; i < this.toolbars.length; i++) {
23031 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23032 this.toolbars[i].tb.show();
23033 this.toolbars[i].footer.show();
23037 //this.deferFocus();
23040 this.setSize(this.wrap.getSize());
23041 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23043 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23046 // private (for BoxComponent)
23047 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23049 // private (for BoxComponent)
23050 getResizeEl : function(){
23054 // private (for BoxComponent)
23055 getPositionEl : function(){
23060 initEvents : function(){
23061 this.originalValue = this.getValue();
23065 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23068 markInvalid : Roo.emptyFn,
23070 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23073 clearInvalid : Roo.emptyFn,
23075 setValue : function(v){
23076 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23077 this.editorcore.pushValue();
23082 deferFocus : function(){
23083 this.focus.defer(10, this);
23087 focus : function(){
23088 this.editorcore.focus();
23094 onDestroy : function(){
23100 for (var i =0; i < this.toolbars.length;i++) {
23101 // fixme - ask toolbars for heights?
23102 this.toolbars[i].onDestroy();
23105 this.wrap.dom.innerHTML = '';
23106 this.wrap.remove();
23111 onFirstFocus : function(){
23112 //Roo.log("onFirstFocus");
23113 this.editorcore.onFirstFocus();
23114 for (var i =0; i < this.toolbars.length;i++) {
23115 this.toolbars[i].onFirstFocus();
23121 syncValue : function()
23123 this.editorcore.syncValue();
23126 pushValue : function()
23128 this.editorcore.pushValue();
23131 setStylesheets : function(stylesheets)
23133 this.editorcore.setStylesheets(stylesheets);
23136 removeStylesheets : function()
23138 this.editorcore.removeStylesheets();
23142 // hide stuff that is not compatible
23156 * @event specialkey
23160 * @cfg {String} fieldClass @hide
23163 * @cfg {String} focusClass @hide
23166 * @cfg {String} autoCreate @hide
23169 * @cfg {String} inputType @hide
23172 * @cfg {String} invalidClass @hide
23175 * @cfg {String} invalidText @hide
23178 * @cfg {String} msgFx @hide
23181 * @cfg {String} validateOnBlur @hide
23185 // <script type="text/javascript">
23188 * Ext JS Library 1.1.1
23189 * Copyright(c) 2006-2007, Ext JS, LLC.
23195 * @class Roo.form.HtmlEditorToolbar1
23200 new Roo.form.HtmlEditor({
23203 new Roo.form.HtmlEditorToolbar1({
23204 disable : { fonts: 1 , format: 1, ..., ... , ...],
23210 * @cfg {Object} disable List of elements to disable..
23211 * @cfg {Array} btns List of additional buttons.
23215 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23218 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23221 Roo.apply(this, config);
23223 // default disabled, based on 'good practice'..
23224 this.disable = this.disable || {};
23225 Roo.applyIf(this.disable, {
23228 specialElements : true
23232 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23233 // dont call parent... till later.
23236 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
23243 editorcore : false,
23245 * @cfg {Object} disable List of toolbar elements to disable
23252 * @cfg {String} createLinkText The default text for the create link prompt
23254 createLinkText : 'Please enter the URL for the link:',
23256 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23258 defaultLinkValue : 'http:/'+'/',
23262 * @cfg {Array} fontFamilies An array of available font families
23280 // "á" , ?? a acute?
23285 "°" // , // degrees
23287 // "é" , // e ecute
23288 // "ú" , // u ecute?
23291 specialElements : [
23293 text: "Insert Table",
23296 ihtml : '<table><tr><td>Cell</td></tr></table>'
23300 text: "Insert Image",
23303 ihtml : '<img src="about:blank"/>'
23312 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23313 "input:submit", "input:button", "select", "textarea", "label" ],
23316 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23318 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23327 * @cfg {String} defaultFont default font to use.
23329 defaultFont: 'tahoma',
23331 fontSelect : false,
23334 formatCombo : false,
23336 init : function(editor)
23338 this.editor = editor;
23339 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23340 var editorcore = this.editorcore;
23344 var fid = editorcore.frameId;
23346 function btn(id, toggle, handler){
23347 var xid = fid + '-'+ id ;
23351 cls : 'x-btn-icon x-edit-'+id,
23352 enableToggle:toggle !== false,
23353 scope: _t, // was editor...
23354 handler:handler||_t.relayBtnCmd,
23355 clickEvent:'mousedown',
23356 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23363 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23365 // stop form submits
23366 tb.el.on('click', function(e){
23367 e.preventDefault(); // what does this do?
23370 if(!this.disable.font) { // && !Roo.isSafari){
23371 /* why no safari for fonts
23372 editor.fontSelect = tb.el.createChild({
23375 cls:'x-font-select',
23376 html: this.createFontOptions()
23379 editor.fontSelect.on('change', function(){
23380 var font = editor.fontSelect.dom.value;
23381 editor.relayCmd('fontname', font);
23382 editor.deferFocus();
23386 editor.fontSelect.dom,
23392 if(!this.disable.formats){
23393 this.formatCombo = new Roo.form.ComboBox({
23394 store: new Roo.data.SimpleStore({
23397 data : this.formats // from states.js
23401 //autoCreate : {tag: "div", size: "20"},
23402 displayField:'tag',
23406 triggerAction: 'all',
23407 emptyText:'Add tag',
23408 selectOnFocus:true,
23411 'select': function(c, r, i) {
23412 editorcore.insertTag(r.get('tag'));
23418 tb.addField(this.formatCombo);
23422 if(!this.disable.format){
23427 btn('strikethrough')
23430 if(!this.disable.fontSize){
23435 btn('increasefontsize', false, editorcore.adjustFont),
23436 btn('decreasefontsize', false, editorcore.adjustFont)
23441 if(!this.disable.colors){
23444 id:editorcore.frameId +'-forecolor',
23445 cls:'x-btn-icon x-edit-forecolor',
23446 clickEvent:'mousedown',
23447 tooltip: this.buttonTips['forecolor'] || undefined,
23449 menu : new Roo.menu.ColorMenu({
23450 allowReselect: true,
23451 focus: Roo.emptyFn,
23454 selectHandler: function(cp, color){
23455 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23456 editor.deferFocus();
23459 clickEvent:'mousedown'
23462 id:editorcore.frameId +'backcolor',
23463 cls:'x-btn-icon x-edit-backcolor',
23464 clickEvent:'mousedown',
23465 tooltip: this.buttonTips['backcolor'] || undefined,
23467 menu : new Roo.menu.ColorMenu({
23468 focus: Roo.emptyFn,
23471 allowReselect: true,
23472 selectHandler: function(cp, color){
23474 editorcore.execCmd('useCSS', false);
23475 editorcore.execCmd('hilitecolor', color);
23476 editorcore.execCmd('useCSS', true);
23477 editor.deferFocus();
23479 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23480 Roo.isSafari || Roo.isIE ? '#'+color : color);
23481 editor.deferFocus();
23485 clickEvent:'mousedown'
23490 // now add all the items...
23493 if(!this.disable.alignments){
23496 btn('justifyleft'),
23497 btn('justifycenter'),
23498 btn('justifyright')
23502 //if(!Roo.isSafari){
23503 if(!this.disable.links){
23506 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23510 if(!this.disable.lists){
23513 btn('insertorderedlist'),
23514 btn('insertunorderedlist')
23517 if(!this.disable.sourceEdit){
23520 btn('sourceedit', true, function(btn){
23521 this.toggleSourceEdit(btn.pressed);
23528 // special menu.. - needs to be tidied up..
23529 if (!this.disable.special) {
23532 cls: 'x-edit-none',
23538 for (var i =0; i < this.specialChars.length; i++) {
23539 smenu.menu.items.push({
23541 html: this.specialChars[i],
23542 handler: function(a,b) {
23543 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23544 //editor.insertAtCursor(a.html);
23558 if (!this.disable.cleanStyles) {
23560 cls: 'x-btn-icon x-btn-clear',
23566 for (var i =0; i < this.cleanStyles.length; i++) {
23567 cmenu.menu.items.push({
23568 actiontype : this.cleanStyles[i],
23569 html: 'Remove ' + this.cleanStyles[i],
23570 handler: function(a,b) {
23573 var c = Roo.get(editorcore.doc.body);
23574 c.select('[style]').each(function(s) {
23575 s.dom.style.removeProperty(a.actiontype);
23577 editorcore.syncValue();
23582 cmenu.menu.items.push({
23583 actiontype : 'tablewidths',
23584 html: 'Remove Table Widths',
23585 handler: function(a,b) {
23586 editorcore.cleanTableWidths();
23587 editorcore.syncValue();
23591 cmenu.menu.items.push({
23592 actiontype : 'word',
23593 html: 'Remove MS Word Formating',
23594 handler: function(a,b) {
23595 editorcore.cleanWord();
23596 editorcore.syncValue();
23601 cmenu.menu.items.push({
23602 actiontype : 'all',
23603 html: 'Remove All Styles',
23604 handler: function(a,b) {
23606 var c = Roo.get(editorcore.doc.body);
23607 c.select('[style]').each(function(s) {
23608 s.dom.removeAttribute('style');
23610 editorcore.syncValue();
23615 cmenu.menu.items.push({
23616 actiontype : 'all',
23617 html: 'Remove All CSS Classes',
23618 handler: function(a,b) {
23620 var c = Roo.get(editorcore.doc.body);
23621 c.select('[class]').each(function(s) {
23622 s.dom.removeAttribute('class');
23624 editorcore.cleanWord();
23625 editorcore.syncValue();
23630 cmenu.menu.items.push({
23631 actiontype : 'tidy',
23632 html: 'Tidy HTML Source',
23633 handler: function(a,b) {
23634 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23635 editorcore.syncValue();
23644 if (!this.disable.specialElements) {
23647 cls: 'x-edit-none',
23652 for (var i =0; i < this.specialElements.length; i++) {
23653 semenu.menu.items.push(
23655 handler: function(a,b) {
23656 editor.insertAtCursor(this.ihtml);
23658 }, this.specialElements[i])
23670 for(var i =0; i< this.btns.length;i++) {
23671 var b = Roo.factory(this.btns[i],Roo.form);
23672 b.cls = 'x-edit-none';
23674 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23675 b.cls += ' x-init-enable';
23678 b.scope = editorcore;
23686 // disable everything...
23688 this.tb.items.each(function(item){
23691 item.id != editorcore.frameId+ '-sourceedit' &&
23692 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23698 this.rendered = true;
23700 // the all the btns;
23701 editor.on('editorevent', this.updateToolbar, this);
23702 // other toolbars need to implement this..
23703 //editor.on('editmodechange', this.updateToolbar, this);
23707 relayBtnCmd : function(btn) {
23708 this.editorcore.relayCmd(btn.cmd);
23710 // private used internally
23711 createLink : function(){
23712 Roo.log("create link?");
23713 var url = prompt(this.createLinkText, this.defaultLinkValue);
23714 if(url && url != 'http:/'+'/'){
23715 this.editorcore.relayCmd('createlink', url);
23721 * Protected method that will not generally be called directly. It triggers
23722 * a toolbar update by reading the markup state of the current selection in the editor.
23724 updateToolbar: function(){
23726 if(!this.editorcore.activated){
23727 this.editor.onFirstFocus();
23731 var btns = this.tb.items.map,
23732 doc = this.editorcore.doc,
23733 frameId = this.editorcore.frameId;
23735 if(!this.disable.font && !Roo.isSafari){
23737 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23738 if(name != this.fontSelect.dom.value){
23739 this.fontSelect.dom.value = name;
23743 if(!this.disable.format){
23744 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23745 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23746 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23747 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23749 if(!this.disable.alignments){
23750 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23751 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23752 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23754 if(!Roo.isSafari && !this.disable.lists){
23755 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23756 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23759 var ans = this.editorcore.getAllAncestors();
23760 if (this.formatCombo) {
23763 var store = this.formatCombo.store;
23764 this.formatCombo.setValue("");
23765 for (var i =0; i < ans.length;i++) {
23766 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23768 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23776 // hides menus... - so this cant be on a menu...
23777 Roo.menu.MenuMgr.hideAll();
23779 //this.editorsyncValue();
23783 createFontOptions : function(){
23784 var buf = [], fs = this.fontFamilies, ff, lc;
23788 for(var i = 0, len = fs.length; i< len; i++){
23790 lc = ff.toLowerCase();
23792 '<option value="',lc,'" style="font-family:',ff,';"',
23793 (this.defaultFont == lc ? ' selected="true">' : '>'),
23798 return buf.join('');
23801 toggleSourceEdit : function(sourceEditMode){
23803 Roo.log("toolbar toogle");
23804 if(sourceEditMode === undefined){
23805 sourceEditMode = !this.sourceEditMode;
23807 this.sourceEditMode = sourceEditMode === true;
23808 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23809 // just toggle the button?
23810 if(btn.pressed !== this.sourceEditMode){
23811 btn.toggle(this.sourceEditMode);
23815 if(sourceEditMode){
23816 Roo.log("disabling buttons");
23817 this.tb.items.each(function(item){
23818 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23824 Roo.log("enabling buttons");
23825 if(this.editorcore.initialized){
23826 this.tb.items.each(function(item){
23832 Roo.log("calling toggole on editor");
23833 // tell the editor that it's been pressed..
23834 this.editor.toggleSourceEdit(sourceEditMode);
23838 * Object collection of toolbar tooltips for the buttons in the editor. The key
23839 * is the command id associated with that button and the value is a valid QuickTips object.
23844 title: 'Bold (Ctrl+B)',
23845 text: 'Make the selected text bold.',
23846 cls: 'x-html-editor-tip'
23849 title: 'Italic (Ctrl+I)',
23850 text: 'Make the selected text italic.',
23851 cls: 'x-html-editor-tip'
23859 title: 'Bold (Ctrl+B)',
23860 text: 'Make the selected text bold.',
23861 cls: 'x-html-editor-tip'
23864 title: 'Italic (Ctrl+I)',
23865 text: 'Make the selected text italic.',
23866 cls: 'x-html-editor-tip'
23869 title: 'Underline (Ctrl+U)',
23870 text: 'Underline the selected text.',
23871 cls: 'x-html-editor-tip'
23874 title: 'Strikethrough',
23875 text: 'Strikethrough the selected text.',
23876 cls: 'x-html-editor-tip'
23878 increasefontsize : {
23879 title: 'Grow Text',
23880 text: 'Increase the font size.',
23881 cls: 'x-html-editor-tip'
23883 decreasefontsize : {
23884 title: 'Shrink Text',
23885 text: 'Decrease the font size.',
23886 cls: 'x-html-editor-tip'
23889 title: 'Text Highlight Color',
23890 text: 'Change the background color of the selected text.',
23891 cls: 'x-html-editor-tip'
23894 title: 'Font Color',
23895 text: 'Change the color of the selected text.',
23896 cls: 'x-html-editor-tip'
23899 title: 'Align Text Left',
23900 text: 'Align text to the left.',
23901 cls: 'x-html-editor-tip'
23904 title: 'Center Text',
23905 text: 'Center text in the editor.',
23906 cls: 'x-html-editor-tip'
23909 title: 'Align Text Right',
23910 text: 'Align text to the right.',
23911 cls: 'x-html-editor-tip'
23913 insertunorderedlist : {
23914 title: 'Bullet List',
23915 text: 'Start a bulleted list.',
23916 cls: 'x-html-editor-tip'
23918 insertorderedlist : {
23919 title: 'Numbered List',
23920 text: 'Start a numbered list.',
23921 cls: 'x-html-editor-tip'
23924 title: 'Hyperlink',
23925 text: 'Make the selected text a hyperlink.',
23926 cls: 'x-html-editor-tip'
23929 title: 'Source Edit',
23930 text: 'Switch to source editing mode.',
23931 cls: 'x-html-editor-tip'
23935 onDestroy : function(){
23938 this.tb.items.each(function(item){
23940 item.menu.removeAll();
23942 item.menu.el.destroy();
23950 onFirstFocus: function() {
23951 this.tb.items.each(function(item){
23960 // <script type="text/javascript">
23963 * Ext JS Library 1.1.1
23964 * Copyright(c) 2006-2007, Ext JS, LLC.
23971 * @class Roo.form.HtmlEditor.ToolbarContext
23976 new Roo.form.HtmlEditor({
23979 { xtype: 'ToolbarStandard', styles : {} }
23980 { xtype: 'ToolbarContext', disable : {} }
23986 * @config : {Object} disable List of elements to disable.. (not done yet.)
23987 * @config : {Object} styles Map of styles available.
23991 Roo.form.HtmlEditor.ToolbarContext = function(config)
23994 Roo.apply(this, config);
23995 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23996 // dont call parent... till later.
23997 this.styles = this.styles || {};
24002 Roo.form.HtmlEditor.ToolbarContext.types = {
24014 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24080 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24085 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24095 style : 'fontFamily',
24096 displayField: 'display',
24097 optname : 'font-family',
24146 // should we really allow this??
24147 // should this just be
24158 style : 'fontFamily',
24159 displayField: 'display',
24160 optname : 'font-family',
24167 style : 'fontFamily',
24168 displayField: 'display',
24169 optname : 'font-family',
24176 style : 'fontFamily',
24177 displayField: 'display',
24178 optname : 'font-family',
24189 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24190 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24192 Roo.form.HtmlEditor.ToolbarContext.options = {
24194 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24195 [ 'Courier New', 'Courier New'],
24196 [ 'Tahoma', 'Tahoma'],
24197 [ 'Times New Roman,serif', 'Times'],
24198 [ 'Verdana','Verdana' ]
24202 // fixme - these need to be configurable..
24205 //Roo.form.HtmlEditor.ToolbarContext.types
24208 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
24215 editorcore : false,
24217 * @cfg {Object} disable List of toolbar elements to disable
24222 * @cfg {Object} styles List of styles
24223 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
24225 * These must be defined in the page, so they get rendered correctly..
24236 init : function(editor)
24238 this.editor = editor;
24239 this.editorcore = editor.editorcore ? editor.editorcore : editor;
24240 var editorcore = this.editorcore;
24242 var fid = editorcore.frameId;
24244 function btn(id, toggle, handler){
24245 var xid = fid + '-'+ id ;
24249 cls : 'x-btn-icon x-edit-'+id,
24250 enableToggle:toggle !== false,
24251 scope: editorcore, // was editor...
24252 handler:handler||editorcore.relayBtnCmd,
24253 clickEvent:'mousedown',
24254 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24258 // create a new element.
24259 var wdiv = editor.wrap.createChild({
24261 }, editor.wrap.dom.firstChild.nextSibling, true);
24263 // can we do this more than once??
24265 // stop form submits
24268 // disable everything...
24269 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24270 this.toolbars = {};
24272 for (var i in ty) {
24274 this.toolbars[i] = this.buildToolbar(ty[i],i);
24276 this.tb = this.toolbars.BODY;
24278 this.buildFooter();
24279 this.footer.show();
24280 editor.on('hide', function( ) { this.footer.hide() }, this);
24281 editor.on('show', function( ) { this.footer.show() }, this);
24284 this.rendered = true;
24286 // the all the btns;
24287 editor.on('editorevent', this.updateToolbar, this);
24288 // other toolbars need to implement this..
24289 //editor.on('editmodechange', this.updateToolbar, this);
24295 * Protected method that will not generally be called directly. It triggers
24296 * a toolbar update by reading the markup state of the current selection in the editor.
24298 * Note you can force an update by calling on('editorevent', scope, false)
24300 updateToolbar: function(editor,ev,sel){
24303 // capture mouse up - this is handy for selecting images..
24304 // perhaps should go somewhere else...
24305 if(!this.editorcore.activated){
24306 this.editor.onFirstFocus();
24312 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24313 // selectNode - might want to handle IE?
24315 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24316 ev.target && ev.target.tagName == 'IMG') {
24317 // they have click on an image...
24318 // let's see if we can change the selection...
24321 var nodeRange = sel.ownerDocument.createRange();
24323 nodeRange.selectNode(sel);
24325 nodeRange.selectNodeContents(sel);
24327 //nodeRange.collapse(true);
24328 var s = this.editorcore.win.getSelection();
24329 s.removeAllRanges();
24330 s.addRange(nodeRange);
24334 var updateFooter = sel ? false : true;
24337 var ans = this.editorcore.getAllAncestors();
24340 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24343 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24344 sel = sel ? sel : this.editorcore.doc.body;
24345 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24348 // pick a menu that exists..
24349 var tn = sel.tagName.toUpperCase();
24350 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24352 tn = sel.tagName.toUpperCase();
24354 var lastSel = this.tb.selectedNode;
24356 this.tb.selectedNode = sel;
24358 // if current menu does not match..
24360 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24363 ///console.log("show: " + tn);
24364 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24367 this.tb.items.first().el.innerHTML = tn + ': ';
24370 // update attributes
24371 if (this.tb.fields) {
24372 this.tb.fields.each(function(e) {
24374 e.setValue(sel.style[e.stylename]);
24377 e.setValue(sel.getAttribute(e.attrname));
24381 var hasStyles = false;
24382 for(var i in this.styles) {
24389 var st = this.tb.fields.item(0);
24391 st.store.removeAll();
24394 var cn = sel.className.split(/\s+/);
24397 if (this.styles['*']) {
24399 Roo.each(this.styles['*'], function(v) {
24400 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24403 if (this.styles[tn]) {
24404 Roo.each(this.styles[tn], function(v) {
24405 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24409 st.store.loadData(avs);
24413 // flag our selected Node.
24414 this.tb.selectedNode = sel;
24417 Roo.menu.MenuMgr.hideAll();
24421 if (!updateFooter) {
24422 //this.footDisp.dom.innerHTML = '';
24425 // update the footer
24429 this.footerEls = ans.reverse();
24430 Roo.each(this.footerEls, function(a,i) {
24431 if (!a) { return; }
24432 html += html.length ? ' > ' : '';
24434 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24439 var sz = this.footDisp.up('td').getSize();
24440 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24441 this.footDisp.dom.style.marginLeft = '5px';
24443 this.footDisp.dom.style.overflow = 'hidden';
24445 this.footDisp.dom.innerHTML = html;
24447 //this.editorsyncValue();
24454 onDestroy : function(){
24457 this.tb.items.each(function(item){
24459 item.menu.removeAll();
24461 item.menu.el.destroy();
24469 onFirstFocus: function() {
24470 // need to do this for all the toolbars..
24471 this.tb.items.each(function(item){
24475 buildToolbar: function(tlist, nm)
24477 var editor = this.editor;
24478 var editorcore = this.editorcore;
24479 // create a new element.
24480 var wdiv = editor.wrap.createChild({
24482 }, editor.wrap.dom.firstChild.nextSibling, true);
24485 var tb = new Roo.Toolbar(wdiv);
24488 tb.add(nm+ ": ");
24491 for(var i in this.styles) {
24496 if (styles && styles.length) {
24498 // this needs a multi-select checkbox...
24499 tb.addField( new Roo.form.ComboBox({
24500 store: new Roo.data.SimpleStore({
24502 fields: ['val', 'selected'],
24505 name : '-roo-edit-className',
24506 attrname : 'className',
24507 displayField: 'val',
24511 triggerAction: 'all',
24512 emptyText:'Select Style',
24513 selectOnFocus:true,
24516 'select': function(c, r, i) {
24517 // initial support only for on class per el..
24518 tb.selectedNode.className = r ? r.get('val') : '';
24519 editorcore.syncValue();
24526 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24527 var tbops = tbc.options;
24529 for (var i in tlist) {
24531 var item = tlist[i];
24532 tb.add(item.title + ": ");
24535 //optname == used so you can configure the options available..
24536 var opts = item.opts ? item.opts : false;
24537 if (item.optname) {
24538 opts = tbops[item.optname];
24543 // opts == pulldown..
24544 tb.addField( new Roo.form.ComboBox({
24545 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24547 fields: ['val', 'display'],
24550 name : '-roo-edit-' + i,
24552 stylename : item.style ? item.style : false,
24553 displayField: item.displayField ? item.displayField : 'val',
24554 valueField : 'val',
24556 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24558 triggerAction: 'all',
24559 emptyText:'Select',
24560 selectOnFocus:true,
24561 width: item.width ? item.width : 130,
24563 'select': function(c, r, i) {
24565 tb.selectedNode.style[c.stylename] = r.get('val');
24568 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24577 tb.addField( new Roo.form.TextField({
24580 //allowBlank:false,
24585 tb.addField( new Roo.form.TextField({
24586 name: '-roo-edit-' + i,
24593 'change' : function(f, nv, ov) {
24594 tb.selectedNode.setAttribute(f.attrname, nv);
24595 editorcore.syncValue();
24608 text: 'Stylesheets',
24611 click : function ()
24613 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24621 text: 'Remove Tag',
24624 click : function ()
24627 // undo does not work.
24629 var sn = tb.selectedNode;
24631 var pn = sn.parentNode;
24633 var stn = sn.childNodes[0];
24634 var en = sn.childNodes[sn.childNodes.length - 1 ];
24635 while (sn.childNodes.length) {
24636 var node = sn.childNodes[0];
24637 sn.removeChild(node);
24639 pn.insertBefore(node, sn);
24642 pn.removeChild(sn);
24643 var range = editorcore.createRange();
24645 range.setStart(stn,0);
24646 range.setEnd(en,0); //????
24647 //range.selectNode(sel);
24650 var selection = editorcore.getSelection();
24651 selection.removeAllRanges();
24652 selection.addRange(range);
24656 //_this.updateToolbar(null, null, pn);
24657 _this.updateToolbar(null, null, null);
24658 _this.footDisp.dom.innerHTML = '';
24668 tb.el.on('click', function(e){
24669 e.preventDefault(); // what does this do?
24671 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24674 // dont need to disable them... as they will get hidden
24679 buildFooter : function()
24682 var fel = this.editor.wrap.createChild();
24683 this.footer = new Roo.Toolbar(fel);
24684 // toolbar has scrolly on left / right?
24685 var footDisp= new Roo.Toolbar.Fill();
24691 handler : function() {
24692 _t.footDisp.scrollTo('left',0,true)
24696 this.footer.add( footDisp );
24701 handler : function() {
24703 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24707 var fel = Roo.get(footDisp.el);
24708 fel.addClass('x-editor-context');
24709 this.footDispWrap = fel;
24710 this.footDispWrap.overflow = 'hidden';
24712 this.footDisp = fel.createChild();
24713 this.footDispWrap.on('click', this.onContextClick, this)
24717 onContextClick : function (ev,dom)
24719 ev.preventDefault();
24720 var cn = dom.className;
24722 if (!cn.match(/x-ed-loc-/)) {
24725 var n = cn.split('-').pop();
24726 var ans = this.footerEls;
24730 var range = this.editorcore.createRange();
24732 range.selectNodeContents(sel);
24733 //range.selectNode(sel);
24736 var selection = this.editorcore.getSelection();
24737 selection.removeAllRanges();
24738 selection.addRange(range);
24742 this.updateToolbar(null, null, sel);
24759 * Ext JS Library 1.1.1
24760 * Copyright(c) 2006-2007, Ext JS, LLC.
24762 * Originally Released Under LGPL - original licence link has changed is not relivant.
24765 * <script type="text/javascript">
24769 * @class Roo.form.BasicForm
24770 * @extends Roo.util.Observable
24771 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24773 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24774 * @param {Object} config Configuration options
24776 Roo.form.BasicForm = function(el, config){
24777 this.allItems = [];
24778 this.childForms = [];
24779 Roo.apply(this, config);
24781 * The Roo.form.Field items in this form.
24782 * @type MixedCollection
24786 this.items = new Roo.util.MixedCollection(false, function(o){
24787 return o.id || (o.id = Roo.id());
24791 * @event beforeaction
24792 * Fires before any action is performed. Return false to cancel the action.
24793 * @param {Form} this
24794 * @param {Action} action The action to be performed
24796 beforeaction: true,
24798 * @event actionfailed
24799 * Fires when an action fails.
24800 * @param {Form} this
24801 * @param {Action} action The action that failed
24803 actionfailed : true,
24805 * @event actioncomplete
24806 * Fires when an action is completed.
24807 * @param {Form} this
24808 * @param {Action} action The action that completed
24810 actioncomplete : true
24815 Roo.form.BasicForm.superclass.constructor.call(this);
24817 Roo.form.BasicForm.popover.apply();
24820 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24822 * @cfg {String} method
24823 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24826 * @cfg {DataReader} reader
24827 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24828 * This is optional as there is built-in support for processing JSON.
24831 * @cfg {DataReader} errorReader
24832 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24833 * This is completely optional as there is built-in support for processing JSON.
24836 * @cfg {String} url
24837 * The URL to use for form actions if one isn't supplied in the action options.
24840 * @cfg {Boolean} fileUpload
24841 * Set to true if this form is a file upload.
24845 * @cfg {Object} baseParams
24846 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24851 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24856 activeAction : null,
24859 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24860 * or setValues() data instead of when the form was first created.
24862 trackResetOnLoad : false,
24866 * childForms - used for multi-tab forms
24869 childForms : false,
24872 * allItems - full list of fields.
24878 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24879 * element by passing it or its id or mask the form itself by passing in true.
24882 waitMsgTarget : false,
24887 disableMask : false,
24890 * @cfg {Boolean} errorMask (true|false) default false
24895 * @cfg {Number} maskOffset Default 100
24900 initEl : function(el){
24901 this.el = Roo.get(el);
24902 this.id = this.el.id || Roo.id();
24903 this.el.on('submit', this.onSubmit, this);
24904 this.el.addClass('x-form');
24908 onSubmit : function(e){
24913 * Returns true if client-side validation on the form is successful.
24916 isValid : function(){
24918 var target = false;
24919 this.items.each(function(f){
24926 if(!target && f.el.isVisible(true)){
24931 if(this.errorMask && !valid){
24932 Roo.form.BasicForm.popover.mask(this, target);
24939 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24942 isDirty : function(){
24944 this.items.each(function(f){
24954 * Returns true if any fields in this form have changed since their original load. (New version)
24958 hasChanged : function()
24961 this.items.each(function(f){
24962 if(f.hasChanged()){
24971 * Resets all hasChanged to 'false' -
24972 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24973 * So hasChanged storage is only to be used for this purpose
24976 resetHasChanged : function()
24978 this.items.each(function(f){
24979 f.resetHasChanged();
24986 * Performs a predefined action (submit or load) or custom actions you define on this form.
24987 * @param {String} actionName The name of the action type
24988 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24989 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24990 * accept other config options):
24992 Property Type Description
24993 ---------------- --------------- ----------------------------------------------------------------------------------
24994 url String The url for the action (defaults to the form's url)
24995 method String The form method to use (defaults to the form's method, or POST if not defined)
24996 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
24997 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
24998 validate the form on the client (defaults to false)
25000 * @return {BasicForm} this
25002 doAction : function(action, options){
25003 if(typeof action == 'string'){
25004 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25006 if(this.fireEvent('beforeaction', this, action) !== false){
25007 this.beforeAction(action);
25008 action.run.defer(100, action);
25014 * Shortcut to do a submit action.
25015 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25016 * @return {BasicForm} this
25018 submit : function(options){
25019 this.doAction('submit', options);
25024 * Shortcut to do a load action.
25025 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25026 * @return {BasicForm} this
25028 load : function(options){
25029 this.doAction('load', options);
25034 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25035 * @param {Record} record The record to edit
25036 * @return {BasicForm} this
25038 updateRecord : function(record){
25039 record.beginEdit();
25040 var fs = record.fields;
25041 fs.each(function(f){
25042 var field = this.findField(f.name);
25044 record.set(f.name, field.getValue());
25052 * Loads an Roo.data.Record into this form.
25053 * @param {Record} record The record to load
25054 * @return {BasicForm} this
25056 loadRecord : function(record){
25057 this.setValues(record.data);
25062 beforeAction : function(action){
25063 var o = action.options;
25065 if(!this.disableMask) {
25066 if(this.waitMsgTarget === true){
25067 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25068 }else if(this.waitMsgTarget){
25069 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25070 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25072 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25080 afterAction : function(action, success){
25081 this.activeAction = null;
25082 var o = action.options;
25084 if(!this.disableMask) {
25085 if(this.waitMsgTarget === true){
25087 }else if(this.waitMsgTarget){
25088 this.waitMsgTarget.unmask();
25090 Roo.MessageBox.updateProgress(1);
25091 Roo.MessageBox.hide();
25099 Roo.callback(o.success, o.scope, [this, action]);
25100 this.fireEvent('actioncomplete', this, action);
25104 // failure condition..
25105 // we have a scenario where updates need confirming.
25106 // eg. if a locking scenario exists..
25107 // we look for { errors : { needs_confirm : true }} in the response.
25109 (typeof(action.result) != 'undefined') &&
25110 (typeof(action.result.errors) != 'undefined') &&
25111 (typeof(action.result.errors.needs_confirm) != 'undefined')
25114 Roo.MessageBox.confirm(
25115 "Change requires confirmation",
25116 action.result.errorMsg,
25121 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
25131 Roo.callback(o.failure, o.scope, [this, action]);
25132 // show an error message if no failed handler is set..
25133 if (!this.hasListener('actionfailed')) {
25134 Roo.MessageBox.alert("Error",
25135 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25136 action.result.errorMsg :
25137 "Saving Failed, please check your entries or try again"
25141 this.fireEvent('actionfailed', this, action);
25147 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25148 * @param {String} id The value to search for
25151 findField : function(id){
25152 var field = this.items.get(id);
25154 this.items.each(function(f){
25155 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25161 return field || null;
25165 * Add a secondary form to this one,
25166 * Used to provide tabbed forms. One form is primary, with hidden values
25167 * which mirror the elements from the other forms.
25169 * @param {Roo.form.Form} form to add.
25172 addForm : function(form)
25175 if (this.childForms.indexOf(form) > -1) {
25179 this.childForms.push(form);
25181 Roo.each(form.allItems, function (fe) {
25183 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25184 if (this.findField(n)) { // already added..
25187 var add = new Roo.form.Hidden({
25190 add.render(this.el);
25197 * Mark fields in this form invalid in bulk.
25198 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25199 * @return {BasicForm} this
25201 markInvalid : function(errors){
25202 if(errors instanceof Array){
25203 for(var i = 0, len = errors.length; i < len; i++){
25204 var fieldError = errors[i];
25205 var f = this.findField(fieldError.id);
25207 f.markInvalid(fieldError.msg);
25213 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25214 field.markInvalid(errors[id]);
25218 Roo.each(this.childForms || [], function (f) {
25219 f.markInvalid(errors);
25226 * Set values for fields in this form in bulk.
25227 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25228 * @return {BasicForm} this
25230 setValues : function(values){
25231 if(values instanceof Array){ // array of objects
25232 for(var i = 0, len = values.length; i < len; i++){
25234 var f = this.findField(v.id);
25236 f.setValue(v.value);
25237 if(this.trackResetOnLoad){
25238 f.originalValue = f.getValue();
25242 }else{ // object hash
25245 if(typeof values[id] != 'function' && (field = this.findField(id))){
25247 if (field.setFromData &&
25248 field.valueField &&
25249 field.displayField &&
25250 // combos' with local stores can
25251 // be queried via setValue()
25252 // to set their value..
25253 (field.store && !field.store.isLocal)
25257 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25258 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25259 field.setFromData(sd);
25262 field.setValue(values[id]);
25266 if(this.trackResetOnLoad){
25267 field.originalValue = field.getValue();
25272 this.resetHasChanged();
25275 Roo.each(this.childForms || [], function (f) {
25276 f.setValues(values);
25277 f.resetHasChanged();
25284 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25285 * they are returned as an array.
25286 * @param {Boolean} asString
25289 getValues : function(asString){
25290 if (this.childForms) {
25291 // copy values from the child forms
25292 Roo.each(this.childForms, function (f) {
25293 this.setValues(f.getValues());
25298 if (typeof(FormData) != 'undefined' && asString !== true) {
25299 var fd = (new FormData(this.el.dom)).entries();
25301 var ent = fd.next();
25302 while (!ent.done) {
25303 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25310 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25311 if(asString === true){
25314 return Roo.urlDecode(fs);
25318 * Returns the fields in this form as an object with key/value pairs.
25319 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25322 getFieldValues : function(with_hidden)
25324 if (this.childForms) {
25325 // copy values from the child forms
25326 // should this call getFieldValues - probably not as we do not currently copy
25327 // hidden fields when we generate..
25328 Roo.each(this.childForms, function (f) {
25329 this.setValues(f.getValues());
25334 this.items.each(function(f){
25335 if (!f.getName()) {
25338 var v = f.getValue();
25339 if (f.inputType =='radio') {
25340 if (typeof(ret[f.getName()]) == 'undefined') {
25341 ret[f.getName()] = ''; // empty..
25344 if (!f.el.dom.checked) {
25348 v = f.el.dom.value;
25352 // not sure if this supported any more..
25353 if ((typeof(v) == 'object') && f.getRawValue) {
25354 v = f.getRawValue() ; // dates..
25356 // combo boxes where name != hiddenName...
25357 if (f.name != f.getName()) {
25358 ret[f.name] = f.getRawValue();
25360 ret[f.getName()] = v;
25367 * Clears all invalid messages in this form.
25368 * @return {BasicForm} this
25370 clearInvalid : function(){
25371 this.items.each(function(f){
25375 Roo.each(this.childForms || [], function (f) {
25384 * Resets this form.
25385 * @return {BasicForm} this
25387 reset : function(){
25388 this.items.each(function(f){
25392 Roo.each(this.childForms || [], function (f) {
25395 this.resetHasChanged();
25401 * Add Roo.form components to this form.
25402 * @param {Field} field1
25403 * @param {Field} field2 (optional)
25404 * @param {Field} etc (optional)
25405 * @return {BasicForm} this
25408 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25414 * Removes a field from the items collection (does NOT remove its markup).
25415 * @param {Field} field
25416 * @return {BasicForm} this
25418 remove : function(field){
25419 this.items.remove(field);
25424 * Looks at the fields in this form, checks them for an id attribute,
25425 * and calls applyTo on the existing dom element with that id.
25426 * @return {BasicForm} this
25428 render : function(){
25429 this.items.each(function(f){
25430 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25438 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25439 * @param {Object} values
25440 * @return {BasicForm} this
25442 applyToFields : function(o){
25443 this.items.each(function(f){
25450 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25451 * @param {Object} values
25452 * @return {BasicForm} this
25454 applyIfToFields : function(o){
25455 this.items.each(function(f){
25463 Roo.BasicForm = Roo.form.BasicForm;
25465 Roo.apply(Roo.form.BasicForm, {
25479 intervalID : false,
25485 if(this.isApplied){
25490 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25491 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25492 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25493 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25496 this.maskEl.top.enableDisplayMode("block");
25497 this.maskEl.left.enableDisplayMode("block");
25498 this.maskEl.bottom.enableDisplayMode("block");
25499 this.maskEl.right.enableDisplayMode("block");
25501 Roo.get(document.body).on('click', function(){
25505 Roo.get(document.body).on('touchstart', function(){
25509 this.isApplied = true
25512 mask : function(form, target)
25516 this.target = target;
25518 if(!this.form.errorMask || !target.el){
25522 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25524 var ot = this.target.el.calcOffsetsTo(scrollable);
25526 var scrollTo = ot[1] - this.form.maskOffset;
25528 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25530 scrollable.scrollTo('top', scrollTo);
25532 var el = this.target.wrap || this.target.el;
25534 var box = el.getBox();
25536 this.maskEl.top.setStyle('position', 'absolute');
25537 this.maskEl.top.setStyle('z-index', 10000);
25538 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25539 this.maskEl.top.setLeft(0);
25540 this.maskEl.top.setTop(0);
25541 this.maskEl.top.show();
25543 this.maskEl.left.setStyle('position', 'absolute');
25544 this.maskEl.left.setStyle('z-index', 10000);
25545 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25546 this.maskEl.left.setLeft(0);
25547 this.maskEl.left.setTop(box.y - this.padding);
25548 this.maskEl.left.show();
25550 this.maskEl.bottom.setStyle('position', 'absolute');
25551 this.maskEl.bottom.setStyle('z-index', 10000);
25552 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25553 this.maskEl.bottom.setLeft(0);
25554 this.maskEl.bottom.setTop(box.bottom + this.padding);
25555 this.maskEl.bottom.show();
25557 this.maskEl.right.setStyle('position', 'absolute');
25558 this.maskEl.right.setStyle('z-index', 10000);
25559 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25560 this.maskEl.right.setLeft(box.right + this.padding);
25561 this.maskEl.right.setTop(box.y - this.padding);
25562 this.maskEl.right.show();
25564 this.intervalID = window.setInterval(function() {
25565 Roo.form.BasicForm.popover.unmask();
25568 window.onwheel = function(){ return false;};
25570 (function(){ this.isMasked = true; }).defer(500, this);
25574 unmask : function()
25576 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25580 this.maskEl.top.setStyle('position', 'absolute');
25581 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25582 this.maskEl.top.hide();
25584 this.maskEl.left.setStyle('position', 'absolute');
25585 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25586 this.maskEl.left.hide();
25588 this.maskEl.bottom.setStyle('position', 'absolute');
25589 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25590 this.maskEl.bottom.hide();
25592 this.maskEl.right.setStyle('position', 'absolute');
25593 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25594 this.maskEl.right.hide();
25596 window.onwheel = function(){ return true;};
25598 if(this.intervalID){
25599 window.clearInterval(this.intervalID);
25600 this.intervalID = false;
25603 this.isMasked = false;
25611 * Ext JS Library 1.1.1
25612 * Copyright(c) 2006-2007, Ext JS, LLC.
25614 * Originally Released Under LGPL - original licence link has changed is not relivant.
25617 * <script type="text/javascript">
25621 * @class Roo.form.Form
25622 * @extends Roo.form.BasicForm
25623 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25625 * @param {Object} config Configuration options
25627 Roo.form.Form = function(config){
25629 if (config.items) {
25630 xitems = config.items;
25631 delete config.items;
25635 Roo.form.Form.superclass.constructor.call(this, null, config);
25636 this.url = this.url || this.action;
25638 this.root = new Roo.form.Layout(Roo.applyIf({
25642 this.active = this.root;
25644 * Array of all the buttons that have been added to this form via {@link addButton}
25648 this.allItems = [];
25651 * @event clientvalidation
25652 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25653 * @param {Form} this
25654 * @param {Boolean} valid true if the form has passed client-side validation
25656 clientvalidation: true,
25659 * Fires when the form is rendered
25660 * @param {Roo.form.Form} form
25665 if (this.progressUrl) {
25666 // push a hidden field onto the list of fields..
25670 name : 'UPLOAD_IDENTIFIER'
25675 Roo.each(xitems, this.addxtype, this);
25679 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25681 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25684 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25687 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25689 buttonAlign:'center',
25692 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25697 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25698 * This property cascades to child containers if not set.
25703 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25704 * fires a looping event with that state. This is required to bind buttons to the valid
25705 * state using the config value formBind:true on the button.
25707 monitorValid : false,
25710 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25715 * @cfg {String} progressUrl - Url to return progress data
25718 progressUrl : false,
25720 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25721 * sending a formdata with extra parameters - eg uploaded elements.
25727 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25728 * fields are added and the column is closed. If no fields are passed the column remains open
25729 * until end() is called.
25730 * @param {Object} config The config to pass to the column
25731 * @param {Field} field1 (optional)
25732 * @param {Field} field2 (optional)
25733 * @param {Field} etc (optional)
25734 * @return Column The column container object
25736 column : function(c){
25737 var col = new Roo.form.Column(c);
25739 if(arguments.length > 1){ // duplicate code required because of Opera
25740 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25747 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25748 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25749 * until end() is called.
25750 * @param {Object} config The config to pass to the fieldset
25751 * @param {Field} field1 (optional)
25752 * @param {Field} field2 (optional)
25753 * @param {Field} etc (optional)
25754 * @return FieldSet The fieldset container object
25756 fieldset : function(c){
25757 var fs = new Roo.form.FieldSet(c);
25759 if(arguments.length > 1){ // duplicate code required because of Opera
25760 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25767 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25768 * fields are added and the container is closed. If no fields are passed the container remains open
25769 * until end() is called.
25770 * @param {Object} config The config to pass to the Layout
25771 * @param {Field} field1 (optional)
25772 * @param {Field} field2 (optional)
25773 * @param {Field} etc (optional)
25774 * @return Layout The container object
25776 container : function(c){
25777 var l = new Roo.form.Layout(c);
25779 if(arguments.length > 1){ // duplicate code required because of Opera
25780 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25787 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25788 * @param {Object} container A Roo.form.Layout or subclass of Layout
25789 * @return {Form} this
25791 start : function(c){
25792 // cascade label info
25793 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25794 this.active.stack.push(c);
25795 c.ownerCt = this.active;
25801 * Closes the current open container
25802 * @return {Form} this
25805 if(this.active == this.root){
25808 this.active = this.active.ownerCt;
25813 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25814 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25815 * as the label of the field.
25816 * @param {Field} field1
25817 * @param {Field} field2 (optional)
25818 * @param {Field} etc. (optional)
25819 * @return {Form} this
25822 this.active.stack.push.apply(this.active.stack, arguments);
25823 this.allItems.push.apply(this.allItems,arguments);
25825 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25826 if(a[i].isFormField){
25831 Roo.form.Form.superclass.add.apply(this, r);
25841 * Find any element that has been added to a form, using it's ID or name
25842 * This can include framesets, columns etc. along with regular fields..
25843 * @param {String} id - id or name to find.
25845 * @return {Element} e - or false if nothing found.
25847 findbyId : function(id)
25853 Roo.each(this.allItems, function(f){
25854 if (f.id == id || f.name == id ){
25865 * Render this form into the passed container. This should only be called once!
25866 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25867 * @return {Form} this
25869 render : function(ct)
25875 var o = this.autoCreate || {
25877 method : this.method || 'POST',
25878 id : this.id || Roo.id()
25880 this.initEl(ct.createChild(o));
25882 this.root.render(this.el);
25886 this.items.each(function(f){
25887 f.render('x-form-el-'+f.id);
25890 if(this.buttons.length > 0){
25891 // tables are required to maintain order and for correct IE layout
25892 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25893 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25894 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25896 var tr = tb.getElementsByTagName('tr')[0];
25897 for(var i = 0, len = this.buttons.length; i < len; i++) {
25898 var b = this.buttons[i];
25899 var td = document.createElement('td');
25900 td.className = 'x-form-btn-td';
25901 b.render(tr.appendChild(td));
25904 if(this.monitorValid){ // initialize after render
25905 this.startMonitoring();
25907 this.fireEvent('rendered', this);
25912 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25913 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25914 * object or a valid Roo.DomHelper element config
25915 * @param {Function} handler The function called when the button is clicked
25916 * @param {Object} scope (optional) The scope of the handler function
25917 * @return {Roo.Button}
25919 addButton : function(config, handler, scope){
25923 minWidth: this.minButtonWidth,
25926 if(typeof config == "string"){
25929 Roo.apply(bc, config);
25931 var btn = new Roo.Button(null, bc);
25932 this.buttons.push(btn);
25937 * Adds a series of form elements (using the xtype property as the factory method.
25938 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25939 * @param {Object} config
25942 addxtype : function()
25944 var ar = Array.prototype.slice.call(arguments, 0);
25946 for(var i = 0; i < ar.length; i++) {
25948 continue; // skip -- if this happends something invalid got sent, we
25949 // should ignore it, as basically that interface element will not show up
25950 // and that should be pretty obvious!!
25953 if (Roo.form[ar[i].xtype]) {
25955 var fe = Roo.factory(ar[i], Roo.form);
25961 fe.store.form = this;
25966 this.allItems.push(fe);
25967 if (fe.items && fe.addxtype) {
25968 fe.addxtype.apply(fe, fe.items);
25978 // console.log('adding ' + ar[i].xtype);
25980 if (ar[i].xtype == 'Button') {
25981 //console.log('adding button');
25982 //console.log(ar[i]);
25983 this.addButton(ar[i]);
25984 this.allItems.push(fe);
25988 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25989 alert('end is not supported on xtype any more, use items');
25991 // //console.log('adding end');
25999 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26000 * option "monitorValid"
26002 startMonitoring : function(){
26005 Roo.TaskMgr.start({
26006 run : this.bindHandler,
26007 interval : this.monitorPoll || 200,
26014 * Stops monitoring of the valid state of this form
26016 stopMonitoring : function(){
26017 this.bound = false;
26021 bindHandler : function(){
26023 return false; // stops binding
26026 this.items.each(function(f){
26027 if(!f.isValid(true)){
26032 for(var i = 0, len = this.buttons.length; i < len; i++){
26033 var btn = this.buttons[i];
26034 if(btn.formBind === true && btn.disabled === valid){
26035 btn.setDisabled(!valid);
26038 this.fireEvent('clientvalidation', this, valid);
26052 Roo.Form = Roo.form.Form;
26055 * Ext JS Library 1.1.1
26056 * Copyright(c) 2006-2007, Ext JS, LLC.
26058 * Originally Released Under LGPL - original licence link has changed is not relivant.
26061 * <script type="text/javascript">
26064 // as we use this in bootstrap.
26065 Roo.namespace('Roo.form');
26067 * @class Roo.form.Action
26068 * Internal Class used to handle form actions
26070 * @param {Roo.form.BasicForm} el The form element or its id
26071 * @param {Object} config Configuration options
26076 // define the action interface
26077 Roo.form.Action = function(form, options){
26079 this.options = options || {};
26082 * Client Validation Failed
26085 Roo.form.Action.CLIENT_INVALID = 'client';
26087 * Server Validation Failed
26090 Roo.form.Action.SERVER_INVALID = 'server';
26092 * Connect to Server Failed
26095 Roo.form.Action.CONNECT_FAILURE = 'connect';
26097 * Reading Data from Server Failed
26100 Roo.form.Action.LOAD_FAILURE = 'load';
26102 Roo.form.Action.prototype = {
26104 failureType : undefined,
26105 response : undefined,
26106 result : undefined,
26108 // interface method
26109 run : function(options){
26113 // interface method
26114 success : function(response){
26118 // interface method
26119 handleResponse : function(response){
26123 // default connection failure
26124 failure : function(response){
26126 this.response = response;
26127 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26128 this.form.afterAction(this, false);
26131 processResponse : function(response){
26132 this.response = response;
26133 if(!response.responseText){
26136 this.result = this.handleResponse(response);
26137 return this.result;
26140 // utility functions used internally
26141 getUrl : function(appendParams){
26142 var url = this.options.url || this.form.url || this.form.el.dom.action;
26144 var p = this.getParams();
26146 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26152 getMethod : function(){
26153 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26156 getParams : function(){
26157 var bp = this.form.baseParams;
26158 var p = this.options.params;
26160 if(typeof p == "object"){
26161 p = Roo.urlEncode(Roo.applyIf(p, bp));
26162 }else if(typeof p == 'string' && bp){
26163 p += '&' + Roo.urlEncode(bp);
26166 p = Roo.urlEncode(bp);
26171 createCallback : function(){
26173 success: this.success,
26174 failure: this.failure,
26176 timeout: (this.form.timeout*1000),
26177 upload: this.form.fileUpload ? this.success : undefined
26182 Roo.form.Action.Submit = function(form, options){
26183 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26186 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26189 haveProgress : false,
26190 uploadComplete : false,
26192 // uploadProgress indicator.
26193 uploadProgress : function()
26195 if (!this.form.progressUrl) {
26199 if (!this.haveProgress) {
26200 Roo.MessageBox.progress("Uploading", "Uploading");
26202 if (this.uploadComplete) {
26203 Roo.MessageBox.hide();
26207 this.haveProgress = true;
26209 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26211 var c = new Roo.data.Connection();
26213 url : this.form.progressUrl,
26218 success : function(req){
26219 //console.log(data);
26223 rdata = Roo.decode(req.responseText)
26225 Roo.log("Invalid data from server..");
26229 if (!rdata || !rdata.success) {
26231 Roo.MessageBox.alert(Roo.encode(rdata));
26234 var data = rdata.data;
26236 if (this.uploadComplete) {
26237 Roo.MessageBox.hide();
26242 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26243 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26246 this.uploadProgress.defer(2000,this);
26249 failure: function(data) {
26250 Roo.log('progress url failed ');
26261 // run get Values on the form, so it syncs any secondary forms.
26262 this.form.getValues();
26264 var o = this.options;
26265 var method = this.getMethod();
26266 var isPost = method == 'POST';
26267 if(o.clientValidation === false || this.form.isValid()){
26269 if (this.form.progressUrl) {
26270 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26271 (new Date() * 1) + '' + Math.random());
26276 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26277 form:this.form.el.dom,
26278 url:this.getUrl(!isPost),
26280 params:isPost ? this.getParams() : null,
26281 isUpload: this.form.fileUpload,
26282 formData : this.form.formData
26285 this.uploadProgress();
26287 }else if (o.clientValidation !== false){ // client validation failed
26288 this.failureType = Roo.form.Action.CLIENT_INVALID;
26289 this.form.afterAction(this, false);
26293 success : function(response)
26295 this.uploadComplete= true;
26296 if (this.haveProgress) {
26297 Roo.MessageBox.hide();
26301 var result = this.processResponse(response);
26302 if(result === true || result.success){
26303 this.form.afterAction(this, true);
26307 this.form.markInvalid(result.errors);
26308 this.failureType = Roo.form.Action.SERVER_INVALID;
26310 this.form.afterAction(this, false);
26312 failure : function(response)
26314 this.uploadComplete= true;
26315 if (this.haveProgress) {
26316 Roo.MessageBox.hide();
26319 this.response = response;
26320 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26321 this.form.afterAction(this, false);
26324 handleResponse : function(response){
26325 if(this.form.errorReader){
26326 var rs = this.form.errorReader.read(response);
26329 for(var i = 0, len = rs.records.length; i < len; i++) {
26330 var r = rs.records[i];
26331 errors[i] = r.data;
26334 if(errors.length < 1){
26338 success : rs.success,
26344 ret = Roo.decode(response.responseText);
26348 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26358 Roo.form.Action.Load = function(form, options){
26359 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26360 this.reader = this.form.reader;
26363 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26368 Roo.Ajax.request(Roo.apply(
26369 this.createCallback(), {
26370 method:this.getMethod(),
26371 url:this.getUrl(false),
26372 params:this.getParams()
26376 success : function(response){
26378 var result = this.processResponse(response);
26379 if(result === true || !result.success || !result.data){
26380 this.failureType = Roo.form.Action.LOAD_FAILURE;
26381 this.form.afterAction(this, false);
26384 this.form.clearInvalid();
26385 this.form.setValues(result.data);
26386 this.form.afterAction(this, true);
26389 handleResponse : function(response){
26390 if(this.form.reader){
26391 var rs = this.form.reader.read(response);
26392 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26394 success : rs.success,
26398 return Roo.decode(response.responseText);
26402 Roo.form.Action.ACTION_TYPES = {
26403 'load' : Roo.form.Action.Load,
26404 'submit' : Roo.form.Action.Submit
26407 * Ext JS Library 1.1.1
26408 * Copyright(c) 2006-2007, Ext JS, LLC.
26410 * Originally Released Under LGPL - original licence link has changed is not relivant.
26413 * <script type="text/javascript">
26417 * @class Roo.form.Layout
26418 * @extends Roo.Component
26419 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26421 * @param {Object} config Configuration options
26423 Roo.form.Layout = function(config){
26425 if (config.items) {
26426 xitems = config.items;
26427 delete config.items;
26429 Roo.form.Layout.superclass.constructor.call(this, config);
26431 Roo.each(xitems, this.addxtype, this);
26435 Roo.extend(Roo.form.Layout, Roo.Component, {
26437 * @cfg {String/Object} autoCreate
26438 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26441 * @cfg {String/Object/Function} style
26442 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26443 * a function which returns such a specification.
26446 * @cfg {String} labelAlign
26447 * Valid values are "left," "top" and "right" (defaults to "left")
26450 * @cfg {Number} labelWidth
26451 * Fixed width in pixels of all field labels (defaults to undefined)
26454 * @cfg {Boolean} clear
26455 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26459 * @cfg {String} labelSeparator
26460 * The separator to use after field labels (defaults to ':')
26462 labelSeparator : ':',
26464 * @cfg {Boolean} hideLabels
26465 * True to suppress the display of field labels in this layout (defaults to false)
26467 hideLabels : false,
26470 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26475 onRender : function(ct, position){
26476 if(this.el){ // from markup
26477 this.el = Roo.get(this.el);
26478 }else { // generate
26479 var cfg = this.getAutoCreate();
26480 this.el = ct.createChild(cfg, position);
26483 this.el.applyStyles(this.style);
26485 if(this.labelAlign){
26486 this.el.addClass('x-form-label-'+this.labelAlign);
26488 if(this.hideLabels){
26489 this.labelStyle = "display:none";
26490 this.elementStyle = "padding-left:0;";
26492 if(typeof this.labelWidth == 'number'){
26493 this.labelStyle = "width:"+this.labelWidth+"px;";
26494 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26496 if(this.labelAlign == 'top'){
26497 this.labelStyle = "width:auto;";
26498 this.elementStyle = "padding-left:0;";
26501 var stack = this.stack;
26502 var slen = stack.length;
26504 if(!this.fieldTpl){
26505 var t = new Roo.Template(
26506 '<div class="x-form-item {5}">',
26507 '<label for="{0}" style="{2}">{1}{4}</label>',
26508 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26510 '</div><div class="x-form-clear-left"></div>'
26512 t.disableFormats = true;
26514 Roo.form.Layout.prototype.fieldTpl = t;
26516 for(var i = 0; i < slen; i++) {
26517 if(stack[i].isFormField){
26518 this.renderField(stack[i]);
26520 this.renderComponent(stack[i]);
26525 this.el.createChild({cls:'x-form-clear'});
26530 renderField : function(f){
26531 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26534 f.labelStyle||this.labelStyle||'', //2
26535 this.elementStyle||'', //3
26536 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26537 f.itemCls||this.itemCls||'' //5
26538 ], true).getPrevSibling());
26542 renderComponent : function(c){
26543 c.render(c.isLayout ? this.el : this.el.createChild());
26546 * Adds a object form elements (using the xtype property as the factory method.)
26547 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26548 * @param {Object} config
26550 addxtype : function(o)
26552 // create the lement.
26553 o.form = this.form;
26554 var fe = Roo.factory(o, Roo.form);
26555 this.form.allItems.push(fe);
26556 this.stack.push(fe);
26558 if (fe.isFormField) {
26559 this.form.items.add(fe);
26567 * @class Roo.form.Column
26568 * @extends Roo.form.Layout
26569 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26571 * @param {Object} config Configuration options
26573 Roo.form.Column = function(config){
26574 Roo.form.Column.superclass.constructor.call(this, config);
26577 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26579 * @cfg {Number/String} width
26580 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26583 * @cfg {String/Object} autoCreate
26584 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26588 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26591 onRender : function(ct, position){
26592 Roo.form.Column.superclass.onRender.call(this, ct, position);
26594 this.el.setWidth(this.width);
26601 * @class Roo.form.Row
26602 * @extends Roo.form.Layout
26603 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26605 * @param {Object} config Configuration options
26609 Roo.form.Row = function(config){
26610 Roo.form.Row.superclass.constructor.call(this, config);
26613 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26615 * @cfg {Number/String} width
26616 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26619 * @cfg {Number/String} height
26620 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26622 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26626 onRender : function(ct, position){
26627 //console.log('row render');
26629 var t = new Roo.Template(
26630 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26631 '<label for="{0}" style="{2}">{1}{4}</label>',
26632 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26636 t.disableFormats = true;
26638 Roo.form.Layout.prototype.rowTpl = t;
26640 this.fieldTpl = this.rowTpl;
26642 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26643 var labelWidth = 100;
26645 if ((this.labelAlign != 'top')) {
26646 if (typeof this.labelWidth == 'number') {
26647 labelWidth = this.labelWidth
26649 this.padWidth = 20 + labelWidth;
26653 Roo.form.Column.superclass.onRender.call(this, ct, position);
26655 this.el.setWidth(this.width);
26658 this.el.setHeight(this.height);
26663 renderField : function(f){
26664 f.fieldEl = this.fieldTpl.append(this.el, [
26665 f.id, f.fieldLabel,
26666 f.labelStyle||this.labelStyle||'',
26667 this.elementStyle||'',
26668 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26669 f.itemCls||this.itemCls||'',
26670 f.width ? f.width + this.padWidth : 160 + this.padWidth
26677 * @class Roo.form.FieldSet
26678 * @extends Roo.form.Layout
26679 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26681 * @param {Object} config Configuration options
26683 Roo.form.FieldSet = function(config){
26684 Roo.form.FieldSet.superclass.constructor.call(this, config);
26687 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26689 * @cfg {String} legend
26690 * The text to display as the legend for the FieldSet (defaults to '')
26693 * @cfg {String/Object} autoCreate
26694 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26698 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26701 onRender : function(ct, position){
26702 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26704 this.setLegend(this.legend);
26709 setLegend : function(text){
26711 this.el.child('legend').update(text);
26716 * Ext JS Library 1.1.1
26717 * Copyright(c) 2006-2007, Ext JS, LLC.
26719 * Originally Released Under LGPL - original licence link has changed is not relivant.
26722 * <script type="text/javascript">
26725 * @class Roo.form.VTypes
26726 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26729 Roo.form.VTypes = function(){
26730 // closure these in so they are only created once.
26731 var alpha = /^[a-zA-Z_]+$/;
26732 var alphanum = /^[a-zA-Z0-9_]+$/;
26733 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26734 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26736 // All these messages and functions are configurable
26739 * The function used to validate email addresses
26740 * @param {String} value The email address
26742 'email' : function(v){
26743 return email.test(v);
26746 * The error text to display when the email validation function returns false
26749 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26751 * The keystroke filter mask to be applied on email input
26754 'emailMask' : /[a-z0-9_\.\-@]/i,
26757 * The function used to validate URLs
26758 * @param {String} value The URL
26760 'url' : function(v){
26761 return url.test(v);
26764 * The error text to display when the url validation function returns false
26767 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26770 * The function used to validate alpha values
26771 * @param {String} value The value
26773 'alpha' : function(v){
26774 return alpha.test(v);
26777 * The error text to display when the alpha validation function returns false
26780 'alphaText' : 'This field should only contain letters and _',
26782 * The keystroke filter mask to be applied on alpha input
26785 'alphaMask' : /[a-z_]/i,
26788 * The function used to validate alphanumeric values
26789 * @param {String} value The value
26791 'alphanum' : function(v){
26792 return alphanum.test(v);
26795 * The error text to display when the alphanumeric validation function returns false
26798 'alphanumText' : 'This field should only contain letters, numbers and _',
26800 * The keystroke filter mask to be applied on alphanumeric input
26803 'alphanumMask' : /[a-z0-9_]/i
26805 }();//<script type="text/javascript">
26808 * @class Roo.form.FCKeditor
26809 * @extends Roo.form.TextArea
26810 * Wrapper around the FCKEditor http://www.fckeditor.net
26812 * Creates a new FCKeditor
26813 * @param {Object} config Configuration options
26815 Roo.form.FCKeditor = function(config){
26816 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26819 * @event editorinit
26820 * Fired when the editor is initialized - you can add extra handlers here..
26821 * @param {FCKeditor} this
26822 * @param {Object} the FCK object.
26829 Roo.form.FCKeditor.editors = { };
26830 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26832 //defaultAutoCreate : {
26833 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26837 * @cfg {Object} fck options - see fck manual for details.
26842 * @cfg {Object} fck toolbar set (Basic or Default)
26844 toolbarSet : 'Basic',
26846 * @cfg {Object} fck BasePath
26848 basePath : '/fckeditor/',
26856 onRender : function(ct, position)
26859 this.defaultAutoCreate = {
26861 style:"width:300px;height:60px;",
26862 autocomplete: "new-password"
26865 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26868 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26869 if(this.preventScrollbars){
26870 this.el.setStyle("overflow", "hidden");
26872 this.el.setHeight(this.growMin);
26875 //console.log('onrender' + this.getId() );
26876 Roo.form.FCKeditor.editors[this.getId()] = this;
26879 this.replaceTextarea() ;
26883 getEditor : function() {
26884 return this.fckEditor;
26887 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26888 * @param {Mixed} value The value to set
26892 setValue : function(value)
26894 //console.log('setValue: ' + value);
26896 if(typeof(value) == 'undefined') { // not sure why this is happending...
26899 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26901 //if(!this.el || !this.getEditor()) {
26902 // this.value = value;
26903 //this.setValue.defer(100,this,[value]);
26907 if(!this.getEditor()) {
26911 this.getEditor().SetData(value);
26918 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26919 * @return {Mixed} value The field value
26921 getValue : function()
26924 if (this.frame && this.frame.dom.style.display == 'none') {
26925 return Roo.form.FCKeditor.superclass.getValue.call(this);
26928 if(!this.el || !this.getEditor()) {
26930 // this.getValue.defer(100,this);
26935 var value=this.getEditor().GetData();
26936 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26937 return Roo.form.FCKeditor.superclass.getValue.call(this);
26943 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26944 * @return {Mixed} value The field value
26946 getRawValue : function()
26948 if (this.frame && this.frame.dom.style.display == 'none') {
26949 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26952 if(!this.el || !this.getEditor()) {
26953 //this.getRawValue.defer(100,this);
26960 var value=this.getEditor().GetData();
26961 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26962 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26966 setSize : function(w,h) {
26970 //if (this.frame && this.frame.dom.style.display == 'none') {
26971 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26974 //if(!this.el || !this.getEditor()) {
26975 // this.setSize.defer(100,this, [w,h]);
26981 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26983 this.frame.dom.setAttribute('width', w);
26984 this.frame.dom.setAttribute('height', h);
26985 this.frame.setSize(w,h);
26989 toggleSourceEdit : function(value) {
26993 this.el.dom.style.display = value ? '' : 'none';
26994 this.frame.dom.style.display = value ? 'none' : '';
26999 focus: function(tag)
27001 if (this.frame.dom.style.display == 'none') {
27002 return Roo.form.FCKeditor.superclass.focus.call(this);
27004 if(!this.el || !this.getEditor()) {
27005 this.focus.defer(100,this, [tag]);
27012 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27013 this.getEditor().Focus();
27015 if (!this.getEditor().Selection.GetSelection()) {
27016 this.focus.defer(100,this, [tag]);
27021 var r = this.getEditor().EditorDocument.createRange();
27022 r.setStart(tgs[0],0);
27023 r.setEnd(tgs[0],0);
27024 this.getEditor().Selection.GetSelection().removeAllRanges();
27025 this.getEditor().Selection.GetSelection().addRange(r);
27026 this.getEditor().Focus();
27033 replaceTextarea : function()
27035 if ( document.getElementById( this.getId() + '___Frame' ) ) {
27038 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27040 // We must check the elements firstly using the Id and then the name.
27041 var oTextarea = document.getElementById( this.getId() );
27043 var colElementsByName = document.getElementsByName( this.getId() ) ;
27045 oTextarea.style.display = 'none' ;
27047 if ( oTextarea.tabIndex ) {
27048 this.TabIndex = oTextarea.tabIndex ;
27051 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27052 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27053 this.frame = Roo.get(this.getId() + '___Frame')
27056 _getConfigHtml : function()
27060 for ( var o in this.fckconfig ) {
27061 sConfig += sConfig.length > 0 ? '&' : '';
27062 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27065 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27069 _getIFrameHtml : function()
27071 var sFile = 'fckeditor.html' ;
27072 /* no idea what this is about..
27075 if ( (/fcksource=true/i).test( window.top.location.search ) )
27076 sFile = 'fckeditor.original.html' ;
27081 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27082 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
27085 var html = '<iframe id="' + this.getId() +
27086 '___Frame" src="' + sLink +
27087 '" width="' + this.width +
27088 '" height="' + this.height + '"' +
27089 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
27090 ' frameborder="0" scrolling="no"></iframe>' ;
27095 _insertHtmlBefore : function( html, element )
27097 if ( element.insertAdjacentHTML ) {
27099 element.insertAdjacentHTML( 'beforeBegin', html ) ;
27101 var oRange = document.createRange() ;
27102 oRange.setStartBefore( element ) ;
27103 var oFragment = oRange.createContextualFragment( html );
27104 element.parentNode.insertBefore( oFragment, element ) ;
27117 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27119 function FCKeditor_OnComplete(editorInstance){
27120 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27121 f.fckEditor = editorInstance;
27122 //console.log("loaded");
27123 f.fireEvent('editorinit', f, editorInstance);
27143 //<script type="text/javascript">
27145 * @class Roo.form.GridField
27146 * @extends Roo.form.Field
27147 * Embed a grid (or editable grid into a form)
27150 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27152 * xgrid.store = Roo.data.Store
27153 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27154 * xgrid.store.reader = Roo.data.JsonReader
27158 * Creates a new GridField
27159 * @param {Object} config Configuration options
27161 Roo.form.GridField = function(config){
27162 Roo.form.GridField.superclass.constructor.call(this, config);
27166 Roo.extend(Roo.form.GridField, Roo.form.Field, {
27168 * @cfg {Number} width - used to restrict width of grid..
27172 * @cfg {Number} height - used to restrict height of grid..
27176 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27182 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27183 * {tag: "input", type: "checkbox", autocomplete: "off"})
27185 // defaultAutoCreate : { tag: 'div' },
27186 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27188 * @cfg {String} addTitle Text to include for adding a title.
27192 onResize : function(){
27193 Roo.form.Field.superclass.onResize.apply(this, arguments);
27196 initEvents : function(){
27197 // Roo.form.Checkbox.superclass.initEvents.call(this);
27198 // has no events...
27203 getResizeEl : function(){
27207 getPositionEl : function(){
27212 onRender : function(ct, position){
27214 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27215 var style = this.style;
27218 Roo.form.GridField.superclass.onRender.call(this, ct, position);
27219 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27220 this.viewEl = this.wrap.createChild({ tag: 'div' });
27222 this.viewEl.applyStyles(style);
27225 this.viewEl.setWidth(this.width);
27228 this.viewEl.setHeight(this.height);
27230 //if(this.inputValue !== undefined){
27231 //this.setValue(this.value);
27234 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27237 this.grid.render();
27238 this.grid.getDataSource().on('remove', this.refreshValue, this);
27239 this.grid.getDataSource().on('update', this.refreshValue, this);
27240 this.grid.on('afteredit', this.refreshValue, this);
27246 * Sets the value of the item.
27247 * @param {String} either an object or a string..
27249 setValue : function(v){
27251 v = v || []; // empty set..
27252 // this does not seem smart - it really only affects memoryproxy grids..
27253 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27254 var ds = this.grid.getDataSource();
27255 // assumes a json reader..
27257 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27258 ds.loadData( data);
27260 // clear selection so it does not get stale.
27261 if (this.grid.sm) {
27262 this.grid.sm.clearSelections();
27265 Roo.form.GridField.superclass.setValue.call(this, v);
27266 this.refreshValue();
27267 // should load data in the grid really....
27271 refreshValue: function() {
27273 this.grid.getDataSource().each(function(r) {
27276 this.el.dom.value = Roo.encode(val);
27284 * Ext JS Library 1.1.1
27285 * Copyright(c) 2006-2007, Ext JS, LLC.
27287 * Originally Released Under LGPL - original licence link has changed is not relivant.
27290 * <script type="text/javascript">
27293 * @class Roo.form.DisplayField
27294 * @extends Roo.form.Field
27295 * A generic Field to display non-editable data.
27296 * @cfg {Boolean} closable (true|false) default false
27298 * Creates a new Display Field item.
27299 * @param {Object} config Configuration options
27301 Roo.form.DisplayField = function(config){
27302 Roo.form.DisplayField.superclass.constructor.call(this, config);
27307 * Fires after the click the close btn
27308 * @param {Roo.form.DisplayField} this
27314 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27315 inputType: 'hidden',
27321 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27323 focusClass : undefined,
27325 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27327 fieldClass: 'x-form-field',
27330 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27332 valueRenderer: undefined,
27336 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27337 * {tag: "input", type: "checkbox", autocomplete: "off"})
27340 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27344 onResize : function(){
27345 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27349 initEvents : function(){
27350 // Roo.form.Checkbox.superclass.initEvents.call(this);
27351 // has no events...
27354 this.closeEl.on('click', this.onClose, this);
27360 getResizeEl : function(){
27364 getPositionEl : function(){
27369 onRender : function(ct, position){
27371 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27372 //if(this.inputValue !== undefined){
27373 this.wrap = this.el.wrap();
27375 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27378 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27381 if (this.bodyStyle) {
27382 this.viewEl.applyStyles(this.bodyStyle);
27384 //this.viewEl.setStyle('padding', '2px');
27386 this.setValue(this.value);
27391 initValue : Roo.emptyFn,
27396 onClick : function(){
27401 * Sets the checked state of the checkbox.
27402 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27404 setValue : function(v){
27406 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27407 // this might be called before we have a dom element..
27408 if (!this.viewEl) {
27411 this.viewEl.dom.innerHTML = html;
27412 Roo.form.DisplayField.superclass.setValue.call(this, v);
27416 onClose : function(e)
27418 e.preventDefault();
27420 this.fireEvent('close', this);
27429 * @class Roo.form.DayPicker
27430 * @extends Roo.form.Field
27431 * A Day picker show [M] [T] [W] ....
27433 * Creates a new Day Picker
27434 * @param {Object} config Configuration options
27436 Roo.form.DayPicker= function(config){
27437 Roo.form.DayPicker.superclass.constructor.call(this, config);
27441 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27443 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27445 focusClass : undefined,
27447 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27449 fieldClass: "x-form-field",
27452 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27453 * {tag: "input", type: "checkbox", autocomplete: "off"})
27455 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27458 actionMode : 'viewEl',
27462 inputType : 'hidden',
27465 inputElement: false, // real input element?
27466 basedOn: false, // ????
27468 isFormField: true, // not sure where this is needed!!!!
27470 onResize : function(){
27471 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27472 if(!this.boxLabel){
27473 this.el.alignTo(this.wrap, 'c-c');
27477 initEvents : function(){
27478 Roo.form.Checkbox.superclass.initEvents.call(this);
27479 this.el.on("click", this.onClick, this);
27480 this.el.on("change", this.onClick, this);
27484 getResizeEl : function(){
27488 getPositionEl : function(){
27494 onRender : function(ct, position){
27495 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27497 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27499 var r1 = '<table><tr>';
27500 var r2 = '<tr class="x-form-daypick-icons">';
27501 for (var i=0; i < 7; i++) {
27502 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27503 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27506 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27507 viewEl.select('img').on('click', this.onClick, this);
27508 this.viewEl = viewEl;
27511 // this will not work on Chrome!!!
27512 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27513 this.el.on('propertychange', this.setFromHidden, this); //ie
27521 initValue : Roo.emptyFn,
27524 * Returns the checked state of the checkbox.
27525 * @return {Boolean} True if checked, else false
27527 getValue : function(){
27528 return this.el.dom.value;
27533 onClick : function(e){
27534 //this.setChecked(!this.checked);
27535 Roo.get(e.target).toggleClass('x-menu-item-checked');
27536 this.refreshValue();
27537 //if(this.el.dom.checked != this.checked){
27538 // this.setValue(this.el.dom.checked);
27543 refreshValue : function()
27546 this.viewEl.select('img',true).each(function(e,i,n) {
27547 val += e.is(".x-menu-item-checked") ? String(n) : '';
27549 this.setValue(val, true);
27553 * Sets the checked state of the checkbox.
27554 * On is always based on a string comparison between inputValue and the param.
27555 * @param {Boolean/String} value - the value to set
27556 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27558 setValue : function(v,suppressEvent){
27559 if (!this.el.dom) {
27562 var old = this.el.dom.value ;
27563 this.el.dom.value = v;
27564 if (suppressEvent) {
27568 // update display..
27569 this.viewEl.select('img',true).each(function(e,i,n) {
27571 var on = e.is(".x-menu-item-checked");
27572 var newv = v.indexOf(String(n)) > -1;
27574 e.toggleClass('x-menu-item-checked');
27580 this.fireEvent('change', this, v, old);
27585 // handle setting of hidden value by some other method!!?!?
27586 setFromHidden: function()
27591 //console.log("SET FROM HIDDEN");
27592 //alert('setFrom hidden');
27593 this.setValue(this.el.dom.value);
27596 onDestroy : function()
27599 Roo.get(this.viewEl).remove();
27602 Roo.form.DayPicker.superclass.onDestroy.call(this);
27606 * RooJS Library 1.1.1
27607 * Copyright(c) 2008-2011 Alan Knowles
27614 * @class Roo.form.ComboCheck
27615 * @extends Roo.form.ComboBox
27616 * A combobox for multiple select items.
27618 * FIXME - could do with a reset button..
27621 * Create a new ComboCheck
27622 * @param {Object} config Configuration options
27624 Roo.form.ComboCheck = function(config){
27625 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27626 // should verify some data...
27628 // hiddenName = required..
27629 // displayField = required
27630 // valudField == required
27631 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27633 Roo.each(req, function(e) {
27634 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27635 throw "Roo.form.ComboCheck : missing value for: " + e;
27642 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27647 selectedClass: 'x-menu-item-checked',
27650 onRender : function(ct, position){
27656 var cls = 'x-combo-list';
27659 this.tpl = new Roo.Template({
27660 html : '<div class="'+cls+'-item x-menu-check-item">' +
27661 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27662 '<span>{' + this.displayField + '}</span>' +
27669 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27670 this.view.singleSelect = false;
27671 this.view.multiSelect = true;
27672 this.view.toggleSelect = true;
27673 this.pageTb.add(new Roo.Toolbar.Fill(), {
27676 handler: function()
27683 onViewOver : function(e, t){
27689 onViewClick : function(doFocus,index){
27693 select: function () {
27694 //Roo.log("SELECT CALLED");
27697 selectByValue : function(xv, scrollIntoView){
27698 var ar = this.getValueArray();
27701 Roo.each(ar, function(v) {
27702 if(v === undefined || v === null){
27705 var r = this.findRecord(this.valueField, v);
27707 sels.push(this.store.indexOf(r))
27711 this.view.select(sels);
27717 onSelect : function(record, index){
27718 // Roo.log("onselect Called");
27719 // this is only called by the clear button now..
27720 this.view.clearSelections();
27721 this.setValue('[]');
27722 if (this.value != this.valueBefore) {
27723 this.fireEvent('change', this, this.value, this.valueBefore);
27724 this.valueBefore = this.value;
27727 getValueArray : function()
27732 //Roo.log(this.value);
27733 if (typeof(this.value) == 'undefined') {
27736 var ar = Roo.decode(this.value);
27737 return ar instanceof Array ? ar : []; //?? valid?
27740 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27745 expand : function ()
27748 Roo.form.ComboCheck.superclass.expand.call(this);
27749 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27750 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27755 collapse : function(){
27756 Roo.form.ComboCheck.superclass.collapse.call(this);
27757 var sl = this.view.getSelectedIndexes();
27758 var st = this.store;
27762 Roo.each(sl, function(i) {
27764 nv.push(r.get(this.valueField));
27766 this.setValue(Roo.encode(nv));
27767 if (this.value != this.valueBefore) {
27769 this.fireEvent('change', this, this.value, this.valueBefore);
27770 this.valueBefore = this.value;
27775 setValue : function(v){
27779 var vals = this.getValueArray();
27781 Roo.each(vals, function(k) {
27782 var r = this.findRecord(this.valueField, k);
27784 tv.push(r.data[this.displayField]);
27785 }else if(this.valueNotFoundText !== undefined){
27786 tv.push( this.valueNotFoundText );
27791 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27792 this.hiddenField.value = v;
27798 * Ext JS Library 1.1.1
27799 * Copyright(c) 2006-2007, Ext JS, LLC.
27801 * Originally Released Under LGPL - original licence link has changed is not relivant.
27804 * <script type="text/javascript">
27808 * @class Roo.form.Signature
27809 * @extends Roo.form.Field
27813 * @param {Object} config Configuration options
27816 Roo.form.Signature = function(config){
27817 Roo.form.Signature.superclass.constructor.call(this, config);
27819 this.addEvents({// not in used??
27822 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27823 * @param {Roo.form.Signature} combo This combo box
27828 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27829 * @param {Roo.form.ComboBox} combo This combo box
27830 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27836 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27838 * @cfg {Object} labels Label to use when rendering a form.
27842 * confirm : "Confirm"
27847 confirm : "Confirm"
27850 * @cfg {Number} width The signature panel width (defaults to 300)
27854 * @cfg {Number} height The signature panel height (defaults to 100)
27858 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27860 allowBlank : false,
27863 // {Object} signPanel The signature SVG panel element (defaults to {})
27865 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27866 isMouseDown : false,
27867 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27868 isConfirmed : false,
27869 // {String} signatureTmp SVG mapping string (defaults to empty string)
27873 defaultAutoCreate : { // modified by initCompnoent..
27879 onRender : function(ct, position){
27881 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27883 this.wrap = this.el.wrap({
27884 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27887 this.createToolbar(this);
27888 this.signPanel = this.wrap.createChild({
27890 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27894 this.svgID = Roo.id();
27895 this.svgEl = this.signPanel.createChild({
27896 xmlns : 'http://www.w3.org/2000/svg',
27898 id : this.svgID + "-svg",
27900 height: this.height,
27901 viewBox: '0 0 '+this.width+' '+this.height,
27905 id: this.svgID + "-svg-r",
27907 height: this.height,
27912 id: this.svgID + "-svg-l",
27914 y1: (this.height*0.8), // start set the line in 80% of height
27915 x2: this.width, // end
27916 y2: (this.height*0.8), // end set the line in 80% of height
27918 'stroke-width': "1",
27919 'stroke-dasharray': "3",
27920 'shape-rendering': "crispEdges",
27921 'pointer-events': "none"
27925 id: this.svgID + "-svg-p",
27927 'stroke-width': "3",
27929 'pointer-events': 'none'
27934 this.svgBox = this.svgEl.dom.getScreenCTM();
27936 createSVG : function(){
27937 var svg = this.signPanel;
27938 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27941 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27942 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27943 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27944 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27945 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27946 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27947 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27950 isTouchEvent : function(e){
27951 return e.type.match(/^touch/);
27953 getCoords : function (e) {
27954 var pt = this.svgEl.dom.createSVGPoint();
27957 if (this.isTouchEvent(e)) {
27958 pt.x = e.targetTouches[0].clientX;
27959 pt.y = e.targetTouches[0].clientY;
27961 var a = this.svgEl.dom.getScreenCTM();
27962 var b = a.inverse();
27963 var mx = pt.matrixTransform(b);
27964 return mx.x + ',' + mx.y;
27966 //mouse event headler
27967 down : function (e) {
27968 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27969 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27971 this.isMouseDown = true;
27973 e.preventDefault();
27975 move : function (e) {
27976 if (this.isMouseDown) {
27977 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27978 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27981 e.preventDefault();
27983 up : function (e) {
27984 this.isMouseDown = false;
27985 var sp = this.signatureTmp.split(' ');
27988 if(!sp[sp.length-2].match(/^L/)){
27992 this.signatureTmp = sp.join(" ");
27995 if(this.getValue() != this.signatureTmp){
27996 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27997 this.isConfirmed = false;
27999 e.preventDefault();
28003 * Protected method that will not generally be called directly. It
28004 * is called when the editor creates its toolbar. Override this method if you need to
28005 * add custom toolbar buttons.
28006 * @param {HtmlEditor} editor
28008 createToolbar : function(editor){
28009 function btn(id, toggle, handler){
28010 var xid = fid + '-'+ id ;
28014 cls : 'x-btn-icon x-edit-'+id,
28015 enableToggle:toggle !== false,
28016 scope: editor, // was editor...
28017 handler:handler||editor.relayBtnCmd,
28018 clickEvent:'mousedown',
28019 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28025 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28029 cls : ' x-signature-btn x-signature-'+id,
28030 scope: editor, // was editor...
28031 handler: this.reset,
28032 clickEvent:'mousedown',
28033 text: this.labels.clear
28040 cls : ' x-signature-btn x-signature-'+id,
28041 scope: editor, // was editor...
28042 handler: this.confirmHandler,
28043 clickEvent:'mousedown',
28044 text: this.labels.confirm
28051 * when user is clicked confirm then show this image.....
28053 * @return {String} Image Data URI
28055 getImageDataURI : function(){
28056 var svg = this.svgEl.dom.parentNode.innerHTML;
28057 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28062 * @return {Boolean} this.isConfirmed
28064 getConfirmed : function(){
28065 return this.isConfirmed;
28069 * @return {Number} this.width
28071 getWidth : function(){
28076 * @return {Number} this.height
28078 getHeight : function(){
28079 return this.height;
28082 getSignature : function(){
28083 return this.signatureTmp;
28086 reset : function(){
28087 this.signatureTmp = '';
28088 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28089 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28090 this.isConfirmed = false;
28091 Roo.form.Signature.superclass.reset.call(this);
28093 setSignature : function(s){
28094 this.signatureTmp = s;
28095 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28096 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28098 this.isConfirmed = false;
28099 Roo.form.Signature.superclass.reset.call(this);
28102 // Roo.log(this.signPanel.dom.contentWindow.up())
28105 setConfirmed : function(){
28109 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28112 confirmHandler : function(){
28113 if(!this.getSignature()){
28117 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28118 this.setValue(this.getSignature());
28119 this.isConfirmed = true;
28121 this.fireEvent('confirm', this);
28124 // Subclasses should provide the validation implementation by overriding this
28125 validateValue : function(value){
28126 if(this.allowBlank){
28130 if(this.isConfirmed){
28137 * Ext JS Library 1.1.1
28138 * Copyright(c) 2006-2007, Ext JS, LLC.
28140 * Originally Released Under LGPL - original licence link has changed is not relivant.
28143 * <script type="text/javascript">
28148 * @class Roo.form.ComboBox
28149 * @extends Roo.form.TriggerField
28150 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28152 * Create a new ComboBox.
28153 * @param {Object} config Configuration options
28155 Roo.form.Select = function(config){
28156 Roo.form.Select.superclass.constructor.call(this, config);
28160 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28162 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28165 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28166 * rendering into an Roo.Editor, defaults to false)
28169 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28170 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28173 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28176 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28177 * the dropdown list (defaults to undefined, with no header element)
28181 * @cfg {String/Roo.Template} tpl The template to use to render the output
28185 defaultAutoCreate : {tag: "select" },
28187 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28189 listWidth: undefined,
28191 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28192 * mode = 'remote' or 'text' if mode = 'local')
28194 displayField: undefined,
28196 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28197 * mode = 'remote' or 'value' if mode = 'local').
28198 * Note: use of a valueField requires the user make a selection
28199 * in order for a value to be mapped.
28201 valueField: undefined,
28205 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28206 * field's data value (defaults to the underlying DOM element's name)
28208 hiddenName: undefined,
28210 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28214 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28216 selectedClass: 'x-combo-selected',
28218 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
28219 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28220 * which displays a downward arrow icon).
28222 triggerClass : 'x-form-arrow-trigger',
28224 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28228 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28229 * anchor positions (defaults to 'tl-bl')
28231 listAlign: 'tl-bl?',
28233 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28237 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
28238 * query specified by the allQuery config option (defaults to 'query')
28240 triggerAction: 'query',
28242 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28243 * (defaults to 4, does not apply if editable = false)
28247 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28248 * delay (typeAheadDelay) if it matches a known value (defaults to false)
28252 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28253 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28257 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28258 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28262 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28263 * when editable = true (defaults to false)
28265 selectOnFocus:false,
28267 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28269 queryParam: 'query',
28271 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28272 * when mode = 'remote' (defaults to 'Loading...')
28274 loadingText: 'Loading...',
28276 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28280 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28284 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28285 * traditional select (defaults to true)
28289 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28293 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28297 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28298 * listWidth has a higher value)
28302 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28303 * allow the user to set arbitrary text into the field (defaults to false)
28305 forceSelection:false,
28307 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28308 * if typeAhead = true (defaults to 250)
28310 typeAheadDelay : 250,
28312 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28313 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28315 valueNotFoundText : undefined,
28318 * @cfg {String} defaultValue The value displayed after loading the store.
28323 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28325 blockFocus : false,
28328 * @cfg {Boolean} disableClear Disable showing of clear button.
28330 disableClear : false,
28332 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28334 alwaysQuery : false,
28340 // element that contains real text value.. (when hidden is used..)
28343 onRender : function(ct, position){
28344 Roo.form.Field.prototype.onRender.call(this, ct, position);
28347 this.store.on('beforeload', this.onBeforeLoad, this);
28348 this.store.on('load', this.onLoad, this);
28349 this.store.on('loadexception', this.onLoadException, this);
28350 this.store.load({});
28358 initEvents : function(){
28359 //Roo.form.ComboBox.superclass.initEvents.call(this);
28363 onDestroy : function(){
28366 this.store.un('beforeload', this.onBeforeLoad, this);
28367 this.store.un('load', this.onLoad, this);
28368 this.store.un('loadexception', this.onLoadException, this);
28370 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28374 fireKey : function(e){
28375 if(e.isNavKeyPress() && !this.list.isVisible()){
28376 this.fireEvent("specialkey", this, e);
28381 onResize: function(w, h){
28389 * Allow or prevent the user from directly editing the field text. If false is passed,
28390 * the user will only be able to select from the items defined in the dropdown list. This method
28391 * is the runtime equivalent of setting the 'editable' config option at config time.
28392 * @param {Boolean} value True to allow the user to directly edit the field text
28394 setEditable : function(value){
28399 onBeforeLoad : function(){
28401 Roo.log("Select before load");
28404 this.innerList.update(this.loadingText ?
28405 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28406 //this.restrictHeight();
28407 this.selectedIndex = -1;
28411 onLoad : function(){
28414 var dom = this.el.dom;
28415 dom.innerHTML = '';
28416 var od = dom.ownerDocument;
28418 if (this.emptyText) {
28419 var op = od.createElement('option');
28420 op.setAttribute('value', '');
28421 op.innerHTML = String.format('{0}', this.emptyText);
28422 dom.appendChild(op);
28424 if(this.store.getCount() > 0){
28426 var vf = this.valueField;
28427 var df = this.displayField;
28428 this.store.data.each(function(r) {
28429 // which colmsn to use... testing - cdoe / title..
28430 var op = od.createElement('option');
28431 op.setAttribute('value', r.data[vf]);
28432 op.innerHTML = String.format('{0}', r.data[df]);
28433 dom.appendChild(op);
28435 if (typeof(this.defaultValue != 'undefined')) {
28436 this.setValue(this.defaultValue);
28441 //this.onEmptyResults();
28446 onLoadException : function()
28448 dom.innerHTML = '';
28450 Roo.log("Select on load exception");
28454 Roo.log(this.store.reader.jsonData);
28455 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28456 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28462 onTypeAhead : function(){
28467 onSelect : function(record, index){
28468 Roo.log('on select?');
28470 if(this.fireEvent('beforeselect', this, record, index) !== false){
28471 this.setFromData(index > -1 ? record.data : false);
28473 this.fireEvent('select', this, record, index);
28478 * Returns the currently selected field value or empty string if no value is set.
28479 * @return {String} value The selected value
28481 getValue : function(){
28482 var dom = this.el.dom;
28483 this.value = dom.options[dom.selectedIndex].value;
28489 * Clears any text/value currently set in the field
28491 clearValue : function(){
28493 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28498 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28499 * will be displayed in the field. If the value does not match the data value of an existing item,
28500 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28501 * Otherwise the field will be blank (although the value will still be set).
28502 * @param {String} value The value to match
28504 setValue : function(v){
28505 var d = this.el.dom;
28506 for (var i =0; i < d.options.length;i++) {
28507 if (v == d.options[i].value) {
28508 d.selectedIndex = i;
28516 * @property {Object} the last set data for the element
28521 * Sets the value of the field based on a object which is related to the record format for the store.
28522 * @param {Object} value the value to set as. or false on reset?
28524 setFromData : function(o){
28525 Roo.log('setfrom data?');
28531 reset : function(){
28535 findRecord : function(prop, value){
28540 if(this.store.getCount() > 0){
28541 this.store.each(function(r){
28542 if(r.data[prop] == value){
28552 getName: function()
28554 // returns hidden if it's set..
28555 if (!this.rendered) {return ''};
28556 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28564 onEmptyResults : function(){
28565 Roo.log('empty results');
28570 * Returns true if the dropdown list is expanded, else false.
28572 isExpanded : function(){
28577 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28578 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28579 * @param {String} value The data value of the item to select
28580 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28581 * selected item if it is not currently in view (defaults to true)
28582 * @return {Boolean} True if the value matched an item in the list, else false
28584 selectByValue : function(v, scrollIntoView){
28585 Roo.log('select By Value');
28588 if(v !== undefined && v !== null){
28589 var r = this.findRecord(this.valueField || this.displayField, v);
28591 this.select(this.store.indexOf(r), scrollIntoView);
28599 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28600 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28601 * @param {Number} index The zero-based index of the list item to select
28602 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28603 * selected item if it is not currently in view (defaults to true)
28605 select : function(index, scrollIntoView){
28606 Roo.log('select ');
28609 this.selectedIndex = index;
28610 this.view.select(index);
28611 if(scrollIntoView !== false){
28612 var el = this.view.getNode(index);
28614 this.innerList.scrollChildIntoView(el, false);
28622 validateBlur : function(){
28629 initQuery : function(){
28630 this.doQuery(this.getRawValue());
28634 doForce : function(){
28635 if(this.el.dom.value.length > 0){
28636 this.el.dom.value =
28637 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28643 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28644 * query allowing the query action to be canceled if needed.
28645 * @param {String} query The SQL query to execute
28646 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28647 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28648 * saved in the current store (defaults to false)
28650 doQuery : function(q, forceAll){
28652 Roo.log('doQuery?');
28653 if(q === undefined || q === null){
28658 forceAll: forceAll,
28662 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28666 forceAll = qe.forceAll;
28667 if(forceAll === true || (q.length >= this.minChars)){
28668 if(this.lastQuery != q || this.alwaysQuery){
28669 this.lastQuery = q;
28670 if(this.mode == 'local'){
28671 this.selectedIndex = -1;
28673 this.store.clearFilter();
28675 this.store.filter(this.displayField, q);
28679 this.store.baseParams[this.queryParam] = q;
28681 params: this.getParams(q)
28686 this.selectedIndex = -1;
28693 getParams : function(q){
28695 //p[this.queryParam] = q;
28698 p.limit = this.pageSize;
28704 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28706 collapse : function(){
28711 collapseIf : function(e){
28716 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28718 expand : function(){
28726 * @cfg {Boolean} grow
28730 * @cfg {Number} growMin
28734 * @cfg {Number} growMax
28742 setWidth : function()
28746 getResizeEl : function(){
28749 });//<script type="text/javasscript">
28753 * @class Roo.DDView
28754 * A DnD enabled version of Roo.View.
28755 * @param {Element/String} container The Element in which to create the View.
28756 * @param {String} tpl The template string used to create the markup for each element of the View
28757 * @param {Object} config The configuration properties. These include all the config options of
28758 * {@link Roo.View} plus some specific to this class.<br>
28760 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28761 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28763 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28764 .x-view-drag-insert-above {
28765 border-top:1px dotted #3366cc;
28767 .x-view-drag-insert-below {
28768 border-bottom:1px dotted #3366cc;
28774 Roo.DDView = function(container, tpl, config) {
28775 Roo.DDView.superclass.constructor.apply(this, arguments);
28776 this.getEl().setStyle("outline", "0px none");
28777 this.getEl().unselectable();
28778 if (this.dragGroup) {
28779 this.setDraggable(this.dragGroup.split(","));
28781 if (this.dropGroup) {
28782 this.setDroppable(this.dropGroup.split(","));
28784 if (this.deletable) {
28785 this.setDeletable();
28787 this.isDirtyFlag = false;
28793 Roo.extend(Roo.DDView, Roo.View, {
28794 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28795 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28796 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28797 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28801 reset: Roo.emptyFn,
28803 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28805 validate: function() {
28809 destroy: function() {
28810 this.purgeListeners();
28811 this.getEl.removeAllListeners();
28812 this.getEl().remove();
28813 if (this.dragZone) {
28814 if (this.dragZone.destroy) {
28815 this.dragZone.destroy();
28818 if (this.dropZone) {
28819 if (this.dropZone.destroy) {
28820 this.dropZone.destroy();
28825 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28826 getName: function() {
28830 /** Loads the View from a JSON string representing the Records to put into the Store. */
28831 setValue: function(v) {
28833 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28836 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28837 this.store.proxy = new Roo.data.MemoryProxy(data);
28841 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28842 getValue: function() {
28844 this.store.each(function(rec) {
28845 result += rec.id + ',';
28847 return result.substr(0, result.length - 1) + ')';
28850 getIds: function() {
28851 var i = 0, result = new Array(this.store.getCount());
28852 this.store.each(function(rec) {
28853 result[i++] = rec.id;
28858 isDirty: function() {
28859 return this.isDirtyFlag;
28863 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28864 * whole Element becomes the target, and this causes the drop gesture to append.
28866 getTargetFromEvent : function(e) {
28867 var target = e.getTarget();
28868 while ((target !== null) && (target.parentNode != this.el.dom)) {
28869 target = target.parentNode;
28872 target = this.el.dom.lastChild || this.el.dom;
28878 * Create the drag data which consists of an object which has the property "ddel" as
28879 * the drag proxy element.
28881 getDragData : function(e) {
28882 var target = this.findItemFromChild(e.getTarget());
28884 this.handleSelection(e);
28885 var selNodes = this.getSelectedNodes();
28888 copy: this.copy || (this.allowCopy && e.ctrlKey),
28892 var selectedIndices = this.getSelectedIndexes();
28893 for (var i = 0; i < selectedIndices.length; i++) {
28894 dragData.records.push(this.store.getAt(selectedIndices[i]));
28896 if (selNodes.length == 1) {
28897 dragData.ddel = target.cloneNode(true); // the div element
28899 var div = document.createElement('div'); // create the multi element drag "ghost"
28900 div.className = 'multi-proxy';
28901 for (var i = 0, len = selNodes.length; i < len; i++) {
28902 div.appendChild(selNodes[i].cloneNode(true));
28904 dragData.ddel = div;
28906 //console.log(dragData)
28907 //console.log(dragData.ddel.innerHTML)
28910 //console.log('nodragData')
28914 /** Specify to which ddGroup items in this DDView may be dragged. */
28915 setDraggable: function(ddGroup) {
28916 if (ddGroup instanceof Array) {
28917 Roo.each(ddGroup, this.setDraggable, this);
28920 if (this.dragZone) {
28921 this.dragZone.addToGroup(ddGroup);
28923 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28924 containerScroll: true,
28928 // Draggability implies selection. DragZone's mousedown selects the element.
28929 if (!this.multiSelect) { this.singleSelect = true; }
28931 // Wire the DragZone's handlers up to methods in *this*
28932 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28936 /** Specify from which ddGroup this DDView accepts drops. */
28937 setDroppable: function(ddGroup) {
28938 if (ddGroup instanceof Array) {
28939 Roo.each(ddGroup, this.setDroppable, this);
28942 if (this.dropZone) {
28943 this.dropZone.addToGroup(ddGroup);
28945 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28946 containerScroll: true,
28950 // Wire the DropZone's handlers up to methods in *this*
28951 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28952 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28953 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28954 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28955 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28959 /** Decide whether to drop above or below a View node. */
28960 getDropPoint : function(e, n, dd){
28961 if (n == this.el.dom) { return "above"; }
28962 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28963 var c = t + (b - t) / 2;
28964 var y = Roo.lib.Event.getPageY(e);
28972 onNodeEnter : function(n, dd, e, data){
28976 onNodeOver : function(n, dd, e, data){
28977 var pt = this.getDropPoint(e, n, dd);
28978 // set the insert point style on the target node
28979 var dragElClass = this.dropNotAllowed;
28982 if (pt == "above"){
28983 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28984 targetElClass = "x-view-drag-insert-above";
28986 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28987 targetElClass = "x-view-drag-insert-below";
28989 if (this.lastInsertClass != targetElClass){
28990 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28991 this.lastInsertClass = targetElClass;
28994 return dragElClass;
28997 onNodeOut : function(n, dd, e, data){
28998 this.removeDropIndicators(n);
29001 onNodeDrop : function(n, dd, e, data){
29002 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29005 var pt = this.getDropPoint(e, n, dd);
29006 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29007 if (pt == "below") { insertAt++; }
29008 for (var i = 0; i < data.records.length; i++) {
29009 var r = data.records[i];
29010 var dup = this.store.getById(r.id);
29011 if (dup && (dd != this.dragZone)) {
29012 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29015 this.store.insert(insertAt++, r.copy());
29017 data.source.isDirtyFlag = true;
29019 this.store.insert(insertAt++, r);
29021 this.isDirtyFlag = true;
29024 this.dragZone.cachedTarget = null;
29028 removeDropIndicators : function(n){
29030 Roo.fly(n).removeClass([
29031 "x-view-drag-insert-above",
29032 "x-view-drag-insert-below"]);
29033 this.lastInsertClass = "_noclass";
29038 * Utility method. Add a delete option to the DDView's context menu.
29039 * @param {String} imageUrl The URL of the "delete" icon image.
29041 setDeletable: function(imageUrl) {
29042 if (!this.singleSelect && !this.multiSelect) {
29043 this.singleSelect = true;
29045 var c = this.getContextMenu();
29046 this.contextMenu.on("itemclick", function(item) {
29049 this.remove(this.getSelectedIndexes());
29053 this.contextMenu.add({
29060 /** Return the context menu for this DDView. */
29061 getContextMenu: function() {
29062 if (!this.contextMenu) {
29063 // Create the View's context menu
29064 this.contextMenu = new Roo.menu.Menu({
29065 id: this.id + "-contextmenu"
29067 this.el.on("contextmenu", this.showContextMenu, this);
29069 return this.contextMenu;
29072 disableContextMenu: function() {
29073 if (this.contextMenu) {
29074 this.el.un("contextmenu", this.showContextMenu, this);
29078 showContextMenu: function(e, item) {
29079 item = this.findItemFromChild(e.getTarget());
29082 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29083 this.contextMenu.showAt(e.getXY());
29088 * Remove {@link Roo.data.Record}s at the specified indices.
29089 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29091 remove: function(selectedIndices) {
29092 selectedIndices = [].concat(selectedIndices);
29093 for (var i = 0; i < selectedIndices.length; i++) {
29094 var rec = this.store.getAt(selectedIndices[i]);
29095 this.store.remove(rec);
29100 * Double click fires the event, but also, if this is draggable, and there is only one other
29101 * related DropZone, it transfers the selected node.
29103 onDblClick : function(e){
29104 var item = this.findItemFromChild(e.getTarget());
29106 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29109 if (this.dragGroup) {
29110 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29111 while (targets.indexOf(this.dropZone) > -1) {
29112 targets.remove(this.dropZone);
29114 if (targets.length == 1) {
29115 this.dragZone.cachedTarget = null;
29116 var el = Roo.get(targets[0].getEl());
29117 var box = el.getBox(true);
29118 targets[0].onNodeDrop(el.dom, {
29120 xy: [box.x, box.y + box.height - 1]
29121 }, null, this.getDragData(e));
29127 handleSelection: function(e) {
29128 this.dragZone.cachedTarget = null;
29129 var item = this.findItemFromChild(e.getTarget());
29131 this.clearSelections(true);
29134 if (item && (this.multiSelect || this.singleSelect)){
29135 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29136 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29137 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29138 this.unselect(item);
29140 this.select(item, this.multiSelect && e.ctrlKey);
29141 this.lastSelection = item;
29146 onItemClick : function(item, index, e){
29147 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29153 unselect : function(nodeInfo, suppressEvent){
29154 var node = this.getNode(nodeInfo);
29155 if(node && this.isSelected(node)){
29156 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29157 Roo.fly(node).removeClass(this.selectedClass);
29158 this.selections.remove(node);
29159 if(!suppressEvent){
29160 this.fireEvent("selectionchange", this, this.selections);
29168 * Ext JS Library 1.1.1
29169 * Copyright(c) 2006-2007, Ext JS, LLC.
29171 * Originally Released Under LGPL - original licence link has changed is not relivant.
29174 * <script type="text/javascript">
29178 * @class Roo.LayoutManager
29179 * @extends Roo.util.Observable
29180 * Base class for layout managers.
29182 Roo.LayoutManager = function(container, config){
29183 Roo.LayoutManager.superclass.constructor.call(this);
29184 this.el = Roo.get(container);
29185 // ie scrollbar fix
29186 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29187 document.body.scroll = "no";
29188 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29189 this.el.position('relative');
29191 this.id = this.el.id;
29192 this.el.addClass("x-layout-container");
29193 /** false to disable window resize monitoring @type Boolean */
29194 this.monitorWindowResize = true;
29199 * Fires when a layout is performed.
29200 * @param {Roo.LayoutManager} this
29204 * @event regionresized
29205 * Fires when the user resizes a region.
29206 * @param {Roo.LayoutRegion} region The resized region
29207 * @param {Number} newSize The new size (width for east/west, height for north/south)
29209 "regionresized" : true,
29211 * @event regioncollapsed
29212 * Fires when a region is collapsed.
29213 * @param {Roo.LayoutRegion} region The collapsed region
29215 "regioncollapsed" : true,
29217 * @event regionexpanded
29218 * Fires when a region is expanded.
29219 * @param {Roo.LayoutRegion} region The expanded region
29221 "regionexpanded" : true
29223 this.updating = false;
29224 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29227 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29229 * Returns true if this layout is currently being updated
29230 * @return {Boolean}
29232 isUpdating : function(){
29233 return this.updating;
29237 * Suspend the LayoutManager from doing auto-layouts while
29238 * making multiple add or remove calls
29240 beginUpdate : function(){
29241 this.updating = true;
29245 * Restore auto-layouts and optionally disable the manager from performing a layout
29246 * @param {Boolean} noLayout true to disable a layout update
29248 endUpdate : function(noLayout){
29249 this.updating = false;
29255 layout: function(){
29259 onRegionResized : function(region, newSize){
29260 this.fireEvent("regionresized", region, newSize);
29264 onRegionCollapsed : function(region){
29265 this.fireEvent("regioncollapsed", region);
29268 onRegionExpanded : function(region){
29269 this.fireEvent("regionexpanded", region);
29273 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29274 * performs box-model adjustments.
29275 * @return {Object} The size as an object {width: (the width), height: (the height)}
29277 getViewSize : function(){
29279 if(this.el.dom != document.body){
29280 size = this.el.getSize();
29282 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29284 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29285 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29290 * Returns the Element this layout is bound to.
29291 * @return {Roo.Element}
29293 getEl : function(){
29298 * Returns the specified region.
29299 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29300 * @return {Roo.LayoutRegion}
29302 getRegion : function(target){
29303 return this.regions[target.toLowerCase()];
29306 onWindowResize : function(){
29307 if(this.monitorWindowResize){
29313 * Ext JS Library 1.1.1
29314 * Copyright(c) 2006-2007, Ext JS, LLC.
29316 * Originally Released Under LGPL - original licence link has changed is not relivant.
29319 * <script type="text/javascript">
29322 * @class Roo.BorderLayout
29323 * @extends Roo.LayoutManager
29324 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29325 * please see: <br><br>
29326 * <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>
29327 * <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>
29330 var layout = new Roo.BorderLayout(document.body, {
29364 preferredTabWidth: 150
29369 var CP = Roo.ContentPanel;
29371 layout.beginUpdate();
29372 layout.add("north", new CP("north", "North"));
29373 layout.add("south", new CP("south", {title: "South", closable: true}));
29374 layout.add("west", new CP("west", {title: "West"}));
29375 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29376 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29377 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29378 layout.getRegion("center").showPanel("center1");
29379 layout.endUpdate();
29382 <b>The container the layout is rendered into can be either the body element or any other element.
29383 If it is not the body element, the container needs to either be an absolute positioned element,
29384 or you will need to add "position:relative" to the css of the container. You will also need to specify
29385 the container size if it is not the body element.</b>
29388 * Create a new BorderLayout
29389 * @param {String/HTMLElement/Element} container The container this layout is bound to
29390 * @param {Object} config Configuration options
29392 Roo.BorderLayout = function(container, config){
29393 config = config || {};
29394 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29395 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29396 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29397 var target = this.factory.validRegions[i];
29398 if(config[target]){
29399 this.addRegion(target, config[target]);
29404 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29406 * Creates and adds a new region if it doesn't already exist.
29407 * @param {String} target The target region key (north, south, east, west or center).
29408 * @param {Object} config The regions config object
29409 * @return {BorderLayoutRegion} The new region
29411 addRegion : function(target, config){
29412 if(!this.regions[target]){
29413 var r = this.factory.create(target, this, config);
29414 this.bindRegion(target, r);
29416 return this.regions[target];
29420 bindRegion : function(name, r){
29421 this.regions[name] = r;
29422 r.on("visibilitychange", this.layout, this);
29423 r.on("paneladded", this.layout, this);
29424 r.on("panelremoved", this.layout, this);
29425 r.on("invalidated", this.layout, this);
29426 r.on("resized", this.onRegionResized, this);
29427 r.on("collapsed", this.onRegionCollapsed, this);
29428 r.on("expanded", this.onRegionExpanded, this);
29432 * Performs a layout update.
29434 layout : function(){
29435 if(this.updating) {
29438 var size = this.getViewSize();
29439 var w = size.width;
29440 var h = size.height;
29445 //var x = 0, y = 0;
29447 var rs = this.regions;
29448 var north = rs["north"];
29449 var south = rs["south"];
29450 var west = rs["west"];
29451 var east = rs["east"];
29452 var center = rs["center"];
29453 //if(this.hideOnLayout){ // not supported anymore
29454 //c.el.setStyle("display", "none");
29456 if(north && north.isVisible()){
29457 var b = north.getBox();
29458 var m = north.getMargins();
29459 b.width = w - (m.left+m.right);
29462 centerY = b.height + b.y + m.bottom;
29463 centerH -= centerY;
29464 north.updateBox(this.safeBox(b));
29466 if(south && south.isVisible()){
29467 var b = south.getBox();
29468 var m = south.getMargins();
29469 b.width = w - (m.left+m.right);
29471 var totalHeight = (b.height + m.top + m.bottom);
29472 b.y = h - totalHeight + m.top;
29473 centerH -= totalHeight;
29474 south.updateBox(this.safeBox(b));
29476 if(west && west.isVisible()){
29477 var b = west.getBox();
29478 var m = west.getMargins();
29479 b.height = centerH - (m.top+m.bottom);
29481 b.y = centerY + m.top;
29482 var totalWidth = (b.width + m.left + m.right);
29483 centerX += totalWidth;
29484 centerW -= totalWidth;
29485 west.updateBox(this.safeBox(b));
29487 if(east && east.isVisible()){
29488 var b = east.getBox();
29489 var m = east.getMargins();
29490 b.height = centerH - (m.top+m.bottom);
29491 var totalWidth = (b.width + m.left + m.right);
29492 b.x = w - totalWidth + m.left;
29493 b.y = centerY + m.top;
29494 centerW -= totalWidth;
29495 east.updateBox(this.safeBox(b));
29498 var m = center.getMargins();
29500 x: centerX + m.left,
29501 y: centerY + m.top,
29502 width: centerW - (m.left+m.right),
29503 height: centerH - (m.top+m.bottom)
29505 //if(this.hideOnLayout){
29506 //center.el.setStyle("display", "block");
29508 center.updateBox(this.safeBox(centerBox));
29511 this.fireEvent("layout", this);
29515 safeBox : function(box){
29516 box.width = Math.max(0, box.width);
29517 box.height = Math.max(0, box.height);
29522 * Adds a ContentPanel (or subclass) to this layout.
29523 * @param {String} target The target region key (north, south, east, west or center).
29524 * @param {Roo.ContentPanel} panel The panel to add
29525 * @return {Roo.ContentPanel} The added panel
29527 add : function(target, panel){
29529 target = target.toLowerCase();
29530 return this.regions[target].add(panel);
29534 * Remove a ContentPanel (or subclass) to this layout.
29535 * @param {String} target The target region key (north, south, east, west or center).
29536 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29537 * @return {Roo.ContentPanel} The removed panel
29539 remove : function(target, panel){
29540 target = target.toLowerCase();
29541 return this.regions[target].remove(panel);
29545 * Searches all regions for a panel with the specified id
29546 * @param {String} panelId
29547 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29549 findPanel : function(panelId){
29550 var rs = this.regions;
29551 for(var target in rs){
29552 if(typeof rs[target] != "function"){
29553 var p = rs[target].getPanel(panelId);
29563 * Searches all regions for a panel with the specified id and activates (shows) it.
29564 * @param {String/ContentPanel} panelId The panels id or the panel itself
29565 * @return {Roo.ContentPanel} The shown panel or null
29567 showPanel : function(panelId) {
29568 var rs = this.regions;
29569 for(var target in rs){
29570 var r = rs[target];
29571 if(typeof r != "function"){
29572 if(r.hasPanel(panelId)){
29573 return r.showPanel(panelId);
29581 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29582 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29584 restoreState : function(provider){
29586 provider = Roo.state.Manager;
29588 var sm = new Roo.LayoutStateManager();
29589 sm.init(this, provider);
29593 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29594 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29595 * a valid ContentPanel config object. Example:
29597 // Create the main layout
29598 var layout = new Roo.BorderLayout('main-ct', {
29609 // Create and add multiple ContentPanels at once via configs
29612 id: 'source-files',
29614 title:'Ext Source Files',
29627 * @param {Object} regions An object containing ContentPanel configs by region name
29629 batchAdd : function(regions){
29630 this.beginUpdate();
29631 for(var rname in regions){
29632 var lr = this.regions[rname];
29634 this.addTypedPanels(lr, regions[rname]);
29641 addTypedPanels : function(lr, ps){
29642 if(typeof ps == 'string'){
29643 lr.add(new Roo.ContentPanel(ps));
29645 else if(ps instanceof Array){
29646 for(var i =0, len = ps.length; i < len; i++){
29647 this.addTypedPanels(lr, ps[i]);
29650 else if(!ps.events){ // raw config?
29652 delete ps.el; // prevent conflict
29653 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29655 else { // panel object assumed!
29660 * Adds a xtype elements to the layout.
29664 xtype : 'ContentPanel',
29671 xtype : 'NestedLayoutPanel',
29677 items : [ ... list of content panels or nested layout panels.. ]
29681 * @param {Object} cfg Xtype definition of item to add.
29683 addxtype : function(cfg)
29685 // basically accepts a pannel...
29686 // can accept a layout region..!?!?
29687 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29689 if (!cfg.xtype.match(/Panel$/)) {
29694 if (typeof(cfg.region) == 'undefined') {
29695 Roo.log("Failed to add Panel, region was not set");
29699 var region = cfg.region;
29705 xitems = cfg.items;
29712 case 'ContentPanel': // ContentPanel (el, cfg)
29713 case 'ScrollPanel': // ContentPanel (el, cfg)
29715 if(cfg.autoCreate) {
29716 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29718 var el = this.el.createChild();
29719 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29722 this.add(region, ret);
29726 case 'TreePanel': // our new panel!
29727 cfg.el = this.el.createChild();
29728 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29729 this.add(region, ret);
29732 case 'NestedLayoutPanel':
29733 // create a new Layout (which is a Border Layout...
29734 var el = this.el.createChild();
29735 var clayout = cfg.layout;
29737 clayout.items = clayout.items || [];
29738 // replace this exitems with the clayout ones..
29739 xitems = clayout.items;
29742 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29743 cfg.background = false;
29745 var layout = new Roo.BorderLayout(el, clayout);
29747 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29748 //console.log('adding nested layout panel ' + cfg.toSource());
29749 this.add(region, ret);
29750 nb = {}; /// find first...
29755 // needs grid and region
29757 //var el = this.getRegion(region).el.createChild();
29758 var el = this.el.createChild();
29759 // create the grid first...
29761 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29763 if (region == 'center' && this.active ) {
29764 cfg.background = false;
29766 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29768 this.add(region, ret);
29769 if (cfg.background) {
29770 ret.on('activate', function(gp) {
29771 if (!gp.grid.rendered) {
29786 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29788 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29789 this.add(region, ret);
29792 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29796 // GridPanel (grid, cfg)
29799 this.beginUpdate();
29803 Roo.each(xitems, function(i) {
29804 region = nb && i.region ? i.region : false;
29806 var add = ret.addxtype(i);
29809 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29810 if (!i.background) {
29811 abn[region] = nb[region] ;
29818 // make the last non-background panel active..
29819 //if (nb) { Roo.log(abn); }
29822 for(var r in abn) {
29823 region = this.getRegion(r);
29825 // tried using nb[r], but it does not work..
29827 region.showPanel(abn[r]);
29838 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29839 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29840 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29841 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29844 var CP = Roo.ContentPanel;
29846 var layout = Roo.BorderLayout.create({
29850 panels: [new CP("north", "North")]
29859 panels: [new CP("west", {title: "West"})]
29868 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29877 panels: [new CP("south", {title: "South", closable: true})]
29884 preferredTabWidth: 150,
29886 new CP("center1", {title: "Close Me", closable: true}),
29887 new CP("center2", {title: "Center Panel", closable: false})
29892 layout.getRegion("center").showPanel("center1");
29897 Roo.BorderLayout.create = function(config, targetEl){
29898 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29899 layout.beginUpdate();
29900 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29901 for(var j = 0, jlen = regions.length; j < jlen; j++){
29902 var lr = regions[j];
29903 if(layout.regions[lr] && config[lr].panels){
29904 var r = layout.regions[lr];
29905 var ps = config[lr].panels;
29906 layout.addTypedPanels(r, ps);
29909 layout.endUpdate();
29914 Roo.BorderLayout.RegionFactory = {
29916 validRegions : ["north","south","east","west","center"],
29919 create : function(target, mgr, config){
29920 target = target.toLowerCase();
29921 if(config.lightweight || config.basic){
29922 return new Roo.BasicLayoutRegion(mgr, config, target);
29926 return new Roo.NorthLayoutRegion(mgr, config);
29928 return new Roo.SouthLayoutRegion(mgr, config);
29930 return new Roo.EastLayoutRegion(mgr, config);
29932 return new Roo.WestLayoutRegion(mgr, config);
29934 return new Roo.CenterLayoutRegion(mgr, config);
29936 throw 'Layout region "'+target+'" not supported.';
29940 * Ext JS Library 1.1.1
29941 * Copyright(c) 2006-2007, Ext JS, LLC.
29943 * Originally Released Under LGPL - original licence link has changed is not relivant.
29946 * <script type="text/javascript">
29950 * @class Roo.BasicLayoutRegion
29951 * @extends Roo.util.Observable
29952 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29953 * and does not have a titlebar, tabs or any other features. All it does is size and position
29954 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29956 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29958 this.position = pos;
29961 * @scope Roo.BasicLayoutRegion
29965 * @event beforeremove
29966 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29967 * @param {Roo.LayoutRegion} this
29968 * @param {Roo.ContentPanel} panel The panel
29969 * @param {Object} e The cancel event object
29971 "beforeremove" : true,
29973 * @event invalidated
29974 * Fires when the layout for this region is changed.
29975 * @param {Roo.LayoutRegion} this
29977 "invalidated" : true,
29979 * @event visibilitychange
29980 * Fires when this region is shown or hidden
29981 * @param {Roo.LayoutRegion} this
29982 * @param {Boolean} visibility true or false
29984 "visibilitychange" : true,
29986 * @event paneladded
29987 * Fires when a panel is added.
29988 * @param {Roo.LayoutRegion} this
29989 * @param {Roo.ContentPanel} panel The panel
29991 "paneladded" : true,
29993 * @event panelremoved
29994 * Fires when a panel is removed.
29995 * @param {Roo.LayoutRegion} this
29996 * @param {Roo.ContentPanel} panel The panel
29998 "panelremoved" : true,
30000 * @event beforecollapse
30001 * Fires when this region before collapse.
30002 * @param {Roo.LayoutRegion} this
30004 "beforecollapse" : true,
30007 * Fires when this region is collapsed.
30008 * @param {Roo.LayoutRegion} this
30010 "collapsed" : true,
30013 * Fires when this region is expanded.
30014 * @param {Roo.LayoutRegion} this
30019 * Fires when this region is slid into view.
30020 * @param {Roo.LayoutRegion} this
30022 "slideshow" : true,
30025 * Fires when this region slides out of view.
30026 * @param {Roo.LayoutRegion} this
30028 "slidehide" : true,
30030 * @event panelactivated
30031 * Fires when a panel is activated.
30032 * @param {Roo.LayoutRegion} this
30033 * @param {Roo.ContentPanel} panel The activated panel
30035 "panelactivated" : true,
30038 * Fires when the user resizes this region.
30039 * @param {Roo.LayoutRegion} this
30040 * @param {Number} newSize The new size (width for east/west, height for north/south)
30044 /** A collection of panels in this region. @type Roo.util.MixedCollection */
30045 this.panels = new Roo.util.MixedCollection();
30046 this.panels.getKey = this.getPanelId.createDelegate(this);
30048 this.activePanel = null;
30049 // ensure listeners are added...
30051 if (config.listeners || config.events) {
30052 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30053 listeners : config.listeners || {},
30054 events : config.events || {}
30058 if(skipConfig !== true){
30059 this.applyConfig(config);
30063 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30064 getPanelId : function(p){
30068 applyConfig : function(config){
30069 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30070 this.config = config;
30075 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
30076 * the width, for horizontal (north, south) the height.
30077 * @param {Number} newSize The new width or height
30079 resizeTo : function(newSize){
30080 var el = this.el ? this.el :
30081 (this.activePanel ? this.activePanel.getEl() : null);
30083 switch(this.position){
30086 el.setWidth(newSize);
30087 this.fireEvent("resized", this, newSize);
30091 el.setHeight(newSize);
30092 this.fireEvent("resized", this, newSize);
30098 getBox : function(){
30099 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30102 getMargins : function(){
30103 return this.margins;
30106 updateBox : function(box){
30108 var el = this.activePanel.getEl();
30109 el.dom.style.left = box.x + "px";
30110 el.dom.style.top = box.y + "px";
30111 this.activePanel.setSize(box.width, box.height);
30115 * Returns the container element for this region.
30116 * @return {Roo.Element}
30118 getEl : function(){
30119 return this.activePanel;
30123 * Returns true if this region is currently visible.
30124 * @return {Boolean}
30126 isVisible : function(){
30127 return this.activePanel ? true : false;
30130 setActivePanel : function(panel){
30131 panel = this.getPanel(panel);
30132 if(this.activePanel && this.activePanel != panel){
30133 this.activePanel.setActiveState(false);
30134 this.activePanel.getEl().setLeftTop(-10000,-10000);
30136 this.activePanel = panel;
30137 panel.setActiveState(true);
30139 panel.setSize(this.box.width, this.box.height);
30141 this.fireEvent("panelactivated", this, panel);
30142 this.fireEvent("invalidated");
30146 * Show the specified panel.
30147 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30148 * @return {Roo.ContentPanel} The shown panel or null
30150 showPanel : function(panel){
30151 if(panel = this.getPanel(panel)){
30152 this.setActivePanel(panel);
30158 * Get the active panel for this region.
30159 * @return {Roo.ContentPanel} The active panel or null
30161 getActivePanel : function(){
30162 return this.activePanel;
30166 * Add the passed ContentPanel(s)
30167 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30168 * @return {Roo.ContentPanel} The panel added (if only one was added)
30170 add : function(panel){
30171 if(arguments.length > 1){
30172 for(var i = 0, len = arguments.length; i < len; i++) {
30173 this.add(arguments[i]);
30177 if(this.hasPanel(panel)){
30178 this.showPanel(panel);
30181 var el = panel.getEl();
30182 if(el.dom.parentNode != this.mgr.el.dom){
30183 this.mgr.el.dom.appendChild(el.dom);
30185 if(panel.setRegion){
30186 panel.setRegion(this);
30188 this.panels.add(panel);
30189 el.setStyle("position", "absolute");
30190 if(!panel.background){
30191 this.setActivePanel(panel);
30192 if(this.config.initialSize && this.panels.getCount()==1){
30193 this.resizeTo(this.config.initialSize);
30196 this.fireEvent("paneladded", this, panel);
30201 * Returns true if the panel is in this region.
30202 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30203 * @return {Boolean}
30205 hasPanel : function(panel){
30206 if(typeof panel == "object"){ // must be panel obj
30207 panel = panel.getId();
30209 return this.getPanel(panel) ? true : false;
30213 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30214 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30215 * @param {Boolean} preservePanel Overrides the config preservePanel option
30216 * @return {Roo.ContentPanel} The panel that was removed
30218 remove : function(panel, preservePanel){
30219 panel = this.getPanel(panel);
30224 this.fireEvent("beforeremove", this, panel, e);
30225 if(e.cancel === true){
30228 var panelId = panel.getId();
30229 this.panels.removeKey(panelId);
30234 * Returns the panel specified or null if it's not in this region.
30235 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30236 * @return {Roo.ContentPanel}
30238 getPanel : function(id){
30239 if(typeof id == "object"){ // must be panel obj
30242 return this.panels.get(id);
30246 * Returns this regions position (north/south/east/west/center).
30249 getPosition: function(){
30250 return this.position;
30254 * Ext JS Library 1.1.1
30255 * Copyright(c) 2006-2007, Ext JS, LLC.
30257 * Originally Released Under LGPL - original licence link has changed is not relivant.
30260 * <script type="text/javascript">
30264 * @class Roo.LayoutRegion
30265 * @extends Roo.BasicLayoutRegion
30266 * This class represents a region in a layout manager.
30267 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30268 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30269 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30270 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30271 * @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})
30272 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30273 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30274 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30275 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30276 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30277 * @cfg {String} title The title for the region (overrides panel titles)
30278 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30279 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30280 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30281 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30282 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30283 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30284 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30285 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30286 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30287 * @cfg {Boolean} showPin True to show a pin button
30288 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30289 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30290 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30291 * @cfg {Number} width For East/West panels
30292 * @cfg {Number} height For North/South panels
30293 * @cfg {Boolean} split To show the splitter
30294 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30296 Roo.LayoutRegion = function(mgr, config, pos){
30297 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30298 var dh = Roo.DomHelper;
30299 /** This region's container element
30300 * @type Roo.Element */
30301 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30302 /** This region's title element
30303 * @type Roo.Element */
30305 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30306 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30307 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30309 this.titleEl.enableDisplayMode();
30310 /** This region's title text element
30311 * @type HTMLElement */
30312 this.titleTextEl = this.titleEl.dom.firstChild;
30313 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30314 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30315 this.closeBtn.enableDisplayMode();
30316 this.closeBtn.on("click", this.closeClicked, this);
30317 this.closeBtn.hide();
30319 this.createBody(config);
30320 this.visible = true;
30321 this.collapsed = false;
30323 if(config.hideWhenEmpty){
30325 this.on("paneladded", this.validateVisibility, this);
30326 this.on("panelremoved", this.validateVisibility, this);
30328 this.applyConfig(config);
30331 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30333 createBody : function(){
30334 /** This region's body element
30335 * @type Roo.Element */
30336 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30339 applyConfig : function(c){
30340 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30341 var dh = Roo.DomHelper;
30342 if(c.titlebar !== false){
30343 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30344 this.collapseBtn.on("click", this.collapse, this);
30345 this.collapseBtn.enableDisplayMode();
30347 if(c.showPin === true || this.showPin){
30348 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30349 this.stickBtn.enableDisplayMode();
30350 this.stickBtn.on("click", this.expand, this);
30351 this.stickBtn.hide();
30354 /** This region's collapsed element
30355 * @type Roo.Element */
30356 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30357 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30359 if(c.floatable !== false){
30360 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30361 this.collapsedEl.on("click", this.collapseClick, this);
30364 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30365 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30366 id: "message", unselectable: "on", style:{"float":"left"}});
30367 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30369 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30370 this.expandBtn.on("click", this.expand, this);
30372 if(this.collapseBtn){
30373 this.collapseBtn.setVisible(c.collapsible == true);
30375 this.cmargins = c.cmargins || this.cmargins ||
30376 (this.position == "west" || this.position == "east" ?
30377 {top: 0, left: 2, right:2, bottom: 0} :
30378 {top: 2, left: 0, right:0, bottom: 2});
30379 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30380 this.bottomTabs = c.tabPosition != "top";
30381 this.autoScroll = c.autoScroll || false;
30382 if(this.autoScroll){
30383 this.bodyEl.setStyle("overflow", "auto");
30385 this.bodyEl.setStyle("overflow", "hidden");
30387 //if(c.titlebar !== false){
30388 if((!c.titlebar && !c.title) || c.titlebar === false){
30389 this.titleEl.hide();
30391 this.titleEl.show();
30393 this.titleTextEl.innerHTML = c.title;
30397 this.duration = c.duration || .30;
30398 this.slideDuration = c.slideDuration || .45;
30401 this.collapse(true);
30408 * Returns true if this region is currently visible.
30409 * @return {Boolean}
30411 isVisible : function(){
30412 return this.visible;
30416 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30417 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30419 setCollapsedTitle : function(title){
30420 title = title || " ";
30421 if(this.collapsedTitleTextEl){
30422 this.collapsedTitleTextEl.innerHTML = title;
30426 getBox : function(){
30428 if(!this.collapsed){
30429 b = this.el.getBox(false, true);
30431 b = this.collapsedEl.getBox(false, true);
30436 getMargins : function(){
30437 return this.collapsed ? this.cmargins : this.margins;
30440 highlight : function(){
30441 this.el.addClass("x-layout-panel-dragover");
30444 unhighlight : function(){
30445 this.el.removeClass("x-layout-panel-dragover");
30448 updateBox : function(box){
30450 if(!this.collapsed){
30451 this.el.dom.style.left = box.x + "px";
30452 this.el.dom.style.top = box.y + "px";
30453 this.updateBody(box.width, box.height);
30455 this.collapsedEl.dom.style.left = box.x + "px";
30456 this.collapsedEl.dom.style.top = box.y + "px";
30457 this.collapsedEl.setSize(box.width, box.height);
30460 this.tabs.autoSizeTabs();
30464 updateBody : function(w, h){
30466 this.el.setWidth(w);
30467 w -= this.el.getBorderWidth("rl");
30468 if(this.config.adjustments){
30469 w += this.config.adjustments[0];
30473 this.el.setHeight(h);
30474 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30475 h -= this.el.getBorderWidth("tb");
30476 if(this.config.adjustments){
30477 h += this.config.adjustments[1];
30479 this.bodyEl.setHeight(h);
30481 h = this.tabs.syncHeight(h);
30484 if(this.panelSize){
30485 w = w !== null ? w : this.panelSize.width;
30486 h = h !== null ? h : this.panelSize.height;
30488 if(this.activePanel){
30489 var el = this.activePanel.getEl();
30490 w = w !== null ? w : el.getWidth();
30491 h = h !== null ? h : el.getHeight();
30492 this.panelSize = {width: w, height: h};
30493 this.activePanel.setSize(w, h);
30495 if(Roo.isIE && this.tabs){
30496 this.tabs.el.repaint();
30501 * Returns the container element for this region.
30502 * @return {Roo.Element}
30504 getEl : function(){
30509 * Hides this region.
30512 if(!this.collapsed){
30513 this.el.dom.style.left = "-2000px";
30516 this.collapsedEl.dom.style.left = "-2000px";
30517 this.collapsedEl.hide();
30519 this.visible = false;
30520 this.fireEvent("visibilitychange", this, false);
30524 * Shows this region if it was previously hidden.
30527 if(!this.collapsed){
30530 this.collapsedEl.show();
30532 this.visible = true;
30533 this.fireEvent("visibilitychange", this, true);
30536 closeClicked : function(){
30537 if(this.activePanel){
30538 this.remove(this.activePanel);
30542 collapseClick : function(e){
30544 e.stopPropagation();
30547 e.stopPropagation();
30553 * Collapses this region.
30554 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30556 collapse : function(skipAnim, skipCheck){
30557 if(this.collapsed) {
30561 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30563 this.collapsed = true;
30565 this.split.el.hide();
30567 if(this.config.animate && skipAnim !== true){
30568 this.fireEvent("invalidated", this);
30569 this.animateCollapse();
30571 this.el.setLocation(-20000,-20000);
30573 this.collapsedEl.show();
30574 this.fireEvent("collapsed", this);
30575 this.fireEvent("invalidated", this);
30581 animateCollapse : function(){
30586 * Expands this region if it was previously collapsed.
30587 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30588 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30590 expand : function(e, skipAnim){
30592 e.stopPropagation();
30594 if(!this.collapsed || this.el.hasActiveFx()) {
30598 this.afterSlideIn();
30601 this.collapsed = false;
30602 if(this.config.animate && skipAnim !== true){
30603 this.animateExpand();
30607 this.split.el.show();
30609 this.collapsedEl.setLocation(-2000,-2000);
30610 this.collapsedEl.hide();
30611 this.fireEvent("invalidated", this);
30612 this.fireEvent("expanded", this);
30616 animateExpand : function(){
30620 initTabs : function()
30622 this.bodyEl.setStyle("overflow", "hidden");
30623 var ts = new Roo.TabPanel(
30626 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30627 disableTooltips: this.config.disableTabTips,
30628 toolbar : this.config.toolbar
30631 if(this.config.hideTabs){
30632 ts.stripWrap.setDisplayed(false);
30635 ts.resizeTabs = this.config.resizeTabs === true;
30636 ts.minTabWidth = this.config.minTabWidth || 40;
30637 ts.maxTabWidth = this.config.maxTabWidth || 250;
30638 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30639 ts.monitorResize = false;
30640 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30641 ts.bodyEl.addClass('x-layout-tabs-body');
30642 this.panels.each(this.initPanelAsTab, this);
30645 initPanelAsTab : function(panel){
30646 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30647 this.config.closeOnTab && panel.isClosable());
30648 if(panel.tabTip !== undefined){
30649 ti.setTooltip(panel.tabTip);
30651 ti.on("activate", function(){
30652 this.setActivePanel(panel);
30654 if(this.config.closeOnTab){
30655 ti.on("beforeclose", function(t, e){
30657 this.remove(panel);
30663 updatePanelTitle : function(panel, title){
30664 if(this.activePanel == panel){
30665 this.updateTitle(title);
30668 var ti = this.tabs.getTab(panel.getEl().id);
30670 if(panel.tabTip !== undefined){
30671 ti.setTooltip(panel.tabTip);
30676 updateTitle : function(title){
30677 if(this.titleTextEl && !this.config.title){
30678 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30682 setActivePanel : function(panel){
30683 panel = this.getPanel(panel);
30684 if(this.activePanel && this.activePanel != panel){
30685 this.activePanel.setActiveState(false);
30687 this.activePanel = panel;
30688 panel.setActiveState(true);
30689 if(this.panelSize){
30690 panel.setSize(this.panelSize.width, this.panelSize.height);
30693 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30695 this.updateTitle(panel.getTitle());
30697 this.fireEvent("invalidated", this);
30699 this.fireEvent("panelactivated", this, panel);
30703 * Shows the specified panel.
30704 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30705 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30707 showPanel : function(panel)
30709 panel = this.getPanel(panel);
30712 var tab = this.tabs.getTab(panel.getEl().id);
30713 if(tab.isHidden()){
30714 this.tabs.unhideTab(tab.id);
30718 this.setActivePanel(panel);
30725 * Get the active panel for this region.
30726 * @return {Roo.ContentPanel} The active panel or null
30728 getActivePanel : function(){
30729 return this.activePanel;
30732 validateVisibility : function(){
30733 if(this.panels.getCount() < 1){
30734 this.updateTitle(" ");
30735 this.closeBtn.hide();
30738 if(!this.isVisible()){
30745 * Adds the passed ContentPanel(s) to this region.
30746 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30747 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30749 add : function(panel){
30750 if(arguments.length > 1){
30751 for(var i = 0, len = arguments.length; i < len; i++) {
30752 this.add(arguments[i]);
30756 if(this.hasPanel(panel)){
30757 this.showPanel(panel);
30760 panel.setRegion(this);
30761 this.panels.add(panel);
30762 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30763 this.bodyEl.dom.appendChild(panel.getEl().dom);
30764 if(panel.background !== true){
30765 this.setActivePanel(panel);
30767 this.fireEvent("paneladded", this, panel);
30773 this.initPanelAsTab(panel);
30775 if(panel.background !== true){
30776 this.tabs.activate(panel.getEl().id);
30778 this.fireEvent("paneladded", this, panel);
30783 * Hides the tab for the specified panel.
30784 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30786 hidePanel : function(panel){
30787 if(this.tabs && (panel = this.getPanel(panel))){
30788 this.tabs.hideTab(panel.getEl().id);
30793 * Unhides the tab for a previously hidden panel.
30794 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30796 unhidePanel : function(panel){
30797 if(this.tabs && (panel = this.getPanel(panel))){
30798 this.tabs.unhideTab(panel.getEl().id);
30802 clearPanels : function(){
30803 while(this.panels.getCount() > 0){
30804 this.remove(this.panels.first());
30809 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30810 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30811 * @param {Boolean} preservePanel Overrides the config preservePanel option
30812 * @return {Roo.ContentPanel} The panel that was removed
30814 remove : function(panel, preservePanel){
30815 panel = this.getPanel(panel);
30820 this.fireEvent("beforeremove", this, panel, e);
30821 if(e.cancel === true){
30824 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30825 var panelId = panel.getId();
30826 this.panels.removeKey(panelId);
30828 document.body.appendChild(panel.getEl().dom);
30831 this.tabs.removeTab(panel.getEl().id);
30832 }else if (!preservePanel){
30833 this.bodyEl.dom.removeChild(panel.getEl().dom);
30835 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30836 var p = this.panels.first();
30837 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30838 tempEl.appendChild(p.getEl().dom);
30839 this.bodyEl.update("");
30840 this.bodyEl.dom.appendChild(p.getEl().dom);
30842 this.updateTitle(p.getTitle());
30844 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30845 this.setActivePanel(p);
30847 panel.setRegion(null);
30848 if(this.activePanel == panel){
30849 this.activePanel = null;
30851 if(this.config.autoDestroy !== false && preservePanel !== true){
30852 try{panel.destroy();}catch(e){}
30854 this.fireEvent("panelremoved", this, panel);
30859 * Returns the TabPanel component used by this region
30860 * @return {Roo.TabPanel}
30862 getTabs : function(){
30866 createTool : function(parentEl, className){
30867 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30868 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30869 btn.addClassOnOver("x-layout-tools-button-over");
30874 * Ext JS Library 1.1.1
30875 * Copyright(c) 2006-2007, Ext JS, LLC.
30877 * Originally Released Under LGPL - original licence link has changed is not relivant.
30880 * <script type="text/javascript">
30886 * @class Roo.SplitLayoutRegion
30887 * @extends Roo.LayoutRegion
30888 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30890 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30891 this.cursor = cursor;
30892 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30895 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30896 splitTip : "Drag to resize.",
30897 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30898 useSplitTips : false,
30900 applyConfig : function(config){
30901 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30904 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30905 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30906 /** The SplitBar for this region
30907 * @type Roo.SplitBar */
30908 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30909 this.split.on("moved", this.onSplitMove, this);
30910 this.split.useShim = config.useShim === true;
30911 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30912 if(this.useSplitTips){
30913 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30915 if(config.collapsible){
30916 this.split.el.on("dblclick", this.collapse, this);
30919 if(typeof config.minSize != "undefined"){
30920 this.split.minSize = config.minSize;
30922 if(typeof config.maxSize != "undefined"){
30923 this.split.maxSize = config.maxSize;
30925 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30926 this.hideSplitter();
30931 getHMaxSize : function(){
30932 var cmax = this.config.maxSize || 10000;
30933 var center = this.mgr.getRegion("center");
30934 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30937 getVMaxSize : function(){
30938 var cmax = this.config.maxSize || 10000;
30939 var center = this.mgr.getRegion("center");
30940 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30943 onSplitMove : function(split, newSize){
30944 this.fireEvent("resized", this, newSize);
30948 * Returns the {@link Roo.SplitBar} for this region.
30949 * @return {Roo.SplitBar}
30951 getSplitBar : function(){
30956 this.hideSplitter();
30957 Roo.SplitLayoutRegion.superclass.hide.call(this);
30960 hideSplitter : function(){
30962 this.split.el.setLocation(-2000,-2000);
30963 this.split.el.hide();
30969 this.split.el.show();
30971 Roo.SplitLayoutRegion.superclass.show.call(this);
30974 beforeSlide: function(){
30975 if(Roo.isGecko){// firefox overflow auto bug workaround
30976 this.bodyEl.clip();
30978 this.tabs.bodyEl.clip();
30980 if(this.activePanel){
30981 this.activePanel.getEl().clip();
30983 if(this.activePanel.beforeSlide){
30984 this.activePanel.beforeSlide();
30990 afterSlide : function(){
30991 if(Roo.isGecko){// firefox overflow auto bug workaround
30992 this.bodyEl.unclip();
30994 this.tabs.bodyEl.unclip();
30996 if(this.activePanel){
30997 this.activePanel.getEl().unclip();
30998 if(this.activePanel.afterSlide){
30999 this.activePanel.afterSlide();
31005 initAutoHide : function(){
31006 if(this.autoHide !== false){
31007 if(!this.autoHideHd){
31008 var st = new Roo.util.DelayedTask(this.slideIn, this);
31009 this.autoHideHd = {
31010 "mouseout": function(e){
31011 if(!e.within(this.el, true)){
31015 "mouseover" : function(e){
31021 this.el.on(this.autoHideHd);
31025 clearAutoHide : function(){
31026 if(this.autoHide !== false){
31027 this.el.un("mouseout", this.autoHideHd.mouseout);
31028 this.el.un("mouseover", this.autoHideHd.mouseover);
31032 clearMonitor : function(){
31033 Roo.get(document).un("click", this.slideInIf, this);
31036 // these names are backwards but not changed for compat
31037 slideOut : function(){
31038 if(this.isSlid || this.el.hasActiveFx()){
31041 this.isSlid = true;
31042 if(this.collapseBtn){
31043 this.collapseBtn.hide();
31045 this.closeBtnState = this.closeBtn.getStyle('display');
31046 this.closeBtn.hide();
31048 this.stickBtn.show();
31051 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31052 this.beforeSlide();
31053 this.el.setStyle("z-index", 10001);
31054 this.el.slideIn(this.getSlideAnchor(), {
31055 callback: function(){
31057 this.initAutoHide();
31058 Roo.get(document).on("click", this.slideInIf, this);
31059 this.fireEvent("slideshow", this);
31066 afterSlideIn : function(){
31067 this.clearAutoHide();
31068 this.isSlid = false;
31069 this.clearMonitor();
31070 this.el.setStyle("z-index", "");
31071 if(this.collapseBtn){
31072 this.collapseBtn.show();
31074 this.closeBtn.setStyle('display', this.closeBtnState);
31076 this.stickBtn.hide();
31078 this.fireEvent("slidehide", this);
31081 slideIn : function(cb){
31082 if(!this.isSlid || this.el.hasActiveFx()){
31086 this.isSlid = false;
31087 this.beforeSlide();
31088 this.el.slideOut(this.getSlideAnchor(), {
31089 callback: function(){
31090 this.el.setLeftTop(-10000, -10000);
31092 this.afterSlideIn();
31100 slideInIf : function(e){
31101 if(!e.within(this.el)){
31106 animateCollapse : function(){
31107 this.beforeSlide();
31108 this.el.setStyle("z-index", 20000);
31109 var anchor = this.getSlideAnchor();
31110 this.el.slideOut(anchor, {
31111 callback : function(){
31112 this.el.setStyle("z-index", "");
31113 this.collapsedEl.slideIn(anchor, {duration:.3});
31115 this.el.setLocation(-10000,-10000);
31117 this.fireEvent("collapsed", this);
31124 animateExpand : function(){
31125 this.beforeSlide();
31126 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31127 this.el.setStyle("z-index", 20000);
31128 this.collapsedEl.hide({
31131 this.el.slideIn(this.getSlideAnchor(), {
31132 callback : function(){
31133 this.el.setStyle("z-index", "");
31136 this.split.el.show();
31138 this.fireEvent("invalidated", this);
31139 this.fireEvent("expanded", this);
31167 getAnchor : function(){
31168 return this.anchors[this.position];
31171 getCollapseAnchor : function(){
31172 return this.canchors[this.position];
31175 getSlideAnchor : function(){
31176 return this.sanchors[this.position];
31179 getAlignAdj : function(){
31180 var cm = this.cmargins;
31181 switch(this.position){
31197 getExpandAdj : function(){
31198 var c = this.collapsedEl, cm = this.cmargins;
31199 switch(this.position){
31201 return [-(cm.right+c.getWidth()+cm.left), 0];
31204 return [cm.right+c.getWidth()+cm.left, 0];
31207 return [0, -(cm.top+cm.bottom+c.getHeight())];
31210 return [0, cm.top+cm.bottom+c.getHeight()];
31216 * Ext JS Library 1.1.1
31217 * Copyright(c) 2006-2007, Ext JS, LLC.
31219 * Originally Released Under LGPL - original licence link has changed is not relivant.
31222 * <script type="text/javascript">
31225 * These classes are private internal classes
31227 Roo.CenterLayoutRegion = function(mgr, config){
31228 Roo.LayoutRegion.call(this, mgr, config, "center");
31229 this.visible = true;
31230 this.minWidth = config.minWidth || 20;
31231 this.minHeight = config.minHeight || 20;
31234 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31236 // center panel can't be hidden
31240 // center panel can't be hidden
31243 getMinWidth: function(){
31244 return this.minWidth;
31247 getMinHeight: function(){
31248 return this.minHeight;
31253 Roo.NorthLayoutRegion = function(mgr, config){
31254 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31256 this.split.placement = Roo.SplitBar.TOP;
31257 this.split.orientation = Roo.SplitBar.VERTICAL;
31258 this.split.el.addClass("x-layout-split-v");
31260 var size = config.initialSize || config.height;
31261 if(typeof size != "undefined"){
31262 this.el.setHeight(size);
31265 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31266 orientation: Roo.SplitBar.VERTICAL,
31267 getBox : function(){
31268 if(this.collapsed){
31269 return this.collapsedEl.getBox();
31271 var box = this.el.getBox();
31273 box.height += this.split.el.getHeight();
31278 updateBox : function(box){
31279 if(this.split && !this.collapsed){
31280 box.height -= this.split.el.getHeight();
31281 this.split.el.setLeft(box.x);
31282 this.split.el.setTop(box.y+box.height);
31283 this.split.el.setWidth(box.width);
31285 if(this.collapsed){
31286 this.updateBody(box.width, null);
31288 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31292 Roo.SouthLayoutRegion = function(mgr, config){
31293 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31295 this.split.placement = Roo.SplitBar.BOTTOM;
31296 this.split.orientation = Roo.SplitBar.VERTICAL;
31297 this.split.el.addClass("x-layout-split-v");
31299 var size = config.initialSize || config.height;
31300 if(typeof size != "undefined"){
31301 this.el.setHeight(size);
31304 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31305 orientation: Roo.SplitBar.VERTICAL,
31306 getBox : function(){
31307 if(this.collapsed){
31308 return this.collapsedEl.getBox();
31310 var box = this.el.getBox();
31312 var sh = this.split.el.getHeight();
31319 updateBox : function(box){
31320 if(this.split && !this.collapsed){
31321 var sh = this.split.el.getHeight();
31324 this.split.el.setLeft(box.x);
31325 this.split.el.setTop(box.y-sh);
31326 this.split.el.setWidth(box.width);
31328 if(this.collapsed){
31329 this.updateBody(box.width, null);
31331 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31335 Roo.EastLayoutRegion = function(mgr, config){
31336 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31338 this.split.placement = Roo.SplitBar.RIGHT;
31339 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31340 this.split.el.addClass("x-layout-split-h");
31342 var size = config.initialSize || config.width;
31343 if(typeof size != "undefined"){
31344 this.el.setWidth(size);
31347 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31348 orientation: Roo.SplitBar.HORIZONTAL,
31349 getBox : function(){
31350 if(this.collapsed){
31351 return this.collapsedEl.getBox();
31353 var box = this.el.getBox();
31355 var sw = this.split.el.getWidth();
31362 updateBox : function(box){
31363 if(this.split && !this.collapsed){
31364 var sw = this.split.el.getWidth();
31366 this.split.el.setLeft(box.x);
31367 this.split.el.setTop(box.y);
31368 this.split.el.setHeight(box.height);
31371 if(this.collapsed){
31372 this.updateBody(null, box.height);
31374 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31378 Roo.WestLayoutRegion = function(mgr, config){
31379 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31381 this.split.placement = Roo.SplitBar.LEFT;
31382 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31383 this.split.el.addClass("x-layout-split-h");
31385 var size = config.initialSize || config.width;
31386 if(typeof size != "undefined"){
31387 this.el.setWidth(size);
31390 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31391 orientation: Roo.SplitBar.HORIZONTAL,
31392 getBox : function(){
31393 if(this.collapsed){
31394 return this.collapsedEl.getBox();
31396 var box = this.el.getBox();
31398 box.width += this.split.el.getWidth();
31403 updateBox : function(box){
31404 if(this.split && !this.collapsed){
31405 var sw = this.split.el.getWidth();
31407 this.split.el.setLeft(box.x+box.width);
31408 this.split.el.setTop(box.y);
31409 this.split.el.setHeight(box.height);
31411 if(this.collapsed){
31412 this.updateBody(null, box.height);
31414 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31419 * Ext JS Library 1.1.1
31420 * Copyright(c) 2006-2007, Ext JS, LLC.
31422 * Originally Released Under LGPL - original licence link has changed is not relivant.
31425 * <script type="text/javascript">
31430 * Private internal class for reading and applying state
31432 Roo.LayoutStateManager = function(layout){
31433 // default empty state
31442 Roo.LayoutStateManager.prototype = {
31443 init : function(layout, provider){
31444 this.provider = provider;
31445 var state = provider.get(layout.id+"-layout-state");
31447 var wasUpdating = layout.isUpdating();
31449 layout.beginUpdate();
31451 for(var key in state){
31452 if(typeof state[key] != "function"){
31453 var rstate = state[key];
31454 var r = layout.getRegion(key);
31457 r.resizeTo(rstate.size);
31459 if(rstate.collapsed == true){
31462 r.expand(null, true);
31468 layout.endUpdate();
31470 this.state = state;
31472 this.layout = layout;
31473 layout.on("regionresized", this.onRegionResized, this);
31474 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31475 layout.on("regionexpanded", this.onRegionExpanded, this);
31478 storeState : function(){
31479 this.provider.set(this.layout.id+"-layout-state", this.state);
31482 onRegionResized : function(region, newSize){
31483 this.state[region.getPosition()].size = newSize;
31487 onRegionCollapsed : function(region){
31488 this.state[region.getPosition()].collapsed = true;
31492 onRegionExpanded : function(region){
31493 this.state[region.getPosition()].collapsed = false;
31498 * Ext JS Library 1.1.1
31499 * Copyright(c) 2006-2007, Ext JS, LLC.
31501 * Originally Released Under LGPL - original licence link has changed is not relivant.
31504 * <script type="text/javascript">
31507 * @class Roo.ContentPanel
31508 * @extends Roo.util.Observable
31509 * A basic ContentPanel element.
31510 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31511 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31512 * @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
31513 * @cfg {Boolean} closable True if the panel can be closed/removed
31514 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31515 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31516 * @cfg {Toolbar} toolbar A toolbar for this panel
31517 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31518 * @cfg {String} title The title for this panel
31519 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31520 * @cfg {String} url Calls {@link #setUrl} with this value
31521 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31522 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31523 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31524 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31527 * Create a new ContentPanel.
31528 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31529 * @param {String/Object} config A string to set only the title or a config object
31530 * @param {String} content (optional) Set the HTML content for this panel
31531 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31533 Roo.ContentPanel = function(el, config, content){
31537 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31541 if (config && config.parentLayout) {
31542 el = config.parentLayout.el.createChild();
31545 if(el.autoCreate){ // xtype is available if this is called from factory
31549 this.el = Roo.get(el);
31550 if(!this.el && config && config.autoCreate){
31551 if(typeof config.autoCreate == "object"){
31552 if(!config.autoCreate.id){
31553 config.autoCreate.id = config.id||el;
31555 this.el = Roo.DomHelper.append(document.body,
31556 config.autoCreate, true);
31558 this.el = Roo.DomHelper.append(document.body,
31559 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31562 this.closable = false;
31563 this.loaded = false;
31564 this.active = false;
31565 if(typeof config == "string"){
31566 this.title = config;
31568 Roo.apply(this, config);
31571 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31572 this.wrapEl = this.el.wrap();
31573 this.toolbar.container = this.el.insertSibling(false, 'before');
31574 this.toolbar = new Roo.Toolbar(this.toolbar);
31577 // xtype created footer. - not sure if will work as we normally have to render first..
31578 if (this.footer && !this.footer.el && this.footer.xtype) {
31579 if (!this.wrapEl) {
31580 this.wrapEl = this.el.wrap();
31583 this.footer.container = this.wrapEl.createChild();
31585 this.footer = Roo.factory(this.footer, Roo);
31590 this.resizeEl = Roo.get(this.resizeEl, true);
31592 this.resizeEl = this.el;
31594 // handle view.xtype
31602 * Fires when this panel is activated.
31603 * @param {Roo.ContentPanel} this
31607 * @event deactivate
31608 * Fires when this panel is activated.
31609 * @param {Roo.ContentPanel} this
31611 "deactivate" : true,
31615 * Fires when this panel is resized if fitToFrame is true.
31616 * @param {Roo.ContentPanel} this
31617 * @param {Number} width The width after any component adjustments
31618 * @param {Number} height The height after any component adjustments
31624 * Fires when this tab is created
31625 * @param {Roo.ContentPanel} this
31635 if(this.autoScroll){
31636 this.resizeEl.setStyle("overflow", "auto");
31638 // fix randome scrolling
31639 this.el.on('scroll', function() {
31640 Roo.log('fix random scolling');
31641 this.scrollTo('top',0);
31644 content = content || this.content;
31646 this.setContent(content);
31648 if(config && config.url){
31649 this.setUrl(this.url, this.params, this.loadOnce);
31654 Roo.ContentPanel.superclass.constructor.call(this);
31656 if (this.view && typeof(this.view.xtype) != 'undefined') {
31657 this.view.el = this.el.appendChild(document.createElement("div"));
31658 this.view = Roo.factory(this.view);
31659 this.view.render && this.view.render(false, '');
31663 this.fireEvent('render', this);
31666 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31668 setRegion : function(region){
31669 this.region = region;
31671 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31673 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31678 * Returns the toolbar for this Panel if one was configured.
31679 * @return {Roo.Toolbar}
31681 getToolbar : function(){
31682 return this.toolbar;
31685 setActiveState : function(active){
31686 this.active = active;
31688 this.fireEvent("deactivate", this);
31690 this.fireEvent("activate", this);
31694 * Updates this panel's element
31695 * @param {String} content The new content
31696 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31698 setContent : function(content, loadScripts){
31699 this.el.update(content, loadScripts);
31702 ignoreResize : function(w, h){
31703 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31706 this.lastSize = {width: w, height: h};
31711 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31712 * @return {Roo.UpdateManager} The UpdateManager
31714 getUpdateManager : function(){
31715 return this.el.getUpdateManager();
31718 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31719 * @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:
31722 url: "your-url.php",
31723 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31724 callback: yourFunction,
31725 scope: yourObject, //(optional scope)
31728 text: "Loading...",
31733 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31734 * 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.
31735 * @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}
31736 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31737 * @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.
31738 * @return {Roo.ContentPanel} this
31741 var um = this.el.getUpdateManager();
31742 um.update.apply(um, arguments);
31748 * 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.
31749 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31750 * @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)
31751 * @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)
31752 * @return {Roo.UpdateManager} The UpdateManager
31754 setUrl : function(url, params, loadOnce){
31755 if(this.refreshDelegate){
31756 this.removeListener("activate", this.refreshDelegate);
31758 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31759 this.on("activate", this.refreshDelegate);
31760 return this.el.getUpdateManager();
31763 _handleRefresh : function(url, params, loadOnce){
31764 if(!loadOnce || !this.loaded){
31765 var updater = this.el.getUpdateManager();
31766 updater.update(url, params, this._setLoaded.createDelegate(this));
31770 _setLoaded : function(){
31771 this.loaded = true;
31775 * Returns this panel's id
31778 getId : function(){
31783 * Returns this panel's element - used by regiosn to add.
31784 * @return {Roo.Element}
31786 getEl : function(){
31787 return this.wrapEl || this.el;
31790 adjustForComponents : function(width, height)
31792 //Roo.log('adjustForComponents ');
31793 if(this.resizeEl != this.el){
31794 width -= this.el.getFrameWidth('lr');
31795 height -= this.el.getFrameWidth('tb');
31798 var te = this.toolbar.getEl();
31799 height -= te.getHeight();
31800 te.setWidth(width);
31803 var te = this.footer.getEl();
31804 //Roo.log("footer:" + te.getHeight());
31806 height -= te.getHeight();
31807 te.setWidth(width);
31811 if(this.adjustments){
31812 width += this.adjustments[0];
31813 height += this.adjustments[1];
31815 return {"width": width, "height": height};
31818 setSize : function(width, height){
31819 if(this.fitToFrame && !this.ignoreResize(width, height)){
31820 if(this.fitContainer && this.resizeEl != this.el){
31821 this.el.setSize(width, height);
31823 var size = this.adjustForComponents(width, height);
31824 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31825 this.fireEvent('resize', this, size.width, size.height);
31830 * Returns this panel's title
31833 getTitle : function(){
31838 * Set this panel's title
31839 * @param {String} title
31841 setTitle : function(title){
31842 this.title = title;
31844 this.region.updatePanelTitle(this, title);
31849 * Returns true is this panel was configured to be closable
31850 * @return {Boolean}
31852 isClosable : function(){
31853 return this.closable;
31856 beforeSlide : function(){
31858 this.resizeEl.clip();
31861 afterSlide : function(){
31863 this.resizeEl.unclip();
31867 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31868 * Will fail silently if the {@link #setUrl} method has not been called.
31869 * This does not activate the panel, just updates its content.
31871 refresh : function(){
31872 if(this.refreshDelegate){
31873 this.loaded = false;
31874 this.refreshDelegate();
31879 * Destroys this panel
31881 destroy : function(){
31882 this.el.removeAllListeners();
31883 var tempEl = document.createElement("span");
31884 tempEl.appendChild(this.el.dom);
31885 tempEl.innerHTML = "";
31891 * form - if the content panel contains a form - this is a reference to it.
31892 * @type {Roo.form.Form}
31896 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31897 * This contains a reference to it.
31903 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31913 * @param {Object} cfg Xtype definition of item to add.
31916 addxtype : function(cfg) {
31918 if (cfg.xtype.match(/^Form$/)) {
31921 //if (this.footer) {
31922 // el = this.footer.container.insertSibling(false, 'before');
31924 el = this.el.createChild();
31927 this.form = new Roo.form.Form(cfg);
31930 if ( this.form.allItems.length) {
31931 this.form.render(el.dom);
31935 // should only have one of theses..
31936 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31937 // views.. should not be just added - used named prop 'view''
31939 cfg.el = this.el.appendChild(document.createElement("div"));
31942 var ret = new Roo.factory(cfg);
31944 ret.render && ret.render(false, ''); // render blank..
31953 * @class Roo.GridPanel
31954 * @extends Roo.ContentPanel
31956 * Create a new GridPanel.
31957 * @param {Roo.grid.Grid} grid The grid for this panel
31958 * @param {String/Object} config A string to set only the panel's title, or a config object
31960 Roo.GridPanel = function(grid, config){
31963 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31964 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31966 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31968 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31971 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31973 // xtype created footer. - not sure if will work as we normally have to render first..
31974 if (this.footer && !this.footer.el && this.footer.xtype) {
31976 this.footer.container = this.grid.getView().getFooterPanel(true);
31977 this.footer.dataSource = this.grid.dataSource;
31978 this.footer = Roo.factory(this.footer, Roo);
31982 grid.monitorWindowResize = false; // turn off autosizing
31983 grid.autoHeight = false;
31984 grid.autoWidth = false;
31986 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31989 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31990 getId : function(){
31991 return this.grid.id;
31995 * Returns the grid for this panel
31996 * @return {Roo.grid.Grid}
31998 getGrid : function(){
32002 setSize : function(width, height){
32003 if(!this.ignoreResize(width, height)){
32004 var grid = this.grid;
32005 var size = this.adjustForComponents(width, height);
32006 grid.getGridEl().setSize(size.width, size.height);
32011 beforeSlide : function(){
32012 this.grid.getView().scroller.clip();
32015 afterSlide : function(){
32016 this.grid.getView().scroller.unclip();
32019 destroy : function(){
32020 this.grid.destroy();
32022 Roo.GridPanel.superclass.destroy.call(this);
32028 * @class Roo.NestedLayoutPanel
32029 * @extends Roo.ContentPanel
32031 * Create a new NestedLayoutPanel.
32034 * @param {Roo.BorderLayout} layout The layout for this panel
32035 * @param {String/Object} config A string to set only the title or a config object
32037 Roo.NestedLayoutPanel = function(layout, config)
32039 // construct with only one argument..
32040 /* FIXME - implement nicer consturctors
32041 if (layout.layout) {
32043 layout = config.layout;
32044 delete config.layout;
32046 if (layout.xtype && !layout.getEl) {
32047 // then layout needs constructing..
32048 layout = Roo.factory(layout, Roo);
32053 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32055 layout.monitorWindowResize = false; // turn off autosizing
32056 this.layout = layout;
32057 this.layout.getEl().addClass("x-layout-nested-layout");
32064 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32066 setSize : function(width, height){
32067 if(!this.ignoreResize(width, height)){
32068 var size = this.adjustForComponents(width, height);
32069 var el = this.layout.getEl();
32070 el.setSize(size.width, size.height);
32071 var touch = el.dom.offsetWidth;
32072 this.layout.layout();
32073 // ie requires a double layout on the first pass
32074 if(Roo.isIE && !this.initialized){
32075 this.initialized = true;
32076 this.layout.layout();
32081 // activate all subpanels if not currently active..
32083 setActiveState : function(active){
32084 this.active = active;
32086 this.fireEvent("deactivate", this);
32090 this.fireEvent("activate", this);
32091 // not sure if this should happen before or after..
32092 if (!this.layout) {
32093 return; // should not happen..
32096 for (var r in this.layout.regions) {
32097 reg = this.layout.getRegion(r);
32098 if (reg.getActivePanel()) {
32099 //reg.showPanel(reg.getActivePanel()); // force it to activate..
32100 reg.setActivePanel(reg.getActivePanel());
32103 if (!reg.panels.length) {
32106 reg.showPanel(reg.getPanel(0));
32115 * Returns the nested BorderLayout for this panel
32116 * @return {Roo.BorderLayout}
32118 getLayout : function(){
32119 return this.layout;
32123 * Adds a xtype elements to the layout of the nested panel
32127 xtype : 'ContentPanel',
32134 xtype : 'NestedLayoutPanel',
32140 items : [ ... list of content panels or nested layout panels.. ]
32144 * @param {Object} cfg Xtype definition of item to add.
32146 addxtype : function(cfg) {
32147 return this.layout.addxtype(cfg);
32152 Roo.ScrollPanel = function(el, config, content){
32153 config = config || {};
32154 config.fitToFrame = true;
32155 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32157 this.el.dom.style.overflow = "hidden";
32158 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32159 this.el.removeClass("x-layout-inactive-content");
32160 this.el.on("mousewheel", this.onWheel, this);
32162 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
32163 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
32164 up.unselectable(); down.unselectable();
32165 up.on("click", this.scrollUp, this);
32166 down.on("click", this.scrollDown, this);
32167 up.addClassOnOver("x-scroller-btn-over");
32168 down.addClassOnOver("x-scroller-btn-over");
32169 up.addClassOnClick("x-scroller-btn-click");
32170 down.addClassOnClick("x-scroller-btn-click");
32171 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32173 this.resizeEl = this.el;
32174 this.el = wrap; this.up = up; this.down = down;
32177 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32179 wheelIncrement : 5,
32180 scrollUp : function(){
32181 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32184 scrollDown : function(){
32185 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32188 afterScroll : function(){
32189 var el = this.resizeEl;
32190 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32191 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32192 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32195 setSize : function(){
32196 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32197 this.afterScroll();
32200 onWheel : function(e){
32201 var d = e.getWheelDelta();
32202 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32203 this.afterScroll();
32207 setContent : function(content, loadScripts){
32208 this.resizeEl.update(content, loadScripts);
32222 * @class Roo.TreePanel
32223 * @extends Roo.ContentPanel
32225 * Create a new TreePanel. - defaults to fit/scoll contents.
32226 * @param {String/Object} config A string to set only the panel's title, or a config object
32227 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32229 Roo.TreePanel = function(config){
32230 var el = config.el;
32231 var tree = config.tree;
32232 delete config.tree;
32233 delete config.el; // hopefull!
32235 // wrapper for IE7 strict & safari scroll issue
32237 var treeEl = el.createChild();
32238 config.resizeEl = treeEl;
32242 Roo.TreePanel.superclass.constructor.call(this, el, config);
32245 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32246 //console.log(tree);
32247 this.on('activate', function()
32249 if (this.tree.rendered) {
32252 //console.log('render tree');
32253 this.tree.render();
32255 // this should not be needed.. - it's actually the 'el' that resizes?
32256 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32258 //this.on('resize', function (cp, w, h) {
32259 // this.tree.innerCt.setWidth(w);
32260 // this.tree.innerCt.setHeight(h);
32261 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32268 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32285 * Ext JS Library 1.1.1
32286 * Copyright(c) 2006-2007, Ext JS, LLC.
32288 * Originally Released Under LGPL - original licence link has changed is not relivant.
32291 * <script type="text/javascript">
32296 * @class Roo.ReaderLayout
32297 * @extends Roo.BorderLayout
32298 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32299 * center region containing two nested regions (a top one for a list view and one for item preview below),
32300 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32301 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32302 * expedites the setup of the overall layout and regions for this common application style.
32305 var reader = new Roo.ReaderLayout();
32306 var CP = Roo.ContentPanel; // shortcut for adding
32308 reader.beginUpdate();
32309 reader.add("north", new CP("north", "North"));
32310 reader.add("west", new CP("west", {title: "West"}));
32311 reader.add("east", new CP("east", {title: "East"}));
32313 reader.regions.listView.add(new CP("listView", "List"));
32314 reader.regions.preview.add(new CP("preview", "Preview"));
32315 reader.endUpdate();
32318 * Create a new ReaderLayout
32319 * @param {Object} config Configuration options
32320 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32321 * document.body if omitted)
32323 Roo.ReaderLayout = function(config, renderTo){
32324 var c = config || {size:{}};
32325 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32326 north: c.north !== false ? Roo.apply({
32330 }, c.north) : false,
32331 west: c.west !== false ? Roo.apply({
32339 margins:{left:5,right:0,bottom:5,top:5},
32340 cmargins:{left:5,right:5,bottom:5,top:5}
32341 }, c.west) : false,
32342 east: c.east !== false ? Roo.apply({
32350 margins:{left:0,right:5,bottom:5,top:5},
32351 cmargins:{left:5,right:5,bottom:5,top:5}
32352 }, c.east) : false,
32353 center: Roo.apply({
32354 tabPosition: 'top',
32358 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32362 this.el.addClass('x-reader');
32364 this.beginUpdate();
32366 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32367 south: c.preview !== false ? Roo.apply({
32374 cmargins:{top:5,left:0, right:0, bottom:0}
32375 }, c.preview) : false,
32376 center: Roo.apply({
32382 this.add('center', new Roo.NestedLayoutPanel(inner,
32383 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32387 this.regions.preview = inner.getRegion('south');
32388 this.regions.listView = inner.getRegion('center');
32391 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32393 * Ext JS Library 1.1.1
32394 * Copyright(c) 2006-2007, Ext JS, LLC.
32396 * Originally Released Under LGPL - original licence link has changed is not relivant.
32399 * <script type="text/javascript">
32403 * @class Roo.grid.Grid
32404 * @extends Roo.util.Observable
32405 * This class represents the primary interface of a component based grid control.
32406 * <br><br>Usage:<pre><code>
32407 var grid = new Roo.grid.Grid("my-container-id", {
32410 selModel: mySelectionModel,
32411 autoSizeColumns: true,
32412 monitorWindowResize: false,
32413 trackMouseOver: true
32418 * <b>Common Problems:</b><br/>
32419 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32420 * element will correct this<br/>
32421 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32422 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32423 * are unpredictable.<br/>
32424 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32425 * grid to calculate dimensions/offsets.<br/>
32427 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32428 * The container MUST have some type of size defined for the grid to fill. The container will be
32429 * automatically set to position relative if it isn't already.
32430 * @param {Object} config A config object that sets properties on this grid.
32432 Roo.grid.Grid = function(container, config){
32433 // initialize the container
32434 this.container = Roo.get(container);
32435 this.container.update("");
32436 this.container.setStyle("overflow", "hidden");
32437 this.container.addClass('x-grid-container');
32439 this.id = this.container.id;
32441 Roo.apply(this, config);
32442 // check and correct shorthanded configs
32444 this.dataSource = this.ds;
32448 this.colModel = this.cm;
32452 this.selModel = this.sm;
32456 if (this.selModel) {
32457 this.selModel = Roo.factory(this.selModel, Roo.grid);
32458 this.sm = this.selModel;
32459 this.sm.xmodule = this.xmodule || false;
32461 if (typeof(this.colModel.config) == 'undefined') {
32462 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32463 this.cm = this.colModel;
32464 this.cm.xmodule = this.xmodule || false;
32466 if (this.dataSource) {
32467 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32468 this.ds = this.dataSource;
32469 this.ds.xmodule = this.xmodule || false;
32476 this.container.setWidth(this.width);
32480 this.container.setHeight(this.height);
32487 * The raw click event for the entire grid.
32488 * @param {Roo.EventObject} e
32493 * The raw dblclick event for the entire grid.
32494 * @param {Roo.EventObject} e
32498 * @event contextmenu
32499 * The raw contextmenu event for the entire grid.
32500 * @param {Roo.EventObject} e
32502 "contextmenu" : true,
32505 * The raw mousedown event for the entire grid.
32506 * @param {Roo.EventObject} e
32508 "mousedown" : true,
32511 * The raw mouseup event for the entire grid.
32512 * @param {Roo.EventObject} e
32517 * The raw mouseover event for the entire grid.
32518 * @param {Roo.EventObject} e
32520 "mouseover" : true,
32523 * The raw mouseout event for the entire grid.
32524 * @param {Roo.EventObject} e
32529 * The raw keypress event for the entire grid.
32530 * @param {Roo.EventObject} e
32535 * The raw keydown event for the entire grid.
32536 * @param {Roo.EventObject} e
32544 * Fires when a cell is clicked
32545 * @param {Grid} this
32546 * @param {Number} rowIndex
32547 * @param {Number} columnIndex
32548 * @param {Roo.EventObject} e
32550 "cellclick" : true,
32552 * @event celldblclick
32553 * Fires when a cell is double clicked
32554 * @param {Grid} this
32555 * @param {Number} rowIndex
32556 * @param {Number} columnIndex
32557 * @param {Roo.EventObject} e
32559 "celldblclick" : true,
32562 * Fires when a row is clicked
32563 * @param {Grid} this
32564 * @param {Number} rowIndex
32565 * @param {Roo.EventObject} e
32569 * @event rowdblclick
32570 * Fires when a row is double clicked
32571 * @param {Grid} this
32572 * @param {Number} rowIndex
32573 * @param {Roo.EventObject} e
32575 "rowdblclick" : true,
32577 * @event headerclick
32578 * Fires when a header is clicked
32579 * @param {Grid} this
32580 * @param {Number} columnIndex
32581 * @param {Roo.EventObject} e
32583 "headerclick" : true,
32585 * @event headerdblclick
32586 * Fires when a header cell is double clicked
32587 * @param {Grid} this
32588 * @param {Number} columnIndex
32589 * @param {Roo.EventObject} e
32591 "headerdblclick" : true,
32593 * @event rowcontextmenu
32594 * Fires when a row is right clicked
32595 * @param {Grid} this
32596 * @param {Number} rowIndex
32597 * @param {Roo.EventObject} e
32599 "rowcontextmenu" : true,
32601 * @event cellcontextmenu
32602 * Fires when a cell is right clicked
32603 * @param {Grid} this
32604 * @param {Number} rowIndex
32605 * @param {Number} cellIndex
32606 * @param {Roo.EventObject} e
32608 "cellcontextmenu" : true,
32610 * @event headercontextmenu
32611 * Fires when a header is right clicked
32612 * @param {Grid} this
32613 * @param {Number} columnIndex
32614 * @param {Roo.EventObject} e
32616 "headercontextmenu" : true,
32618 * @event bodyscroll
32619 * Fires when the body element is scrolled
32620 * @param {Number} scrollLeft
32621 * @param {Number} scrollTop
32623 "bodyscroll" : true,
32625 * @event columnresize
32626 * Fires when the user resizes a column
32627 * @param {Number} columnIndex
32628 * @param {Number} newSize
32630 "columnresize" : true,
32632 * @event columnmove
32633 * Fires when the user moves a column
32634 * @param {Number} oldIndex
32635 * @param {Number} newIndex
32637 "columnmove" : true,
32640 * Fires when row(s) start being dragged
32641 * @param {Grid} this
32642 * @param {Roo.GridDD} dd The drag drop object
32643 * @param {event} e The raw browser event
32645 "startdrag" : true,
32648 * Fires when a drag operation is complete
32649 * @param {Grid} this
32650 * @param {Roo.GridDD} dd The drag drop object
32651 * @param {event} e The raw browser event
32656 * Fires when dragged row(s) are dropped on a valid DD target
32657 * @param {Grid} this
32658 * @param {Roo.GridDD} dd The drag drop object
32659 * @param {String} targetId The target drag drop object
32660 * @param {event} e The raw browser event
32665 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32666 * @param {Grid} this
32667 * @param {Roo.GridDD} dd The drag drop object
32668 * @param {String} targetId The target drag drop object
32669 * @param {event} e The raw browser event
32674 * Fires when the dragged row(s) first cross another DD target while being dragged
32675 * @param {Grid} this
32676 * @param {Roo.GridDD} dd The drag drop object
32677 * @param {String} targetId The target drag drop object
32678 * @param {event} e The raw browser event
32680 "dragenter" : true,
32683 * Fires when the dragged row(s) leave another DD target while being dragged
32684 * @param {Grid} this
32685 * @param {Roo.GridDD} dd The drag drop object
32686 * @param {String} targetId The target drag drop object
32687 * @param {event} e The raw browser event
32692 * Fires when a row is rendered, so you can change add a style to it.
32693 * @param {GridView} gridview The grid view
32694 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32700 * Fires when the grid is rendered
32701 * @param {Grid} grid
32706 Roo.grid.Grid.superclass.constructor.call(this);
32708 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32711 * @cfg {String} ddGroup - drag drop group.
32715 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32717 minColumnWidth : 25,
32720 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32721 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32722 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32724 autoSizeColumns : false,
32727 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32729 autoSizeHeaders : true,
32732 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32734 monitorWindowResize : true,
32737 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32738 * rows measured to get a columns size. Default is 0 (all rows).
32740 maxRowsToMeasure : 0,
32743 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32745 trackMouseOver : true,
32748 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32752 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32754 enableDragDrop : false,
32757 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32759 enableColumnMove : true,
32762 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32764 enableColumnHide : true,
32767 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32769 enableRowHeightSync : false,
32772 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32777 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32779 autoHeight : false,
32782 * @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.
32784 autoExpandColumn : false,
32787 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32790 autoExpandMin : 50,
32793 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32795 autoExpandMax : 1000,
32798 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32803 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32807 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32817 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32818 * of a fixed width. Default is false.
32821 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32824 * Called once after all setup has been completed and the grid is ready to be rendered.
32825 * @return {Roo.grid.Grid} this
32827 render : function()
32829 var c = this.container;
32830 // try to detect autoHeight/width mode
32831 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32832 this.autoHeight = true;
32834 var view = this.getView();
32837 c.on("click", this.onClick, this);
32838 c.on("dblclick", this.onDblClick, this);
32839 c.on("contextmenu", this.onContextMenu, this);
32840 c.on("keydown", this.onKeyDown, this);
32842 c.on("touchstart", this.onTouchStart, this);
32845 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32847 this.getSelectionModel().init(this);
32852 this.loadMask = new Roo.LoadMask(this.container,
32853 Roo.apply({store:this.dataSource}, this.loadMask));
32857 if (this.toolbar && this.toolbar.xtype) {
32858 this.toolbar.container = this.getView().getHeaderPanel(true);
32859 this.toolbar = new Roo.Toolbar(this.toolbar);
32861 if (this.footer && this.footer.xtype) {
32862 this.footer.dataSource = this.getDataSource();
32863 this.footer.container = this.getView().getFooterPanel(true);
32864 this.footer = Roo.factory(this.footer, Roo);
32866 if (this.dropTarget && this.dropTarget.xtype) {
32867 delete this.dropTarget.xtype;
32868 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32872 this.rendered = true;
32873 this.fireEvent('render', this);
32878 * Reconfigures the grid to use a different Store and Column Model.
32879 * The View will be bound to the new objects and refreshed.
32880 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32881 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32883 reconfigure : function(dataSource, colModel){
32885 this.loadMask.destroy();
32886 this.loadMask = new Roo.LoadMask(this.container,
32887 Roo.apply({store:dataSource}, this.loadMask));
32889 this.view.bind(dataSource, colModel);
32890 this.dataSource = dataSource;
32891 this.colModel = colModel;
32892 this.view.refresh(true);
32896 onKeyDown : function(e){
32897 this.fireEvent("keydown", e);
32901 * Destroy this grid.
32902 * @param {Boolean} removeEl True to remove the element
32904 destroy : function(removeEl, keepListeners){
32906 this.loadMask.destroy();
32908 var c = this.container;
32909 c.removeAllListeners();
32910 this.view.destroy();
32911 this.colModel.purgeListeners();
32912 if(!keepListeners){
32913 this.purgeListeners();
32916 if(removeEl === true){
32922 processEvent : function(name, e){
32923 // does this fire select???
32924 //Roo.log('grid:processEvent ' + name);
32926 if (name != 'touchstart' ) {
32927 this.fireEvent(name, e);
32930 var t = e.getTarget();
32932 var header = v.findHeaderIndex(t);
32933 if(header !== false){
32934 var ename = name == 'touchstart' ? 'click' : name;
32936 this.fireEvent("header" + ename, this, header, e);
32938 var row = v.findRowIndex(t);
32939 var cell = v.findCellIndex(t);
32940 if (name == 'touchstart') {
32941 // first touch is always a click.
32942 // hopefull this happens after selection is updated.?
32945 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32946 var cs = this.selModel.getSelectedCell();
32947 if (row == cs[0] && cell == cs[1]){
32951 if (typeof(this.selModel.getSelections) != 'undefined') {
32952 var cs = this.selModel.getSelections();
32953 var ds = this.dataSource;
32954 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32965 this.fireEvent("row" + name, this, row, e);
32966 if(cell !== false){
32967 this.fireEvent("cell" + name, this, row, cell, e);
32974 onClick : function(e){
32975 this.processEvent("click", e);
32978 onTouchStart : function(e){
32979 this.processEvent("touchstart", e);
32983 onContextMenu : function(e, t){
32984 this.processEvent("contextmenu", e);
32988 onDblClick : function(e){
32989 this.processEvent("dblclick", e);
32993 walkCells : function(row, col, step, fn, scope){
32994 var cm = this.colModel, clen = cm.getColumnCount();
32995 var ds = this.dataSource, rlen = ds.getCount(), first = true;
33007 if(fn.call(scope || this, row, col, cm) === true){
33025 if(fn.call(scope || this, row, col, cm) === true){
33037 getSelections : function(){
33038 return this.selModel.getSelections();
33042 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33043 * but if manual update is required this method will initiate it.
33045 autoSize : function(){
33047 this.view.layout();
33048 if(this.view.adjustForScroll){
33049 this.view.adjustForScroll();
33055 * Returns the grid's underlying element.
33056 * @return {Element} The element
33058 getGridEl : function(){
33059 return this.container;
33062 // private for compatibility, overridden by editor grid
33063 stopEditing : function(){},
33066 * Returns the grid's SelectionModel.
33067 * @return {SelectionModel}
33069 getSelectionModel : function(){
33070 if(!this.selModel){
33071 this.selModel = new Roo.grid.RowSelectionModel();
33073 return this.selModel;
33077 * Returns the grid's DataSource.
33078 * @return {DataSource}
33080 getDataSource : function(){
33081 return this.dataSource;
33085 * Returns the grid's ColumnModel.
33086 * @return {ColumnModel}
33088 getColumnModel : function(){
33089 return this.colModel;
33093 * Returns the grid's GridView object.
33094 * @return {GridView}
33096 getView : function(){
33098 this.view = new Roo.grid.GridView(this.viewConfig);
33103 * Called to get grid's drag proxy text, by default returns this.ddText.
33106 getDragDropText : function(){
33107 var count = this.selModel.getCount();
33108 return String.format(this.ddText, count, count == 1 ? '' : 's');
33112 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33113 * %0 is replaced with the number of selected rows.
33116 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33118 * Ext JS Library 1.1.1
33119 * Copyright(c) 2006-2007, Ext JS, LLC.
33121 * Originally Released Under LGPL - original licence link has changed is not relivant.
33124 * <script type="text/javascript">
33127 Roo.grid.AbstractGridView = function(){
33131 "beforerowremoved" : true,
33132 "beforerowsinserted" : true,
33133 "beforerefresh" : true,
33134 "rowremoved" : true,
33135 "rowsinserted" : true,
33136 "rowupdated" : true,
33139 Roo.grid.AbstractGridView.superclass.constructor.call(this);
33142 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33143 rowClass : "x-grid-row",
33144 cellClass : "x-grid-cell",
33145 tdClass : "x-grid-td",
33146 hdClass : "x-grid-hd",
33147 splitClass : "x-grid-hd-split",
33149 init: function(grid){
33151 var cid = this.grid.getGridEl().id;
33152 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33153 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33154 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33155 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33158 getColumnRenderers : function(){
33159 var renderers = [];
33160 var cm = this.grid.colModel;
33161 var colCount = cm.getColumnCount();
33162 for(var i = 0; i < colCount; i++){
33163 renderers[i] = cm.getRenderer(i);
33168 getColumnIds : function(){
33170 var cm = this.grid.colModel;
33171 var colCount = cm.getColumnCount();
33172 for(var i = 0; i < colCount; i++){
33173 ids[i] = cm.getColumnId(i);
33178 getDataIndexes : function(){
33179 if(!this.indexMap){
33180 this.indexMap = this.buildIndexMap();
33182 return this.indexMap.colToData;
33185 getColumnIndexByDataIndex : function(dataIndex){
33186 if(!this.indexMap){
33187 this.indexMap = this.buildIndexMap();
33189 return this.indexMap.dataToCol[dataIndex];
33193 * Set a css style for a column dynamically.
33194 * @param {Number} colIndex The index of the column
33195 * @param {String} name The css property name
33196 * @param {String} value The css value
33198 setCSSStyle : function(colIndex, name, value){
33199 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33200 Roo.util.CSS.updateRule(selector, name, value);
33203 generateRules : function(cm){
33204 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33205 Roo.util.CSS.removeStyleSheet(rulesId);
33206 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33207 var cid = cm.getColumnId(i);
33208 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33209 this.tdSelector, cid, " {\n}\n",
33210 this.hdSelector, cid, " {\n}\n",
33211 this.splitSelector, cid, " {\n}\n");
33213 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33217 * Ext JS Library 1.1.1
33218 * Copyright(c) 2006-2007, Ext JS, LLC.
33220 * Originally Released Under LGPL - original licence link has changed is not relivant.
33223 * <script type="text/javascript">
33227 // This is a support class used internally by the Grid components
33228 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33230 this.view = grid.getView();
33231 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33232 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33234 this.setHandleElId(Roo.id(hd));
33235 this.setOuterHandleElId(Roo.id(hd2));
33237 this.scroll = false;
33239 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33241 getDragData : function(e){
33242 var t = Roo.lib.Event.getTarget(e);
33243 var h = this.view.findHeaderCell(t);
33245 return {ddel: h.firstChild, header:h};
33250 onInitDrag : function(e){
33251 this.view.headersDisabled = true;
33252 var clone = this.dragData.ddel.cloneNode(true);
33253 clone.id = Roo.id();
33254 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33255 this.proxy.update(clone);
33259 afterValidDrop : function(){
33261 setTimeout(function(){
33262 v.headersDisabled = false;
33266 afterInvalidDrop : function(){
33268 setTimeout(function(){
33269 v.headersDisabled = false;
33275 * Ext JS Library 1.1.1
33276 * Copyright(c) 2006-2007, Ext JS, LLC.
33278 * Originally Released Under LGPL - original licence link has changed is not relivant.
33281 * <script type="text/javascript">
33284 // This is a support class used internally by the Grid components
33285 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33287 this.view = grid.getView();
33288 // split the proxies so they don't interfere with mouse events
33289 this.proxyTop = Roo.DomHelper.append(document.body, {
33290 cls:"col-move-top", html:" "
33292 this.proxyBottom = Roo.DomHelper.append(document.body, {
33293 cls:"col-move-bottom", html:" "
33295 this.proxyTop.hide = this.proxyBottom.hide = function(){
33296 this.setLeftTop(-100,-100);
33297 this.setStyle("visibility", "hidden");
33299 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33300 // temporarily disabled
33301 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33302 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33304 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33305 proxyOffsets : [-4, -9],
33306 fly: Roo.Element.fly,
33308 getTargetFromEvent : function(e){
33309 var t = Roo.lib.Event.getTarget(e);
33310 var cindex = this.view.findCellIndex(t);
33311 if(cindex !== false){
33312 return this.view.getHeaderCell(cindex);
33317 nextVisible : function(h){
33318 var v = this.view, cm = this.grid.colModel;
33321 if(!cm.isHidden(v.getCellIndex(h))){
33329 prevVisible : function(h){
33330 var v = this.view, cm = this.grid.colModel;
33333 if(!cm.isHidden(v.getCellIndex(h))){
33341 positionIndicator : function(h, n, e){
33342 var x = Roo.lib.Event.getPageX(e);
33343 var r = Roo.lib.Dom.getRegion(n.firstChild);
33344 var px, pt, py = r.top + this.proxyOffsets[1];
33345 if((r.right - x) <= (r.right-r.left)/2){
33346 px = r.right+this.view.borderWidth;
33352 var oldIndex = this.view.getCellIndex(h);
33353 var newIndex = this.view.getCellIndex(n);
33355 if(this.grid.colModel.isFixed(newIndex)){
33359 var locked = this.grid.colModel.isLocked(newIndex);
33364 if(oldIndex < newIndex){
33367 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33370 px += this.proxyOffsets[0];
33371 this.proxyTop.setLeftTop(px, py);
33372 this.proxyTop.show();
33373 if(!this.bottomOffset){
33374 this.bottomOffset = this.view.mainHd.getHeight();
33376 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33377 this.proxyBottom.show();
33381 onNodeEnter : function(n, dd, e, data){
33382 if(data.header != n){
33383 this.positionIndicator(data.header, n, e);
33387 onNodeOver : function(n, dd, e, data){
33388 var result = false;
33389 if(data.header != n){
33390 result = this.positionIndicator(data.header, n, e);
33393 this.proxyTop.hide();
33394 this.proxyBottom.hide();
33396 return result ? this.dropAllowed : this.dropNotAllowed;
33399 onNodeOut : function(n, dd, e, data){
33400 this.proxyTop.hide();
33401 this.proxyBottom.hide();
33404 onNodeDrop : function(n, dd, e, data){
33405 var h = data.header;
33407 var cm = this.grid.colModel;
33408 var x = Roo.lib.Event.getPageX(e);
33409 var r = Roo.lib.Dom.getRegion(n.firstChild);
33410 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33411 var oldIndex = this.view.getCellIndex(h);
33412 var newIndex = this.view.getCellIndex(n);
33413 var locked = cm.isLocked(newIndex);
33417 if(oldIndex < newIndex){
33420 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33423 cm.setLocked(oldIndex, locked, true);
33424 cm.moveColumn(oldIndex, newIndex);
33425 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33433 * Ext JS Library 1.1.1
33434 * Copyright(c) 2006-2007, Ext JS, LLC.
33436 * Originally Released Under LGPL - original licence link has changed is not relivant.
33439 * <script type="text/javascript">
33443 * @class Roo.grid.GridView
33444 * @extends Roo.util.Observable
33447 * @param {Object} config
33449 Roo.grid.GridView = function(config){
33450 Roo.grid.GridView.superclass.constructor.call(this);
33453 Roo.apply(this, config);
33456 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33458 unselectable : 'unselectable="on"',
33459 unselectableCls : 'x-unselectable',
33462 rowClass : "x-grid-row",
33464 cellClass : "x-grid-col",
33466 tdClass : "x-grid-td",
33468 hdClass : "x-grid-hd",
33470 splitClass : "x-grid-split",
33472 sortClasses : ["sort-asc", "sort-desc"],
33474 enableMoveAnim : false,
33478 dh : Roo.DomHelper,
33480 fly : Roo.Element.fly,
33482 css : Roo.util.CSS,
33488 scrollIncrement : 22,
33490 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33492 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33494 bind : function(ds, cm){
33496 this.ds.un("load", this.onLoad, this);
33497 this.ds.un("datachanged", this.onDataChange, this);
33498 this.ds.un("add", this.onAdd, this);
33499 this.ds.un("remove", this.onRemove, this);
33500 this.ds.un("update", this.onUpdate, this);
33501 this.ds.un("clear", this.onClear, this);
33504 ds.on("load", this.onLoad, this);
33505 ds.on("datachanged", this.onDataChange, this);
33506 ds.on("add", this.onAdd, this);
33507 ds.on("remove", this.onRemove, this);
33508 ds.on("update", this.onUpdate, this);
33509 ds.on("clear", this.onClear, this);
33514 this.cm.un("widthchange", this.onColWidthChange, this);
33515 this.cm.un("headerchange", this.onHeaderChange, this);
33516 this.cm.un("hiddenchange", this.onHiddenChange, this);
33517 this.cm.un("columnmoved", this.onColumnMove, this);
33518 this.cm.un("columnlockchange", this.onColumnLock, this);
33521 this.generateRules(cm);
33522 cm.on("widthchange", this.onColWidthChange, this);
33523 cm.on("headerchange", this.onHeaderChange, this);
33524 cm.on("hiddenchange", this.onHiddenChange, this);
33525 cm.on("columnmoved", this.onColumnMove, this);
33526 cm.on("columnlockchange", this.onColumnLock, this);
33531 init: function(grid){
33532 Roo.grid.GridView.superclass.init.call(this, grid);
33534 this.bind(grid.dataSource, grid.colModel);
33536 grid.on("headerclick", this.handleHeaderClick, this);
33538 if(grid.trackMouseOver){
33539 grid.on("mouseover", this.onRowOver, this);
33540 grid.on("mouseout", this.onRowOut, this);
33542 grid.cancelTextSelection = function(){};
33543 this.gridId = grid.id;
33545 var tpls = this.templates || {};
33548 tpls.master = new Roo.Template(
33549 '<div class="x-grid" hidefocus="true">',
33550 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33551 '<div class="x-grid-topbar"></div>',
33552 '<div class="x-grid-scroller"><div></div></div>',
33553 '<div class="x-grid-locked">',
33554 '<div class="x-grid-header">{lockedHeader}</div>',
33555 '<div class="x-grid-body">{lockedBody}</div>',
33557 '<div class="x-grid-viewport">',
33558 '<div class="x-grid-header">{header}</div>',
33559 '<div class="x-grid-body">{body}</div>',
33561 '<div class="x-grid-bottombar"></div>',
33563 '<div class="x-grid-resize-proxy"> </div>',
33566 tpls.master.disableformats = true;
33570 tpls.header = new Roo.Template(
33571 '<table border="0" cellspacing="0" cellpadding="0">',
33572 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33575 tpls.header.disableformats = true;
33577 tpls.header.compile();
33580 tpls.hcell = new Roo.Template(
33581 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33582 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33585 tpls.hcell.disableFormats = true;
33587 tpls.hcell.compile();
33590 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33591 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33592 tpls.hsplit.disableFormats = true;
33594 tpls.hsplit.compile();
33597 tpls.body = new Roo.Template(
33598 '<table border="0" cellspacing="0" cellpadding="0">',
33599 "<tbody>{rows}</tbody>",
33602 tpls.body.disableFormats = true;
33604 tpls.body.compile();
33607 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33608 tpls.row.disableFormats = true;
33610 tpls.row.compile();
33613 tpls.cell = new Roo.Template(
33614 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33615 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33616 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33619 tpls.cell.disableFormats = true;
33621 tpls.cell.compile();
33623 this.templates = tpls;
33626 // remap these for backwards compat
33627 onColWidthChange : function(){
33628 this.updateColumns.apply(this, arguments);
33630 onHeaderChange : function(){
33631 this.updateHeaders.apply(this, arguments);
33633 onHiddenChange : function(){
33634 this.handleHiddenChange.apply(this, arguments);
33636 onColumnMove : function(){
33637 this.handleColumnMove.apply(this, arguments);
33639 onColumnLock : function(){
33640 this.handleLockChange.apply(this, arguments);
33643 onDataChange : function(){
33645 this.updateHeaderSortState();
33648 onClear : function(){
33652 onUpdate : function(ds, record){
33653 this.refreshRow(record);
33656 refreshRow : function(record){
33657 var ds = this.ds, index;
33658 if(typeof record == 'number'){
33660 record = ds.getAt(index);
33662 index = ds.indexOf(record);
33664 this.insertRows(ds, index, index, true);
33665 this.onRemove(ds, record, index+1, true);
33666 this.syncRowHeights(index, index);
33668 this.fireEvent("rowupdated", this, index, record);
33671 onAdd : function(ds, records, index){
33672 this.insertRows(ds, index, index + (records.length-1));
33675 onRemove : function(ds, record, index, isUpdate){
33676 if(isUpdate !== true){
33677 this.fireEvent("beforerowremoved", this, index, record);
33679 var bt = this.getBodyTable(), lt = this.getLockedTable();
33680 if(bt.rows[index]){
33681 bt.firstChild.removeChild(bt.rows[index]);
33683 if(lt.rows[index]){
33684 lt.firstChild.removeChild(lt.rows[index]);
33686 if(isUpdate !== true){
33687 this.stripeRows(index);
33688 this.syncRowHeights(index, index);
33690 this.fireEvent("rowremoved", this, index, record);
33694 onLoad : function(){
33695 this.scrollToTop();
33699 * Scrolls the grid to the top
33701 scrollToTop : function(){
33703 this.scroller.dom.scrollTop = 0;
33709 * Gets a panel in the header of the grid that can be used for toolbars etc.
33710 * After modifying the contents of this panel a call to grid.autoSize() may be
33711 * required to register any changes in size.
33712 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33713 * @return Roo.Element
33715 getHeaderPanel : function(doShow){
33717 this.headerPanel.show();
33719 return this.headerPanel;
33723 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33724 * After modifying the contents of this panel a call to grid.autoSize() may be
33725 * required to register any changes in size.
33726 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33727 * @return Roo.Element
33729 getFooterPanel : function(doShow){
33731 this.footerPanel.show();
33733 return this.footerPanel;
33736 initElements : function(){
33737 var E = Roo.Element;
33738 var el = this.grid.getGridEl().dom.firstChild;
33739 var cs = el.childNodes;
33741 this.el = new E(el);
33743 this.focusEl = new E(el.firstChild);
33744 this.focusEl.swallowEvent("click", true);
33746 this.headerPanel = new E(cs[1]);
33747 this.headerPanel.enableDisplayMode("block");
33749 this.scroller = new E(cs[2]);
33750 this.scrollSizer = new E(this.scroller.dom.firstChild);
33752 this.lockedWrap = new E(cs[3]);
33753 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33754 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33756 this.mainWrap = new E(cs[4]);
33757 this.mainHd = new E(this.mainWrap.dom.firstChild);
33758 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33760 this.footerPanel = new E(cs[5]);
33761 this.footerPanel.enableDisplayMode("block");
33763 this.resizeProxy = new E(cs[6]);
33765 this.headerSelector = String.format(
33766 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33767 this.lockedHd.id, this.mainHd.id
33770 this.splitterSelector = String.format(
33771 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33772 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33775 idToCssName : function(s)
33777 return s.replace(/[^a-z0-9]+/ig, '-');
33780 getHeaderCell : function(index){
33781 return Roo.DomQuery.select(this.headerSelector)[index];
33784 getHeaderCellMeasure : function(index){
33785 return this.getHeaderCell(index).firstChild;
33788 getHeaderCellText : function(index){
33789 return this.getHeaderCell(index).firstChild.firstChild;
33792 getLockedTable : function(){
33793 return this.lockedBody.dom.firstChild;
33796 getBodyTable : function(){
33797 return this.mainBody.dom.firstChild;
33800 getLockedRow : function(index){
33801 return this.getLockedTable().rows[index];
33804 getRow : function(index){
33805 return this.getBodyTable().rows[index];
33808 getRowComposite : function(index){
33810 this.rowEl = new Roo.CompositeElementLite();
33812 var els = [], lrow, mrow;
33813 if(lrow = this.getLockedRow(index)){
33816 if(mrow = this.getRow(index)){
33819 this.rowEl.elements = els;
33823 * Gets the 'td' of the cell
33825 * @param {Integer} rowIndex row to select
33826 * @param {Integer} colIndex column to select
33830 getCell : function(rowIndex, colIndex){
33831 var locked = this.cm.getLockedCount();
33833 if(colIndex < locked){
33834 source = this.lockedBody.dom.firstChild;
33836 source = this.mainBody.dom.firstChild;
33837 colIndex -= locked;
33839 return source.rows[rowIndex].childNodes[colIndex];
33842 getCellText : function(rowIndex, colIndex){
33843 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33846 getCellBox : function(cell){
33847 var b = this.fly(cell).getBox();
33848 if(Roo.isOpera){ // opera fails to report the Y
33849 b.y = cell.offsetTop + this.mainBody.getY();
33854 getCellIndex : function(cell){
33855 var id = String(cell.className).match(this.cellRE);
33857 return parseInt(id[1], 10);
33862 findHeaderIndex : function(n){
33863 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33864 return r ? this.getCellIndex(r) : false;
33867 findHeaderCell : function(n){
33868 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33869 return r ? r : false;
33872 findRowIndex : function(n){
33876 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33877 return r ? r.rowIndex : false;
33880 findCellIndex : function(node){
33881 var stop = this.el.dom;
33882 while(node && node != stop){
33883 if(this.findRE.test(node.className)){
33884 return this.getCellIndex(node);
33886 node = node.parentNode;
33891 getColumnId : function(index){
33892 return this.cm.getColumnId(index);
33895 getSplitters : function()
33897 if(this.splitterSelector){
33898 return Roo.DomQuery.select(this.splitterSelector);
33904 getSplitter : function(index){
33905 return this.getSplitters()[index];
33908 onRowOver : function(e, t){
33910 if((row = this.findRowIndex(t)) !== false){
33911 this.getRowComposite(row).addClass("x-grid-row-over");
33915 onRowOut : function(e, t){
33917 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33918 this.getRowComposite(row).removeClass("x-grid-row-over");
33922 renderHeaders : function(){
33924 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33925 var cb = [], lb = [], sb = [], lsb = [], p = {};
33926 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33927 p.cellId = "x-grid-hd-0-" + i;
33928 p.splitId = "x-grid-csplit-0-" + i;
33929 p.id = cm.getColumnId(i);
33930 p.value = cm.getColumnHeader(i) || "";
33931 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33932 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33933 if(!cm.isLocked(i)){
33934 cb[cb.length] = ct.apply(p);
33935 sb[sb.length] = st.apply(p);
33937 lb[lb.length] = ct.apply(p);
33938 lsb[lsb.length] = st.apply(p);
33941 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33942 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33945 updateHeaders : function(){
33946 var html = this.renderHeaders();
33947 this.lockedHd.update(html[0]);
33948 this.mainHd.update(html[1]);
33952 * Focuses the specified row.
33953 * @param {Number} row The row index
33955 focusRow : function(row)
33957 //Roo.log('GridView.focusRow');
33958 var x = this.scroller.dom.scrollLeft;
33959 this.focusCell(row, 0, false);
33960 this.scroller.dom.scrollLeft = x;
33964 * Focuses the specified cell.
33965 * @param {Number} row The row index
33966 * @param {Number} col The column index
33967 * @param {Boolean} hscroll false to disable horizontal scrolling
33969 focusCell : function(row, col, hscroll)
33971 //Roo.log('GridView.focusCell');
33972 var el = this.ensureVisible(row, col, hscroll);
33973 this.focusEl.alignTo(el, "tl-tl");
33975 this.focusEl.focus();
33977 this.focusEl.focus.defer(1, this.focusEl);
33982 * Scrolls the specified cell into view
33983 * @param {Number} row The row index
33984 * @param {Number} col The column index
33985 * @param {Boolean} hscroll false to disable horizontal scrolling
33987 ensureVisible : function(row, col, hscroll)
33989 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33990 //return null; //disable for testing.
33991 if(typeof row != "number"){
33992 row = row.rowIndex;
33994 if(row < 0 && row >= this.ds.getCount()){
33997 col = (col !== undefined ? col : 0);
33998 var cm = this.grid.colModel;
33999 while(cm.isHidden(col)){
34003 var el = this.getCell(row, col);
34007 var c = this.scroller.dom;
34009 var ctop = parseInt(el.offsetTop, 10);
34010 var cleft = parseInt(el.offsetLeft, 10);
34011 var cbot = ctop + el.offsetHeight;
34012 var cright = cleft + el.offsetWidth;
34014 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34015 var stop = parseInt(c.scrollTop, 10);
34016 var sleft = parseInt(c.scrollLeft, 10);
34017 var sbot = stop + ch;
34018 var sright = sleft + c.clientWidth;
34020 Roo.log('GridView.ensureVisible:' +
34022 ' c.clientHeight:' + c.clientHeight +
34023 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34031 c.scrollTop = ctop;
34032 //Roo.log("set scrolltop to ctop DISABLE?");
34033 }else if(cbot > sbot){
34034 //Roo.log("set scrolltop to cbot-ch");
34035 c.scrollTop = cbot-ch;
34038 if(hscroll !== false){
34040 c.scrollLeft = cleft;
34041 }else if(cright > sright){
34042 c.scrollLeft = cright-c.clientWidth;
34049 updateColumns : function(){
34050 this.grid.stopEditing();
34051 var cm = this.grid.colModel, colIds = this.getColumnIds();
34052 //var totalWidth = cm.getTotalWidth();
34054 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34055 //if(cm.isHidden(i)) continue;
34056 var w = cm.getColumnWidth(i);
34057 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34058 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34060 this.updateSplitters();
34063 generateRules : function(cm){
34064 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34065 Roo.util.CSS.removeStyleSheet(rulesId);
34066 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34067 var cid = cm.getColumnId(i);
34069 if(cm.config[i].align){
34070 align = 'text-align:'+cm.config[i].align+';';
34073 if(cm.isHidden(i)){
34074 hidden = 'display:none;';
34076 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34078 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34079 this.hdSelector, cid, " {\n", align, width, "}\n",
34080 this.tdSelector, cid, " {\n",hidden,"\n}\n",
34081 this.splitSelector, cid, " {\n", hidden , "\n}\n");
34083 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34086 updateSplitters : function(){
34087 var cm = this.cm, s = this.getSplitters();
34088 if(s){ // splitters not created yet
34089 var pos = 0, locked = true;
34090 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34091 if(cm.isHidden(i)) {
34094 var w = cm.getColumnWidth(i); // make sure it's a number
34095 if(!cm.isLocked(i) && locked){
34100 s[i].style.left = (pos-this.splitOffset) + "px";
34105 handleHiddenChange : function(colModel, colIndex, hidden){
34107 this.hideColumn(colIndex);
34109 this.unhideColumn(colIndex);
34113 hideColumn : function(colIndex){
34114 var cid = this.getColumnId(colIndex);
34115 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34116 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34118 this.updateHeaders();
34120 this.updateSplitters();
34124 unhideColumn : function(colIndex){
34125 var cid = this.getColumnId(colIndex);
34126 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34127 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34130 this.updateHeaders();
34132 this.updateSplitters();
34136 insertRows : function(dm, firstRow, lastRow, isUpdate){
34137 if(firstRow == 0 && lastRow == dm.getCount()-1){
34141 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34143 var s = this.getScrollState();
34144 var markup = this.renderRows(firstRow, lastRow);
34145 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34146 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34147 this.restoreScroll(s);
34149 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34150 this.syncRowHeights(firstRow, lastRow);
34151 this.stripeRows(firstRow);
34157 bufferRows : function(markup, target, index){
34158 var before = null, trows = target.rows, tbody = target.tBodies[0];
34159 if(index < trows.length){
34160 before = trows[index];
34162 var b = document.createElement("div");
34163 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34164 var rows = b.firstChild.rows;
34165 for(var i = 0, len = rows.length; i < len; i++){
34167 tbody.insertBefore(rows[0], before);
34169 tbody.appendChild(rows[0]);
34176 deleteRows : function(dm, firstRow, lastRow){
34177 if(dm.getRowCount()<1){
34178 this.fireEvent("beforerefresh", this);
34179 this.mainBody.update("");
34180 this.lockedBody.update("");
34181 this.fireEvent("refresh", this);
34183 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34184 var bt = this.getBodyTable();
34185 var tbody = bt.firstChild;
34186 var rows = bt.rows;
34187 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34188 tbody.removeChild(rows[firstRow]);
34190 this.stripeRows(firstRow);
34191 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34195 updateRows : function(dataSource, firstRow, lastRow){
34196 var s = this.getScrollState();
34198 this.restoreScroll(s);
34201 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34205 this.updateHeaderSortState();
34208 getScrollState : function(){
34210 var sb = this.scroller.dom;
34211 return {left: sb.scrollLeft, top: sb.scrollTop};
34214 stripeRows : function(startRow){
34215 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34218 startRow = startRow || 0;
34219 var rows = this.getBodyTable().rows;
34220 var lrows = this.getLockedTable().rows;
34221 var cls = ' x-grid-row-alt ';
34222 for(var i = startRow, len = rows.length; i < len; i++){
34223 var row = rows[i], lrow = lrows[i];
34224 var isAlt = ((i+1) % 2 == 0);
34225 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34226 if(isAlt == hasAlt){
34230 row.className += " x-grid-row-alt";
34232 row.className = row.className.replace("x-grid-row-alt", "");
34235 lrow.className = row.className;
34240 restoreScroll : function(state){
34241 //Roo.log('GridView.restoreScroll');
34242 var sb = this.scroller.dom;
34243 sb.scrollLeft = state.left;
34244 sb.scrollTop = state.top;
34248 syncScroll : function(){
34249 //Roo.log('GridView.syncScroll');
34250 var sb = this.scroller.dom;
34251 var sh = this.mainHd.dom;
34252 var bs = this.mainBody.dom;
34253 var lv = this.lockedBody.dom;
34254 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34255 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34258 handleScroll : function(e){
34260 var sb = this.scroller.dom;
34261 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34265 handleWheel : function(e){
34266 var d = e.getWheelDelta();
34267 this.scroller.dom.scrollTop -= d*22;
34268 // set this here to prevent jumpy scrolling on large tables
34269 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34273 renderRows : function(startRow, endRow){
34274 // pull in all the crap needed to render rows
34275 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34276 var colCount = cm.getColumnCount();
34278 if(ds.getCount() < 1){
34282 // build a map for all the columns
34284 for(var i = 0; i < colCount; i++){
34285 var name = cm.getDataIndex(i);
34287 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34288 renderer : cm.getRenderer(i),
34289 id : cm.getColumnId(i),
34290 locked : cm.isLocked(i),
34291 has_editor : cm.isCellEditable(i)
34295 startRow = startRow || 0;
34296 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34298 // records to render
34299 var rs = ds.getRange(startRow, endRow);
34301 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34304 // As much as I hate to duplicate code, this was branched because FireFox really hates
34305 // [].join("") on strings. The performance difference was substantial enough to
34306 // branch this function
34307 doRender : Roo.isGecko ?
34308 function(cs, rs, ds, startRow, colCount, stripe){
34309 var ts = this.templates, ct = ts.cell, rt = ts.row;
34311 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34313 var hasListener = this.grid.hasListener('rowclass');
34315 for(var j = 0, len = rs.length; j < len; j++){
34316 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34317 for(var i = 0; i < colCount; i++){
34319 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34321 p.css = p.attr = "";
34322 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34323 if(p.value == undefined || p.value === "") {
34324 p.value = " ";
34327 p.css += ' x-grid-editable-cell';
34329 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34330 p.css += ' x-grid-dirty-cell';
34332 var markup = ct.apply(p);
34340 if(stripe && ((rowIndex+1) % 2 == 0)){
34341 alt.push("x-grid-row-alt")
34344 alt.push( " x-grid-dirty-row");
34347 if(this.getRowClass){
34348 alt.push(this.getRowClass(r, rowIndex));
34354 rowIndex : rowIndex,
34357 this.grid.fireEvent('rowclass', this, rowcfg);
34358 alt.push(rowcfg.rowClass);
34360 rp.alt = alt.join(" ");
34361 lbuf+= rt.apply(rp);
34363 buf+= rt.apply(rp);
34365 return [lbuf, buf];
34367 function(cs, rs, ds, startRow, colCount, stripe){
34368 var ts = this.templates, ct = ts.cell, rt = ts.row;
34370 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34371 var hasListener = this.grid.hasListener('rowclass');
34374 for(var j = 0, len = rs.length; j < len; j++){
34375 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34376 for(var i = 0; i < colCount; i++){
34378 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34380 p.css = p.attr = "";
34381 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34382 if(p.value == undefined || p.value === "") {
34383 p.value = " ";
34387 p.css += ' x-grid-editable-cell';
34389 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34390 p.css += ' x-grid-dirty-cell'
34393 var markup = ct.apply(p);
34395 cb[cb.length] = markup;
34397 lcb[lcb.length] = markup;
34401 if(stripe && ((rowIndex+1) % 2 == 0)){
34402 alt.push( "x-grid-row-alt");
34405 alt.push(" x-grid-dirty-row");
34408 if(this.getRowClass){
34409 alt.push( this.getRowClass(r, rowIndex));
34415 rowIndex : rowIndex,
34418 this.grid.fireEvent('rowclass', this, rowcfg);
34419 alt.push(rowcfg.rowClass);
34422 rp.alt = alt.join(" ");
34423 rp.cells = lcb.join("");
34424 lbuf[lbuf.length] = rt.apply(rp);
34425 rp.cells = cb.join("");
34426 buf[buf.length] = rt.apply(rp);
34428 return [lbuf.join(""), buf.join("")];
34431 renderBody : function(){
34432 var markup = this.renderRows();
34433 var bt = this.templates.body;
34434 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34438 * Refreshes the grid
34439 * @param {Boolean} headersToo
34441 refresh : function(headersToo){
34442 this.fireEvent("beforerefresh", this);
34443 this.grid.stopEditing();
34444 var result = this.renderBody();
34445 this.lockedBody.update(result[0]);
34446 this.mainBody.update(result[1]);
34447 if(headersToo === true){
34448 this.updateHeaders();
34449 this.updateColumns();
34450 this.updateSplitters();
34451 this.updateHeaderSortState();
34453 this.syncRowHeights();
34455 this.fireEvent("refresh", this);
34458 handleColumnMove : function(cm, oldIndex, newIndex){
34459 this.indexMap = null;
34460 var s = this.getScrollState();
34461 this.refresh(true);
34462 this.restoreScroll(s);
34463 this.afterMove(newIndex);
34466 afterMove : function(colIndex){
34467 if(this.enableMoveAnim && Roo.enableFx){
34468 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34470 // if multisort - fix sortOrder, and reload..
34471 if (this.grid.dataSource.multiSort) {
34472 // the we can call sort again..
34473 var dm = this.grid.dataSource;
34474 var cm = this.grid.colModel;
34476 for(var i = 0; i < cm.config.length; i++ ) {
34478 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34479 continue; // dont' bother, it's not in sort list or being set.
34482 so.push(cm.config[i].dataIndex);
34485 dm.load(dm.lastOptions);
34492 updateCell : function(dm, rowIndex, dataIndex){
34493 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34494 if(typeof colIndex == "undefined"){ // not present in grid
34497 var cm = this.grid.colModel;
34498 var cell = this.getCell(rowIndex, colIndex);
34499 var cellText = this.getCellText(rowIndex, colIndex);
34502 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34503 id : cm.getColumnId(colIndex),
34504 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34506 var renderer = cm.getRenderer(colIndex);
34507 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34508 if(typeof val == "undefined" || val === "") {
34511 cellText.innerHTML = val;
34512 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34513 this.syncRowHeights(rowIndex, rowIndex);
34516 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34518 if(this.grid.autoSizeHeaders){
34519 var h = this.getHeaderCellMeasure(colIndex);
34520 maxWidth = Math.max(maxWidth, h.scrollWidth);
34523 if(this.cm.isLocked(colIndex)){
34524 tb = this.getLockedTable();
34527 tb = this.getBodyTable();
34528 index = colIndex - this.cm.getLockedCount();
34531 var rows = tb.rows;
34532 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34533 for(var i = 0; i < stopIndex; i++){
34534 var cell = rows[i].childNodes[index].firstChild;
34535 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34538 return maxWidth + /*margin for error in IE*/ 5;
34541 * Autofit a column to its content.
34542 * @param {Number} colIndex
34543 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34545 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34546 if(this.cm.isHidden(colIndex)){
34547 return; // can't calc a hidden column
34550 var cid = this.cm.getColumnId(colIndex);
34551 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34552 if(this.grid.autoSizeHeaders){
34553 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34556 var newWidth = this.calcColumnWidth(colIndex);
34557 this.cm.setColumnWidth(colIndex,
34558 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34559 if(!suppressEvent){
34560 this.grid.fireEvent("columnresize", colIndex, newWidth);
34565 * Autofits all columns to their content and then expands to fit any extra space in the grid
34567 autoSizeColumns : function(){
34568 var cm = this.grid.colModel;
34569 var colCount = cm.getColumnCount();
34570 for(var i = 0; i < colCount; i++){
34571 this.autoSizeColumn(i, true, true);
34573 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34576 this.updateColumns();
34582 * Autofits all columns to the grid's width proportionate with their current size
34583 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34585 fitColumns : function(reserveScrollSpace){
34586 var cm = this.grid.colModel;
34587 var colCount = cm.getColumnCount();
34591 for (i = 0; i < colCount; i++){
34592 if(!cm.isHidden(i) && !cm.isFixed(i)){
34593 w = cm.getColumnWidth(i);
34599 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34600 if(reserveScrollSpace){
34603 var frac = (avail - cm.getTotalWidth())/width;
34604 while (cols.length){
34607 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34609 this.updateColumns();
34613 onRowSelect : function(rowIndex){
34614 var row = this.getRowComposite(rowIndex);
34615 row.addClass("x-grid-row-selected");
34618 onRowDeselect : function(rowIndex){
34619 var row = this.getRowComposite(rowIndex);
34620 row.removeClass("x-grid-row-selected");
34623 onCellSelect : function(row, col){
34624 var cell = this.getCell(row, col);
34626 Roo.fly(cell).addClass("x-grid-cell-selected");
34630 onCellDeselect : function(row, col){
34631 var cell = this.getCell(row, col);
34633 Roo.fly(cell).removeClass("x-grid-cell-selected");
34637 updateHeaderSortState : function(){
34639 // sort state can be single { field: xxx, direction : yyy}
34640 // or { xxx=>ASC , yyy : DESC ..... }
34643 if (!this.ds.multiSort) {
34644 var state = this.ds.getSortState();
34648 mstate[state.field] = state.direction;
34649 // FIXME... - this is not used here.. but might be elsewhere..
34650 this.sortState = state;
34653 mstate = this.ds.sortToggle;
34655 //remove existing sort classes..
34657 var sc = this.sortClasses;
34658 var hds = this.el.select(this.headerSelector).removeClass(sc);
34660 for(var f in mstate) {
34662 var sortColumn = this.cm.findColumnIndex(f);
34664 if(sortColumn != -1){
34665 var sortDir = mstate[f];
34666 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34675 handleHeaderClick : function(g, index,e){
34677 Roo.log("header click");
34680 // touch events on header are handled by context
34681 this.handleHdCtx(g,index,e);
34686 if(this.headersDisabled){
34689 var dm = g.dataSource, cm = g.colModel;
34690 if(!cm.isSortable(index)){
34695 if (dm.multiSort) {
34696 // update the sortOrder
34698 for(var i = 0; i < cm.config.length; i++ ) {
34700 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34701 continue; // dont' bother, it's not in sort list or being set.
34704 so.push(cm.config[i].dataIndex);
34710 dm.sort(cm.getDataIndex(index));
34714 destroy : function(){
34716 this.colMenu.removeAll();
34717 Roo.menu.MenuMgr.unregister(this.colMenu);
34718 this.colMenu.getEl().remove();
34719 delete this.colMenu;
34722 this.hmenu.removeAll();
34723 Roo.menu.MenuMgr.unregister(this.hmenu);
34724 this.hmenu.getEl().remove();
34727 if(this.grid.enableColumnMove){
34728 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34730 for(var dd in dds){
34731 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34732 var elid = dds[dd].dragElId;
34734 Roo.get(elid).remove();
34735 } else if(dds[dd].config.isTarget){
34736 dds[dd].proxyTop.remove();
34737 dds[dd].proxyBottom.remove();
34740 if(Roo.dd.DDM.locationCache[dd]){
34741 delete Roo.dd.DDM.locationCache[dd];
34744 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34747 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34748 this.bind(null, null);
34749 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34752 handleLockChange : function(){
34753 this.refresh(true);
34756 onDenyColumnLock : function(){
34760 onDenyColumnHide : function(){
34764 handleHdMenuClick : function(item){
34765 var index = this.hdCtxIndex;
34766 var cm = this.cm, ds = this.ds;
34769 ds.sort(cm.getDataIndex(index), "ASC");
34772 ds.sort(cm.getDataIndex(index), "DESC");
34775 var lc = cm.getLockedCount();
34776 if(cm.getColumnCount(true) <= lc+1){
34777 this.onDenyColumnLock();
34781 cm.setLocked(index, true, true);
34782 cm.moveColumn(index, lc);
34783 this.grid.fireEvent("columnmove", index, lc);
34785 cm.setLocked(index, true);
34789 var lc = cm.getLockedCount();
34790 if((lc-1) != index){
34791 cm.setLocked(index, false, true);
34792 cm.moveColumn(index, lc-1);
34793 this.grid.fireEvent("columnmove", index, lc-1);
34795 cm.setLocked(index, false);
34798 case 'wider': // used to expand cols on touch..
34800 var cw = cm.getColumnWidth(index);
34801 cw += (item.id == 'wider' ? 1 : -1) * 50;
34802 cw = Math.max(0, cw);
34803 cw = Math.min(cw,4000);
34804 cm.setColumnWidth(index, cw);
34808 index = cm.getIndexById(item.id.substr(4));
34810 if(item.checked && cm.getColumnCount(true) <= 1){
34811 this.onDenyColumnHide();
34814 cm.setHidden(index, item.checked);
34820 beforeColMenuShow : function(){
34821 var cm = this.cm, colCount = cm.getColumnCount();
34822 this.colMenu.removeAll();
34823 for(var i = 0; i < colCount; i++){
34824 this.colMenu.add(new Roo.menu.CheckItem({
34825 id: "col-"+cm.getColumnId(i),
34826 text: cm.getColumnHeader(i),
34827 checked: !cm.isHidden(i),
34833 handleHdCtx : function(g, index, e){
34835 var hd = this.getHeaderCell(index);
34836 this.hdCtxIndex = index;
34837 var ms = this.hmenu.items, cm = this.cm;
34838 ms.get("asc").setDisabled(!cm.isSortable(index));
34839 ms.get("desc").setDisabled(!cm.isSortable(index));
34840 if(this.grid.enableColLock !== false){
34841 ms.get("lock").setDisabled(cm.isLocked(index));
34842 ms.get("unlock").setDisabled(!cm.isLocked(index));
34844 this.hmenu.show(hd, "tl-bl");
34847 handleHdOver : function(e){
34848 var hd = this.findHeaderCell(e.getTarget());
34849 if(hd && !this.headersDisabled){
34850 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34851 this.fly(hd).addClass("x-grid-hd-over");
34856 handleHdOut : function(e){
34857 var hd = this.findHeaderCell(e.getTarget());
34859 this.fly(hd).removeClass("x-grid-hd-over");
34863 handleSplitDblClick : function(e, t){
34864 var i = this.getCellIndex(t);
34865 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34866 this.autoSizeColumn(i, true);
34871 render : function(){
34874 var colCount = cm.getColumnCount();
34876 if(this.grid.monitorWindowResize === true){
34877 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34879 var header = this.renderHeaders();
34880 var body = this.templates.body.apply({rows:""});
34881 var html = this.templates.master.apply({
34884 lockedHeader: header[0],
34888 //this.updateColumns();
34890 this.grid.getGridEl().dom.innerHTML = html;
34892 this.initElements();
34894 // a kludge to fix the random scolling effect in webkit
34895 this.el.on("scroll", function() {
34896 this.el.dom.scrollTop=0; // hopefully not recursive..
34899 this.scroller.on("scroll", this.handleScroll, this);
34900 this.lockedBody.on("mousewheel", this.handleWheel, this);
34901 this.mainBody.on("mousewheel", this.handleWheel, this);
34903 this.mainHd.on("mouseover", this.handleHdOver, this);
34904 this.mainHd.on("mouseout", this.handleHdOut, this);
34905 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34906 {delegate: "."+this.splitClass});
34908 this.lockedHd.on("mouseover", this.handleHdOver, this);
34909 this.lockedHd.on("mouseout", this.handleHdOut, this);
34910 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34911 {delegate: "."+this.splitClass});
34913 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34914 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34917 this.updateSplitters();
34919 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34920 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34921 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34924 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34925 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34927 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34928 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34930 if(this.grid.enableColLock !== false){
34931 this.hmenu.add('-',
34932 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34933 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34937 this.hmenu.add('-',
34938 {id:"wider", text: this.columnsWiderText},
34939 {id:"narrow", text: this.columnsNarrowText }
34945 if(this.grid.enableColumnHide !== false){
34947 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34948 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34949 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34951 this.hmenu.add('-',
34952 {id:"columns", text: this.columnsText, menu: this.colMenu}
34955 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34957 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34960 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34961 this.dd = new Roo.grid.GridDragZone(this.grid, {
34962 ddGroup : this.grid.ddGroup || 'GridDD'
34968 for(var i = 0; i < colCount; i++){
34969 if(cm.isHidden(i)){
34970 this.hideColumn(i);
34972 if(cm.config[i].align){
34973 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34974 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34978 this.updateHeaderSortState();
34980 this.beforeInitialResize();
34983 // two part rendering gives faster view to the user
34984 this.renderPhase2.defer(1, this);
34987 renderPhase2 : function(){
34988 // render the rows now
34990 if(this.grid.autoSizeColumns){
34991 this.autoSizeColumns();
34995 beforeInitialResize : function(){
34999 onColumnSplitterMoved : function(i, w){
35000 this.userResized = true;
35001 var cm = this.grid.colModel;
35002 cm.setColumnWidth(i, w, true);
35003 var cid = cm.getColumnId(i);
35004 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35005 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35006 this.updateSplitters();
35008 this.grid.fireEvent("columnresize", i, w);
35011 syncRowHeights : function(startIndex, endIndex){
35012 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35013 startIndex = startIndex || 0;
35014 var mrows = this.getBodyTable().rows;
35015 var lrows = this.getLockedTable().rows;
35016 var len = mrows.length-1;
35017 endIndex = Math.min(endIndex || len, len);
35018 for(var i = startIndex; i <= endIndex; i++){
35019 var m = mrows[i], l = lrows[i];
35020 var h = Math.max(m.offsetHeight, l.offsetHeight);
35021 m.style.height = l.style.height = h + "px";
35026 layout : function(initialRender, is2ndPass){
35028 var auto = g.autoHeight;
35029 var scrollOffset = 16;
35030 var c = g.getGridEl(), cm = this.cm,
35031 expandCol = g.autoExpandColumn,
35033 //c.beginMeasure();
35035 if(!c.dom.offsetWidth){ // display:none?
35037 this.lockedWrap.show();
35038 this.mainWrap.show();
35043 var hasLock = this.cm.isLocked(0);
35045 var tbh = this.headerPanel.getHeight();
35046 var bbh = this.footerPanel.getHeight();
35049 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35050 var newHeight = ch + c.getBorderWidth("tb");
35052 newHeight = Math.min(g.maxHeight, newHeight);
35054 c.setHeight(newHeight);
35058 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35061 var s = this.scroller;
35063 var csize = c.getSize(true);
35065 this.el.setSize(csize.width, csize.height);
35067 this.headerPanel.setWidth(csize.width);
35068 this.footerPanel.setWidth(csize.width);
35070 var hdHeight = this.mainHd.getHeight();
35071 var vw = csize.width;
35072 var vh = csize.height - (tbh + bbh);
35076 var bt = this.getBodyTable();
35078 if(cm.getLockedCount() == cm.config.length){
35079 bt = this.getLockedTable();
35082 var ltWidth = hasLock ?
35083 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35085 var scrollHeight = bt.offsetHeight;
35086 var scrollWidth = ltWidth + bt.offsetWidth;
35087 var vscroll = false, hscroll = false;
35089 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35091 var lw = this.lockedWrap, mw = this.mainWrap;
35092 var lb = this.lockedBody, mb = this.mainBody;
35094 setTimeout(function(){
35095 var t = s.dom.offsetTop;
35096 var w = s.dom.clientWidth,
35097 h = s.dom.clientHeight;
35100 lw.setSize(ltWidth, h);
35102 mw.setLeftTop(ltWidth, t);
35103 mw.setSize(w-ltWidth, h);
35105 lb.setHeight(h-hdHeight);
35106 mb.setHeight(h-hdHeight);
35108 if(is2ndPass !== true && !gv.userResized && expandCol){
35109 // high speed resize without full column calculation
35111 var ci = cm.getIndexById(expandCol);
35113 ci = cm.findColumnIndex(expandCol);
35115 ci = Math.max(0, ci); // make sure it's got at least the first col.
35116 var expandId = cm.getColumnId(ci);
35117 var tw = cm.getTotalWidth(false);
35118 var currentWidth = cm.getColumnWidth(ci);
35119 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35120 if(currentWidth != cw){
35121 cm.setColumnWidth(ci, cw, true);
35122 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35123 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35124 gv.updateSplitters();
35125 gv.layout(false, true);
35137 onWindowResize : function(){
35138 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35144 appendFooter : function(parentEl){
35148 sortAscText : "Sort Ascending",
35149 sortDescText : "Sort Descending",
35150 lockText : "Lock Column",
35151 unlockText : "Unlock Column",
35152 columnsText : "Columns",
35154 columnsWiderText : "Wider",
35155 columnsNarrowText : "Thinner"
35159 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35160 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35161 this.proxy.el.addClass('x-grid3-col-dd');
35164 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35165 handleMouseDown : function(e){
35169 callHandleMouseDown : function(e){
35170 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35175 * Ext JS Library 1.1.1
35176 * Copyright(c) 2006-2007, Ext JS, LLC.
35178 * Originally Released Under LGPL - original licence link has changed is not relivant.
35181 * <script type="text/javascript">
35185 // This is a support class used internally by the Grid components
35186 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35188 this.view = grid.getView();
35189 this.proxy = this.view.resizeProxy;
35190 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35191 "gridSplitters" + this.grid.getGridEl().id, {
35192 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35194 this.setHandleElId(Roo.id(hd));
35195 this.setOuterHandleElId(Roo.id(hd2));
35196 this.scroll = false;
35198 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35199 fly: Roo.Element.fly,
35201 b4StartDrag : function(x, y){
35202 this.view.headersDisabled = true;
35203 this.proxy.setHeight(this.view.mainWrap.getHeight());
35204 var w = this.cm.getColumnWidth(this.cellIndex);
35205 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35206 this.resetConstraints();
35207 this.setXConstraint(minw, 1000);
35208 this.setYConstraint(0, 0);
35209 this.minX = x - minw;
35210 this.maxX = x + 1000;
35212 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35216 handleMouseDown : function(e){
35217 ev = Roo.EventObject.setEvent(e);
35218 var t = this.fly(ev.getTarget());
35219 if(t.hasClass("x-grid-split")){
35220 this.cellIndex = this.view.getCellIndex(t.dom);
35221 this.split = t.dom;
35222 this.cm = this.grid.colModel;
35223 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35224 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35229 endDrag : function(e){
35230 this.view.headersDisabled = false;
35231 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35232 var diff = endX - this.startPos;
35233 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35236 autoOffset : function(){
35237 this.setDelta(0,0);
35241 * Ext JS Library 1.1.1
35242 * Copyright(c) 2006-2007, Ext JS, LLC.
35244 * Originally Released Under LGPL - original licence link has changed is not relivant.
35247 * <script type="text/javascript">
35251 // This is a support class used internally by the Grid components
35252 Roo.grid.GridDragZone = function(grid, config){
35253 this.view = grid.getView();
35254 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35255 if(this.view.lockedBody){
35256 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35257 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35259 this.scroll = false;
35261 this.ddel = document.createElement('div');
35262 this.ddel.className = 'x-grid-dd-wrap';
35265 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35266 ddGroup : "GridDD",
35268 getDragData : function(e){
35269 var t = Roo.lib.Event.getTarget(e);
35270 var rowIndex = this.view.findRowIndex(t);
35271 var sm = this.grid.selModel;
35273 //Roo.log(rowIndex);
35275 if (sm.getSelectedCell) {
35276 // cell selection..
35277 if (!sm.getSelectedCell()) {
35280 if (rowIndex != sm.getSelectedCell()[0]) {
35286 if(rowIndex !== false){
35291 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35293 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35296 if (e.hasModifier()){
35297 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35300 Roo.log("getDragData");
35305 rowIndex: rowIndex,
35306 selections:sm.getSelections ? sm.getSelections() : (
35307 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35314 onInitDrag : function(e){
35315 var data = this.dragData;
35316 this.ddel.innerHTML = this.grid.getDragDropText();
35317 this.proxy.update(this.ddel);
35318 // fire start drag?
35321 afterRepair : function(){
35322 this.dragging = false;
35325 getRepairXY : function(e, data){
35329 onEndDrag : function(data, e){
35333 onValidDrop : function(dd, e, id){
35338 beforeInvalidDrop : function(e, id){
35343 * Ext JS Library 1.1.1
35344 * Copyright(c) 2006-2007, Ext JS, LLC.
35346 * Originally Released Under LGPL - original licence link has changed is not relivant.
35349 * <script type="text/javascript">
35354 * @class Roo.grid.ColumnModel
35355 * @extends Roo.util.Observable
35356 * This is the default implementation of a ColumnModel used by the Grid. It defines
35357 * the columns in the grid.
35360 var colModel = new Roo.grid.ColumnModel([
35361 {header: "Ticker", width: 60, sortable: true, locked: true},
35362 {header: "Company Name", width: 150, sortable: true},
35363 {header: "Market Cap.", width: 100, sortable: true},
35364 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35365 {header: "Employees", width: 100, sortable: true, resizable: false}
35370 * The config options listed for this class are options which may appear in each
35371 * individual column definition.
35372 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35374 * @param {Object} config An Array of column config objects. See this class's
35375 * config objects for details.
35377 Roo.grid.ColumnModel = function(config){
35379 * The config passed into the constructor
35381 this.config = config;
35384 // if no id, create one
35385 // if the column does not have a dataIndex mapping,
35386 // map it to the order it is in the config
35387 for(var i = 0, len = config.length; i < len; i++){
35389 if(typeof c.dataIndex == "undefined"){
35392 if(typeof c.renderer == "string"){
35393 c.renderer = Roo.util.Format[c.renderer];
35395 if(typeof c.id == "undefined"){
35398 if(c.editor && c.editor.xtype){
35399 c.editor = Roo.factory(c.editor, Roo.grid);
35401 if(c.editor && c.editor.isFormField){
35402 c.editor = new Roo.grid.GridEditor(c.editor);
35404 this.lookup[c.id] = c;
35408 * The width of columns which have no width specified (defaults to 100)
35411 this.defaultWidth = 100;
35414 * Default sortable of columns which have no sortable specified (defaults to false)
35417 this.defaultSortable = false;
35421 * @event widthchange
35422 * Fires when the width of a column changes.
35423 * @param {ColumnModel} this
35424 * @param {Number} columnIndex The column index
35425 * @param {Number} newWidth The new width
35427 "widthchange": true,
35429 * @event headerchange
35430 * Fires when the text of a header changes.
35431 * @param {ColumnModel} this
35432 * @param {Number} columnIndex The column index
35433 * @param {Number} newText The new header text
35435 "headerchange": true,
35437 * @event hiddenchange
35438 * Fires when a column is hidden or "unhidden".
35439 * @param {ColumnModel} this
35440 * @param {Number} columnIndex The column index
35441 * @param {Boolean} hidden true if hidden, false otherwise
35443 "hiddenchange": true,
35445 * @event columnmoved
35446 * Fires when a column is moved.
35447 * @param {ColumnModel} this
35448 * @param {Number} oldIndex
35449 * @param {Number} newIndex
35451 "columnmoved" : true,
35453 * @event columlockchange
35454 * Fires when a column's locked state is changed
35455 * @param {ColumnModel} this
35456 * @param {Number} colIndex
35457 * @param {Boolean} locked true if locked
35459 "columnlockchange" : true
35461 Roo.grid.ColumnModel.superclass.constructor.call(this);
35463 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35465 * @cfg {String} header The header text to display in the Grid view.
35468 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35469 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35470 * specified, the column's index is used as an index into the Record's data Array.
35473 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35474 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35477 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35478 * Defaults to the value of the {@link #defaultSortable} property.
35479 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35482 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35485 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35488 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35491 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35494 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35495 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35496 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35497 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35500 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35503 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35506 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35509 * @cfg {String} cursor (Optional)
35512 * @cfg {String} tooltip (Optional)
35515 * @cfg {Number} xs (Optional)
35518 * @cfg {Number} sm (Optional)
35521 * @cfg {Number} md (Optional)
35524 * @cfg {Number} lg (Optional)
35527 * Returns the id of the column at the specified index.
35528 * @param {Number} index The column index
35529 * @return {String} the id
35531 getColumnId : function(index){
35532 return this.config[index].id;
35536 * Returns the column for a specified id.
35537 * @param {String} id The column id
35538 * @return {Object} the column
35540 getColumnById : function(id){
35541 return this.lookup[id];
35546 * Returns the column for a specified dataIndex.
35547 * @param {String} dataIndex The column dataIndex
35548 * @return {Object|Boolean} the column or false if not found
35550 getColumnByDataIndex: function(dataIndex){
35551 var index = this.findColumnIndex(dataIndex);
35552 return index > -1 ? this.config[index] : false;
35556 * Returns the index for a specified column id.
35557 * @param {String} id The column id
35558 * @return {Number} the index, or -1 if not found
35560 getIndexById : function(id){
35561 for(var i = 0, len = this.config.length; i < len; i++){
35562 if(this.config[i].id == id){
35570 * Returns the index for a specified column dataIndex.
35571 * @param {String} dataIndex The column dataIndex
35572 * @return {Number} the index, or -1 if not found
35575 findColumnIndex : function(dataIndex){
35576 for(var i = 0, len = this.config.length; i < len; i++){
35577 if(this.config[i].dataIndex == dataIndex){
35585 moveColumn : function(oldIndex, newIndex){
35586 var c = this.config[oldIndex];
35587 this.config.splice(oldIndex, 1);
35588 this.config.splice(newIndex, 0, c);
35589 this.dataMap = null;
35590 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35593 isLocked : function(colIndex){
35594 return this.config[colIndex].locked === true;
35597 setLocked : function(colIndex, value, suppressEvent){
35598 if(this.isLocked(colIndex) == value){
35601 this.config[colIndex].locked = value;
35602 if(!suppressEvent){
35603 this.fireEvent("columnlockchange", this, colIndex, value);
35607 getTotalLockedWidth : function(){
35608 var totalWidth = 0;
35609 for(var i = 0; i < this.config.length; i++){
35610 if(this.isLocked(i) && !this.isHidden(i)){
35611 this.totalWidth += this.getColumnWidth(i);
35617 getLockedCount : function(){
35618 for(var i = 0, len = this.config.length; i < len; i++){
35619 if(!this.isLocked(i)){
35624 return this.config.length;
35628 * Returns the number of columns.
35631 getColumnCount : function(visibleOnly){
35632 if(visibleOnly === true){
35634 for(var i = 0, len = this.config.length; i < len; i++){
35635 if(!this.isHidden(i)){
35641 return this.config.length;
35645 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35646 * @param {Function} fn
35647 * @param {Object} scope (optional)
35648 * @return {Array} result
35650 getColumnsBy : function(fn, scope){
35652 for(var i = 0, len = this.config.length; i < len; i++){
35653 var c = this.config[i];
35654 if(fn.call(scope||this, c, i) === true){
35662 * Returns true if the specified column is sortable.
35663 * @param {Number} col The column index
35664 * @return {Boolean}
35666 isSortable : function(col){
35667 if(typeof this.config[col].sortable == "undefined"){
35668 return this.defaultSortable;
35670 return this.config[col].sortable;
35674 * Returns the rendering (formatting) function defined for the column.
35675 * @param {Number} col The column index.
35676 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35678 getRenderer : function(col){
35679 if(!this.config[col].renderer){
35680 return Roo.grid.ColumnModel.defaultRenderer;
35682 return this.config[col].renderer;
35686 * Sets the rendering (formatting) function for a column.
35687 * @param {Number} col The column index
35688 * @param {Function} fn The function to use to process the cell's raw data
35689 * to return HTML markup for the grid view. The render function is called with
35690 * the following parameters:<ul>
35691 * <li>Data value.</li>
35692 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35693 * <li>css A CSS style string to apply to the table cell.</li>
35694 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35695 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35696 * <li>Row index</li>
35697 * <li>Column index</li>
35698 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35700 setRenderer : function(col, fn){
35701 this.config[col].renderer = fn;
35705 * Returns the width for the specified column.
35706 * @param {Number} col The column index
35709 getColumnWidth : function(col){
35710 return this.config[col].width * 1 || this.defaultWidth;
35714 * Sets the width for a column.
35715 * @param {Number} col The column index
35716 * @param {Number} width The new width
35718 setColumnWidth : function(col, width, suppressEvent){
35719 this.config[col].width = width;
35720 this.totalWidth = null;
35721 if(!suppressEvent){
35722 this.fireEvent("widthchange", this, col, width);
35727 * Returns the total width of all columns.
35728 * @param {Boolean} includeHidden True to include hidden column widths
35731 getTotalWidth : function(includeHidden){
35732 if(!this.totalWidth){
35733 this.totalWidth = 0;
35734 for(var i = 0, len = this.config.length; i < len; i++){
35735 if(includeHidden || !this.isHidden(i)){
35736 this.totalWidth += this.getColumnWidth(i);
35740 return this.totalWidth;
35744 * Returns the header for the specified column.
35745 * @param {Number} col The column index
35748 getColumnHeader : function(col){
35749 return this.config[col].header;
35753 * Sets the header for a column.
35754 * @param {Number} col The column index
35755 * @param {String} header The new header
35757 setColumnHeader : function(col, header){
35758 this.config[col].header = header;
35759 this.fireEvent("headerchange", this, col, header);
35763 * Returns the tooltip for the specified column.
35764 * @param {Number} col The column index
35767 getColumnTooltip : function(col){
35768 return this.config[col].tooltip;
35771 * Sets the tooltip for a column.
35772 * @param {Number} col The column index
35773 * @param {String} tooltip The new tooltip
35775 setColumnTooltip : function(col, tooltip){
35776 this.config[col].tooltip = tooltip;
35780 * Returns the dataIndex for the specified column.
35781 * @param {Number} col The column index
35784 getDataIndex : function(col){
35785 return this.config[col].dataIndex;
35789 * Sets the dataIndex for a column.
35790 * @param {Number} col The column index
35791 * @param {Number} dataIndex The new dataIndex
35793 setDataIndex : function(col, dataIndex){
35794 this.config[col].dataIndex = dataIndex;
35800 * Returns true if the cell is editable.
35801 * @param {Number} colIndex The column index
35802 * @param {Number} rowIndex The row index - this is nto actually used..?
35803 * @return {Boolean}
35805 isCellEditable : function(colIndex, rowIndex){
35806 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35810 * Returns the editor defined for the cell/column.
35811 * return false or null to disable editing.
35812 * @param {Number} colIndex The column index
35813 * @param {Number} rowIndex The row index
35816 getCellEditor : function(colIndex, rowIndex){
35817 return this.config[colIndex].editor;
35821 * Sets if a column is editable.
35822 * @param {Number} col The column index
35823 * @param {Boolean} editable True if the column is editable
35825 setEditable : function(col, editable){
35826 this.config[col].editable = editable;
35831 * Returns true if the column is hidden.
35832 * @param {Number} colIndex The column index
35833 * @return {Boolean}
35835 isHidden : function(colIndex){
35836 return this.config[colIndex].hidden;
35841 * Returns true if the column width cannot be changed
35843 isFixed : function(colIndex){
35844 return this.config[colIndex].fixed;
35848 * Returns true if the column can be resized
35849 * @return {Boolean}
35851 isResizable : function(colIndex){
35852 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35855 * Sets if a column is hidden.
35856 * @param {Number} colIndex The column index
35857 * @param {Boolean} hidden True if the column is hidden
35859 setHidden : function(colIndex, hidden){
35860 this.config[colIndex].hidden = hidden;
35861 this.totalWidth = null;
35862 this.fireEvent("hiddenchange", this, colIndex, hidden);
35866 * Sets the editor for a column.
35867 * @param {Number} col The column index
35868 * @param {Object} editor The editor object
35870 setEditor : function(col, editor){
35871 this.config[col].editor = editor;
35875 Roo.grid.ColumnModel.defaultRenderer = function(value)
35877 if(typeof value == "object") {
35880 if(typeof value == "string" && value.length < 1){
35884 return String.format("{0}", value);
35887 // Alias for backwards compatibility
35888 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35891 * Ext JS Library 1.1.1
35892 * Copyright(c) 2006-2007, Ext JS, LLC.
35894 * Originally Released Under LGPL - original licence link has changed is not relivant.
35897 * <script type="text/javascript">
35901 * @class Roo.grid.AbstractSelectionModel
35902 * @extends Roo.util.Observable
35903 * Abstract base class for grid SelectionModels. It provides the interface that should be
35904 * implemented by descendant classes. This class should not be directly instantiated.
35907 Roo.grid.AbstractSelectionModel = function(){
35908 this.locked = false;
35909 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35912 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35913 /** @ignore Called by the grid automatically. Do not call directly. */
35914 init : function(grid){
35920 * Locks the selections.
35923 this.locked = true;
35927 * Unlocks the selections.
35929 unlock : function(){
35930 this.locked = false;
35934 * Returns true if the selections are locked.
35935 * @return {Boolean}
35937 isLocked : function(){
35938 return this.locked;
35942 * Ext JS Library 1.1.1
35943 * Copyright(c) 2006-2007, Ext JS, LLC.
35945 * Originally Released Under LGPL - original licence link has changed is not relivant.
35948 * <script type="text/javascript">
35951 * @extends Roo.grid.AbstractSelectionModel
35952 * @class Roo.grid.RowSelectionModel
35953 * The default SelectionModel used by {@link Roo.grid.Grid}.
35954 * It supports multiple selections and keyboard selection/navigation.
35956 * @param {Object} config
35958 Roo.grid.RowSelectionModel = function(config){
35959 Roo.apply(this, config);
35960 this.selections = new Roo.util.MixedCollection(false, function(o){
35965 this.lastActive = false;
35969 * @event selectionchange
35970 * Fires when the selection changes
35971 * @param {SelectionModel} this
35973 "selectionchange" : true,
35975 * @event afterselectionchange
35976 * Fires after the selection changes (eg. by key press or clicking)
35977 * @param {SelectionModel} this
35979 "afterselectionchange" : true,
35981 * @event beforerowselect
35982 * Fires when a row is selected being selected, return false to cancel.
35983 * @param {SelectionModel} this
35984 * @param {Number} rowIndex The selected index
35985 * @param {Boolean} keepExisting False if other selections will be cleared
35987 "beforerowselect" : true,
35990 * Fires when a row is selected.
35991 * @param {SelectionModel} this
35992 * @param {Number} rowIndex The selected index
35993 * @param {Roo.data.Record} r The record
35995 "rowselect" : true,
35997 * @event rowdeselect
35998 * Fires when a row is deselected.
35999 * @param {SelectionModel} this
36000 * @param {Number} rowIndex The selected index
36002 "rowdeselect" : true
36004 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36005 this.locked = false;
36008 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
36010 * @cfg {Boolean} singleSelect
36011 * True to allow selection of only one row at a time (defaults to false)
36013 singleSelect : false,
36016 initEvents : function(){
36018 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36019 this.grid.on("mousedown", this.handleMouseDown, this);
36020 }else{ // allow click to work like normal
36021 this.grid.on("rowclick", this.handleDragableRowClick, this);
36024 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36025 "up" : function(e){
36027 this.selectPrevious(e.shiftKey);
36028 }else if(this.last !== false && this.lastActive !== false){
36029 var last = this.last;
36030 this.selectRange(this.last, this.lastActive-1);
36031 this.grid.getView().focusRow(this.lastActive);
36032 if(last !== false){
36036 this.selectFirstRow();
36038 this.fireEvent("afterselectionchange", this);
36040 "down" : function(e){
36042 this.selectNext(e.shiftKey);
36043 }else if(this.last !== false && this.lastActive !== false){
36044 var last = this.last;
36045 this.selectRange(this.last, this.lastActive+1);
36046 this.grid.getView().focusRow(this.lastActive);
36047 if(last !== false){
36051 this.selectFirstRow();
36053 this.fireEvent("afterselectionchange", this);
36058 var view = this.grid.view;
36059 view.on("refresh", this.onRefresh, this);
36060 view.on("rowupdated", this.onRowUpdated, this);
36061 view.on("rowremoved", this.onRemove, this);
36065 onRefresh : function(){
36066 var ds = this.grid.dataSource, i, v = this.grid.view;
36067 var s = this.selections;
36068 s.each(function(r){
36069 if((i = ds.indexOfId(r.id)) != -1){
36071 s.add(ds.getAt(i)); // updating the selection relate data
36079 onRemove : function(v, index, r){
36080 this.selections.remove(r);
36084 onRowUpdated : function(v, index, r){
36085 if(this.isSelected(r)){
36086 v.onRowSelect(index);
36092 * @param {Array} records The records to select
36093 * @param {Boolean} keepExisting (optional) True to keep existing selections
36095 selectRecords : function(records, keepExisting){
36097 this.clearSelections();
36099 var ds = this.grid.dataSource;
36100 for(var i = 0, len = records.length; i < len; i++){
36101 this.selectRow(ds.indexOf(records[i]), true);
36106 * Gets the number of selected rows.
36109 getCount : function(){
36110 return this.selections.length;
36114 * Selects the first row in the grid.
36116 selectFirstRow : function(){
36121 * Select the last row.
36122 * @param {Boolean} keepExisting (optional) True to keep existing selections
36124 selectLastRow : function(keepExisting){
36125 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36129 * Selects the row immediately following the last selected row.
36130 * @param {Boolean} keepExisting (optional) True to keep existing selections
36132 selectNext : function(keepExisting){
36133 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36134 this.selectRow(this.last+1, keepExisting);
36135 this.grid.getView().focusRow(this.last);
36140 * Selects the row that precedes the last selected row.
36141 * @param {Boolean} keepExisting (optional) True to keep existing selections
36143 selectPrevious : function(keepExisting){
36145 this.selectRow(this.last-1, keepExisting);
36146 this.grid.getView().focusRow(this.last);
36151 * Returns the selected records
36152 * @return {Array} Array of selected records
36154 getSelections : function(){
36155 return [].concat(this.selections.items);
36159 * Returns the first selected record.
36162 getSelected : function(){
36163 return this.selections.itemAt(0);
36168 * Clears all selections.
36170 clearSelections : function(fast){
36175 var ds = this.grid.dataSource;
36176 var s = this.selections;
36177 s.each(function(r){
36178 this.deselectRow(ds.indexOfId(r.id));
36182 this.selections.clear();
36189 * Selects all rows.
36191 selectAll : function(){
36195 this.selections.clear();
36196 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36197 this.selectRow(i, true);
36202 * Returns True if there is a selection.
36203 * @return {Boolean}
36205 hasSelection : function(){
36206 return this.selections.length > 0;
36210 * Returns True if the specified row is selected.
36211 * @param {Number/Record} record The record or index of the record to check
36212 * @return {Boolean}
36214 isSelected : function(index){
36215 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36216 return (r && this.selections.key(r.id) ? true : false);
36220 * Returns True if the specified record id is selected.
36221 * @param {String} id The id of record to check
36222 * @return {Boolean}
36224 isIdSelected : function(id){
36225 return (this.selections.key(id) ? true : false);
36229 handleMouseDown : function(e, t){
36230 var view = this.grid.getView(), rowIndex;
36231 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36234 if(e.shiftKey && this.last !== false){
36235 var last = this.last;
36236 this.selectRange(last, rowIndex, e.ctrlKey);
36237 this.last = last; // reset the last
36238 view.focusRow(rowIndex);
36240 var isSelected = this.isSelected(rowIndex);
36241 if(e.button !== 0 && isSelected){
36242 view.focusRow(rowIndex);
36243 }else if(e.ctrlKey && isSelected){
36244 this.deselectRow(rowIndex);
36245 }else if(!isSelected){
36246 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36247 view.focusRow(rowIndex);
36250 this.fireEvent("afterselectionchange", this);
36253 handleDragableRowClick : function(grid, rowIndex, e)
36255 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36256 this.selectRow(rowIndex, false);
36257 grid.view.focusRow(rowIndex);
36258 this.fireEvent("afterselectionchange", this);
36263 * Selects multiple rows.
36264 * @param {Array} rows Array of the indexes of the row to select
36265 * @param {Boolean} keepExisting (optional) True to keep existing selections
36267 selectRows : function(rows, keepExisting){
36269 this.clearSelections();
36271 for(var i = 0, len = rows.length; i < len; i++){
36272 this.selectRow(rows[i], true);
36277 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36278 * @param {Number} startRow The index of the first row in the range
36279 * @param {Number} endRow The index of the last row in the range
36280 * @param {Boolean} keepExisting (optional) True to retain existing selections
36282 selectRange : function(startRow, endRow, keepExisting){
36287 this.clearSelections();
36289 if(startRow <= endRow){
36290 for(var i = startRow; i <= endRow; i++){
36291 this.selectRow(i, true);
36294 for(var i = startRow; i >= endRow; i--){
36295 this.selectRow(i, true);
36301 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36302 * @param {Number} startRow The index of the first row in the range
36303 * @param {Number} endRow The index of the last row in the range
36305 deselectRange : function(startRow, endRow, preventViewNotify){
36309 for(var i = startRow; i <= endRow; i++){
36310 this.deselectRow(i, preventViewNotify);
36316 * @param {Number} row The index of the row to select
36317 * @param {Boolean} keepExisting (optional) True to keep existing selections
36319 selectRow : function(index, keepExisting, preventViewNotify){
36320 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36323 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36324 if(!keepExisting || this.singleSelect){
36325 this.clearSelections();
36327 var r = this.grid.dataSource.getAt(index);
36328 this.selections.add(r);
36329 this.last = this.lastActive = index;
36330 if(!preventViewNotify){
36331 this.grid.getView().onRowSelect(index);
36333 this.fireEvent("rowselect", this, index, r);
36334 this.fireEvent("selectionchange", this);
36340 * @param {Number} row The index of the row to deselect
36342 deselectRow : function(index, preventViewNotify){
36346 if(this.last == index){
36349 if(this.lastActive == index){
36350 this.lastActive = false;
36352 var r = this.grid.dataSource.getAt(index);
36353 this.selections.remove(r);
36354 if(!preventViewNotify){
36355 this.grid.getView().onRowDeselect(index);
36357 this.fireEvent("rowdeselect", this, index);
36358 this.fireEvent("selectionchange", this);
36362 restoreLast : function(){
36364 this.last = this._last;
36369 acceptsNav : function(row, col, cm){
36370 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36374 onEditorKey : function(field, e){
36375 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36380 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36382 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36384 }else if(k == e.ENTER && !e.ctrlKey){
36388 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36390 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36392 }else if(k == e.ESC){
36396 g.startEditing(newCell[0], newCell[1]);
36401 * Ext JS Library 1.1.1
36402 * Copyright(c) 2006-2007, Ext JS, LLC.
36404 * Originally Released Under LGPL - original licence link has changed is not relivant.
36407 * <script type="text/javascript">
36410 * @class Roo.grid.CellSelectionModel
36411 * @extends Roo.grid.AbstractSelectionModel
36412 * This class provides the basic implementation for cell selection in a grid.
36414 * @param {Object} config The object containing the configuration of this model.
36415 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36417 Roo.grid.CellSelectionModel = function(config){
36418 Roo.apply(this, config);
36420 this.selection = null;
36424 * @event beforerowselect
36425 * Fires before a cell is selected.
36426 * @param {SelectionModel} this
36427 * @param {Number} rowIndex The selected row index
36428 * @param {Number} colIndex The selected cell index
36430 "beforecellselect" : true,
36432 * @event cellselect
36433 * Fires when a cell is selected.
36434 * @param {SelectionModel} this
36435 * @param {Number} rowIndex The selected row index
36436 * @param {Number} colIndex The selected cell index
36438 "cellselect" : true,
36440 * @event selectionchange
36441 * Fires when the active selection changes.
36442 * @param {SelectionModel} this
36443 * @param {Object} selection null for no selection or an object (o) with two properties
36445 <li>o.record: the record object for the row the selection is in</li>
36446 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36449 "selectionchange" : true,
36452 * Fires when the tab (or enter) was pressed on the last editable cell
36453 * You can use this to trigger add new row.
36454 * @param {SelectionModel} this
36458 * @event beforeeditnext
36459 * Fires before the next editable sell is made active
36460 * You can use this to skip to another cell or fire the tabend
36461 * if you set cell to false
36462 * @param {Object} eventdata object : { cell : [ row, col ] }
36464 "beforeeditnext" : true
36466 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36469 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36471 enter_is_tab: false,
36474 initEvents : function(){
36475 this.grid.on("mousedown", this.handleMouseDown, this);
36476 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36477 var view = this.grid.view;
36478 view.on("refresh", this.onViewChange, this);
36479 view.on("rowupdated", this.onRowUpdated, this);
36480 view.on("beforerowremoved", this.clearSelections, this);
36481 view.on("beforerowsinserted", this.clearSelections, this);
36482 if(this.grid.isEditor){
36483 this.grid.on("beforeedit", this.beforeEdit, this);
36488 beforeEdit : function(e){
36489 this.select(e.row, e.column, false, true, e.record);
36493 onRowUpdated : function(v, index, r){
36494 if(this.selection && this.selection.record == r){
36495 v.onCellSelect(index, this.selection.cell[1]);
36500 onViewChange : function(){
36501 this.clearSelections(true);
36505 * Returns the currently selected cell,.
36506 * @return {Array} The selected cell (row, column) or null if none selected.
36508 getSelectedCell : function(){
36509 return this.selection ? this.selection.cell : null;
36513 * Clears all selections.
36514 * @param {Boolean} true to prevent the gridview from being notified about the change.
36516 clearSelections : function(preventNotify){
36517 var s = this.selection;
36519 if(preventNotify !== true){
36520 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36522 this.selection = null;
36523 this.fireEvent("selectionchange", this, null);
36528 * Returns true if there is a selection.
36529 * @return {Boolean}
36531 hasSelection : function(){
36532 return this.selection ? true : false;
36536 handleMouseDown : function(e, t){
36537 var v = this.grid.getView();
36538 if(this.isLocked()){
36541 var row = v.findRowIndex(t);
36542 var cell = v.findCellIndex(t);
36543 if(row !== false && cell !== false){
36544 this.select(row, cell);
36550 * @param {Number} rowIndex
36551 * @param {Number} collIndex
36553 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36554 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36555 this.clearSelections();
36556 r = r || this.grid.dataSource.getAt(rowIndex);
36559 cell : [rowIndex, colIndex]
36561 if(!preventViewNotify){
36562 var v = this.grid.getView();
36563 v.onCellSelect(rowIndex, colIndex);
36564 if(preventFocus !== true){
36565 v.focusCell(rowIndex, colIndex);
36568 this.fireEvent("cellselect", this, rowIndex, colIndex);
36569 this.fireEvent("selectionchange", this, this.selection);
36574 isSelectable : function(rowIndex, colIndex, cm){
36575 return !cm.isHidden(colIndex);
36579 handleKeyDown : function(e){
36580 //Roo.log('Cell Sel Model handleKeyDown');
36581 if(!e.isNavKeyPress()){
36584 var g = this.grid, s = this.selection;
36587 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36589 this.select(cell[0], cell[1]);
36594 var walk = function(row, col, step){
36595 return g.walkCells(row, col, step, sm.isSelectable, sm);
36597 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36604 // handled by onEditorKey
36605 if (g.isEditor && g.editing) {
36609 newCell = walk(r, c-1, -1);
36611 newCell = walk(r, c+1, 1);
36616 newCell = walk(r+1, c, 1);
36620 newCell = walk(r-1, c, -1);
36624 newCell = walk(r, c+1, 1);
36628 newCell = walk(r, c-1, -1);
36633 if(g.isEditor && !g.editing){
36634 g.startEditing(r, c);
36643 this.select(newCell[0], newCell[1]);
36649 acceptsNav : function(row, col, cm){
36650 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36654 * @param {Number} field (not used) - as it's normally used as a listener
36655 * @param {Number} e - event - fake it by using
36657 * var e = Roo.EventObjectImpl.prototype;
36658 * e.keyCode = e.TAB
36662 onEditorKey : function(field, e){
36664 var k = e.getKey(),
36667 ed = g.activeEditor,
36669 ///Roo.log('onEditorKey' + k);
36672 if (this.enter_is_tab && k == e.ENTER) {
36678 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36680 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36686 } else if(k == e.ENTER && !e.ctrlKey){
36689 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36691 } else if(k == e.ESC){
36696 var ecall = { cell : newCell, forward : forward };
36697 this.fireEvent('beforeeditnext', ecall );
36698 newCell = ecall.cell;
36699 forward = ecall.forward;
36703 //Roo.log('next cell after edit');
36704 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36705 } else if (forward) {
36706 // tabbed past last
36707 this.fireEvent.defer(100, this, ['tabend',this]);
36712 * Ext JS Library 1.1.1
36713 * Copyright(c) 2006-2007, Ext JS, LLC.
36715 * Originally Released Under LGPL - original licence link has changed is not relivant.
36718 * <script type="text/javascript">
36722 * @class Roo.grid.EditorGrid
36723 * @extends Roo.grid.Grid
36724 * Class for creating and editable grid.
36725 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36726 * The container MUST have some type of size defined for the grid to fill. The container will be
36727 * automatically set to position relative if it isn't already.
36728 * @param {Object} dataSource The data model to bind to
36729 * @param {Object} colModel The column model with info about this grid's columns
36731 Roo.grid.EditorGrid = function(container, config){
36732 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36733 this.getGridEl().addClass("xedit-grid");
36735 if(!this.selModel){
36736 this.selModel = new Roo.grid.CellSelectionModel();
36739 this.activeEditor = null;
36743 * @event beforeedit
36744 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36745 * <ul style="padding:5px;padding-left:16px;">
36746 * <li>grid - This grid</li>
36747 * <li>record - The record being edited</li>
36748 * <li>field - The field name being edited</li>
36749 * <li>value - The value for the field being edited.</li>
36750 * <li>row - The grid row index</li>
36751 * <li>column - The grid column index</li>
36752 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36754 * @param {Object} e An edit event (see above for description)
36756 "beforeedit" : true,
36759 * Fires after a cell is edited. <br />
36760 * <ul style="padding:5px;padding-left:16px;">
36761 * <li>grid - This grid</li>
36762 * <li>record - The record being edited</li>
36763 * <li>field - The field name being edited</li>
36764 * <li>value - The value being set</li>
36765 * <li>originalValue - The original value for the field, before the edit.</li>
36766 * <li>row - The grid row index</li>
36767 * <li>column - The grid column index</li>
36769 * @param {Object} e An edit event (see above for description)
36771 "afteredit" : true,
36773 * @event validateedit
36774 * Fires after a cell is edited, but before the value is set in the record.
36775 * You can use this to modify the value being set in the field, Return false
36776 * to cancel the change. The edit event object has the following properties <br />
36777 * <ul style="padding:5px;padding-left:16px;">
36778 * <li>editor - This editor</li>
36779 * <li>grid - This grid</li>
36780 * <li>record - The record being edited</li>
36781 * <li>field - The field name being edited</li>
36782 * <li>value - The value being set</li>
36783 * <li>originalValue - The original value for the field, before the edit.</li>
36784 * <li>row - The grid row index</li>
36785 * <li>column - The grid column index</li>
36786 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36788 * @param {Object} e An edit event (see above for description)
36790 "validateedit" : true
36792 this.on("bodyscroll", this.stopEditing, this);
36793 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36796 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36798 * @cfg {Number} clicksToEdit
36799 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36806 trackMouseOver: false, // causes very odd FF errors
36808 onCellDblClick : function(g, row, col){
36809 this.startEditing(row, col);
36812 onEditComplete : function(ed, value, startValue){
36813 this.editing = false;
36814 this.activeEditor = null;
36815 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36817 var field = this.colModel.getDataIndex(ed.col);
36822 originalValue: startValue,
36829 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36832 if(String(value) !== String(startValue)){
36834 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36835 r.set(field, e.value);
36836 // if we are dealing with a combo box..
36837 // then we also set the 'name' colum to be the displayField
36838 if (ed.field.displayField && ed.field.name) {
36839 r.set(ed.field.name, ed.field.el.dom.value);
36842 delete e.cancel; //?? why!!!
36843 this.fireEvent("afteredit", e);
36846 this.fireEvent("afteredit", e); // always fire it!
36848 this.view.focusCell(ed.row, ed.col);
36852 * Starts editing the specified for the specified row/column
36853 * @param {Number} rowIndex
36854 * @param {Number} colIndex
36856 startEditing : function(row, col){
36857 this.stopEditing();
36858 if(this.colModel.isCellEditable(col, row)){
36859 this.view.ensureVisible(row, col, true);
36861 var r = this.dataSource.getAt(row);
36862 var field = this.colModel.getDataIndex(col);
36863 var cell = Roo.get(this.view.getCell(row,col));
36868 value: r.data[field],
36873 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36874 this.editing = true;
36875 var ed = this.colModel.getCellEditor(col, row);
36881 ed.render(ed.parentEl || document.body);
36887 (function(){ // complex but required for focus issues in safari, ie and opera
36891 ed.on("complete", this.onEditComplete, this, {single: true});
36892 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36893 this.activeEditor = ed;
36894 var v = r.data[field];
36895 ed.startEdit(this.view.getCell(row, col), v);
36896 // combo's with 'displayField and name set
36897 if (ed.field.displayField && ed.field.name) {
36898 ed.field.el.dom.value = r.data[ed.field.name];
36902 }).defer(50, this);
36908 * Stops any active editing
36910 stopEditing : function(){
36911 if(this.activeEditor){
36912 this.activeEditor.completeEdit();
36914 this.activeEditor = null;
36918 * Called to get grid's drag proxy text, by default returns this.ddText.
36921 getDragDropText : function(){
36922 var count = this.selModel.getSelectedCell() ? 1 : 0;
36923 return String.format(this.ddText, count, count == 1 ? '' : 's');
36928 * Ext JS Library 1.1.1
36929 * Copyright(c) 2006-2007, Ext JS, LLC.
36931 * Originally Released Under LGPL - original licence link has changed is not relivant.
36934 * <script type="text/javascript">
36937 // private - not really -- you end up using it !
36938 // This is a support class used internally by the Grid components
36941 * @class Roo.grid.GridEditor
36942 * @extends Roo.Editor
36943 * Class for creating and editable grid elements.
36944 * @param {Object} config any settings (must include field)
36946 Roo.grid.GridEditor = function(field, config){
36947 if (!config && field.field) {
36949 field = Roo.factory(config.field, Roo.form);
36951 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36952 field.monitorTab = false;
36955 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36958 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36961 alignment: "tl-tl",
36964 cls: "x-small-editor x-grid-editor",
36969 * Ext JS Library 1.1.1
36970 * Copyright(c) 2006-2007, Ext JS, LLC.
36972 * Originally Released Under LGPL - original licence link has changed is not relivant.
36975 * <script type="text/javascript">
36980 Roo.grid.PropertyRecord = Roo.data.Record.create([
36981 {name:'name',type:'string'}, 'value'
36985 Roo.grid.PropertyStore = function(grid, source){
36987 this.store = new Roo.data.Store({
36988 recordType : Roo.grid.PropertyRecord
36990 this.store.on('update', this.onUpdate, this);
36992 this.setSource(source);
36994 Roo.grid.PropertyStore.superclass.constructor.call(this);
36999 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37000 setSource : function(o){
37002 this.store.removeAll();
37005 if(this.isEditableValue(o[k])){
37006 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37009 this.store.loadRecords({records: data}, {}, true);
37012 onUpdate : function(ds, record, type){
37013 if(type == Roo.data.Record.EDIT){
37014 var v = record.data['value'];
37015 var oldValue = record.modified['value'];
37016 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37017 this.source[record.id] = v;
37019 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37026 getProperty : function(row){
37027 return this.store.getAt(row);
37030 isEditableValue: function(val){
37031 if(val && val instanceof Date){
37033 }else if(typeof val == 'object' || typeof val == 'function'){
37039 setValue : function(prop, value){
37040 this.source[prop] = value;
37041 this.store.getById(prop).set('value', value);
37044 getSource : function(){
37045 return this.source;
37049 Roo.grid.PropertyColumnModel = function(grid, store){
37052 g.PropertyColumnModel.superclass.constructor.call(this, [
37053 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37054 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37056 this.store = store;
37057 this.bselect = Roo.DomHelper.append(document.body, {
37058 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37059 {tag: 'option', value: 'true', html: 'true'},
37060 {tag: 'option', value: 'false', html: 'false'}
37063 Roo.id(this.bselect);
37066 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37067 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37068 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37069 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37070 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37072 this.renderCellDelegate = this.renderCell.createDelegate(this);
37073 this.renderPropDelegate = this.renderProp.createDelegate(this);
37076 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37080 valueText : 'Value',
37082 dateFormat : 'm/j/Y',
37085 renderDate : function(dateVal){
37086 return dateVal.dateFormat(this.dateFormat);
37089 renderBool : function(bVal){
37090 return bVal ? 'true' : 'false';
37093 isCellEditable : function(colIndex, rowIndex){
37094 return colIndex == 1;
37097 getRenderer : function(col){
37099 this.renderCellDelegate : this.renderPropDelegate;
37102 renderProp : function(v){
37103 return this.getPropertyName(v);
37106 renderCell : function(val){
37108 if(val instanceof Date){
37109 rv = this.renderDate(val);
37110 }else if(typeof val == 'boolean'){
37111 rv = this.renderBool(val);
37113 return Roo.util.Format.htmlEncode(rv);
37116 getPropertyName : function(name){
37117 var pn = this.grid.propertyNames;
37118 return pn && pn[name] ? pn[name] : name;
37121 getCellEditor : function(colIndex, rowIndex){
37122 var p = this.store.getProperty(rowIndex);
37123 var n = p.data['name'], val = p.data['value'];
37125 if(typeof(this.grid.customEditors[n]) == 'string'){
37126 return this.editors[this.grid.customEditors[n]];
37128 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37129 return this.grid.customEditors[n];
37131 if(val instanceof Date){
37132 return this.editors['date'];
37133 }else if(typeof val == 'number'){
37134 return this.editors['number'];
37135 }else if(typeof val == 'boolean'){
37136 return this.editors['boolean'];
37138 return this.editors['string'];
37144 * @class Roo.grid.PropertyGrid
37145 * @extends Roo.grid.EditorGrid
37146 * This class represents the interface of a component based property grid control.
37147 * <br><br>Usage:<pre><code>
37148 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37156 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37157 * The container MUST have some type of size defined for the grid to fill. The container will be
37158 * automatically set to position relative if it isn't already.
37159 * @param {Object} config A config object that sets properties on this grid.
37161 Roo.grid.PropertyGrid = function(container, config){
37162 config = config || {};
37163 var store = new Roo.grid.PropertyStore(this);
37164 this.store = store;
37165 var cm = new Roo.grid.PropertyColumnModel(this, store);
37166 store.store.sort('name', 'ASC');
37167 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37170 enableColLock:false,
37171 enableColumnMove:false,
37173 trackMouseOver: false,
37176 this.getGridEl().addClass('x-props-grid');
37177 this.lastEditRow = null;
37178 this.on('columnresize', this.onColumnResize, this);
37181 * @event beforepropertychange
37182 * Fires before a property changes (return false to stop?)
37183 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37184 * @param {String} id Record Id
37185 * @param {String} newval New Value
37186 * @param {String} oldval Old Value
37188 "beforepropertychange": true,
37190 * @event propertychange
37191 * Fires after a property changes
37192 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37193 * @param {String} id Record Id
37194 * @param {String} newval New Value
37195 * @param {String} oldval Old Value
37197 "propertychange": true
37199 this.customEditors = this.customEditors || {};
37201 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37204 * @cfg {Object} customEditors map of colnames=> custom editors.
37205 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37206 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37207 * false disables editing of the field.
37211 * @cfg {Object} propertyNames map of property Names to their displayed value
37214 render : function(){
37215 Roo.grid.PropertyGrid.superclass.render.call(this);
37216 this.autoSize.defer(100, this);
37219 autoSize : function(){
37220 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37222 this.view.fitColumns();
37226 onColumnResize : function(){
37227 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37231 * Sets the data for the Grid
37232 * accepts a Key => Value object of all the elements avaiable.
37233 * @param {Object} data to appear in grid.
37235 setSource : function(source){
37236 this.store.setSource(source);
37240 * Gets all the data from the grid.
37241 * @return {Object} data data stored in grid
37243 getSource : function(){
37244 return this.store.getSource();
37253 * @class Roo.grid.Calendar
37254 * @extends Roo.util.Grid
37255 * This class extends the Grid to provide a calendar widget
37256 * <br><br>Usage:<pre><code>
37257 var grid = new Roo.grid.Calendar("my-container-id", {
37260 selModel: mySelectionModel,
37261 autoSizeColumns: true,
37262 monitorWindowResize: false,
37263 trackMouseOver: true
37264 eventstore : real data store..
37270 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37271 * The container MUST have some type of size defined for the grid to fill. The container will be
37272 * automatically set to position relative if it isn't already.
37273 * @param {Object} config A config object that sets properties on this grid.
37275 Roo.grid.Calendar = function(container, config){
37276 // initialize the container
37277 this.container = Roo.get(container);
37278 this.container.update("");
37279 this.container.setStyle("overflow", "hidden");
37280 this.container.addClass('x-grid-container');
37282 this.id = this.container.id;
37284 Roo.apply(this, config);
37285 // check and correct shorthanded configs
37289 for (var r = 0;r < 6;r++) {
37292 for (var c =0;c < 7;c++) {
37296 if (this.eventStore) {
37297 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37298 this.eventStore.on('load',this.onLoad, this);
37299 this.eventStore.on('beforeload',this.clearEvents, this);
37303 this.dataSource = new Roo.data.Store({
37304 proxy: new Roo.data.MemoryProxy(rows),
37305 reader: new Roo.data.ArrayReader({}, [
37306 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37309 this.dataSource.load();
37310 this.ds = this.dataSource;
37311 this.ds.xmodule = this.xmodule || false;
37314 var cellRender = function(v,x,r)
37316 return String.format(
37317 '<div class="fc-day fc-widget-content"><div>' +
37318 '<div class="fc-event-container"></div>' +
37319 '<div class="fc-day-number">{0}</div>'+
37321 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37322 '</div></div>', v);
37327 this.colModel = new Roo.grid.ColumnModel( [
37329 xtype: 'ColumnModel',
37331 dataIndex : 'weekday0',
37333 renderer : cellRender
37336 xtype: 'ColumnModel',
37338 dataIndex : 'weekday1',
37340 renderer : cellRender
37343 xtype: 'ColumnModel',
37345 dataIndex : 'weekday2',
37346 header : 'Tuesday',
37347 renderer : cellRender
37350 xtype: 'ColumnModel',
37352 dataIndex : 'weekday3',
37353 header : 'Wednesday',
37354 renderer : cellRender
37357 xtype: 'ColumnModel',
37359 dataIndex : 'weekday4',
37360 header : 'Thursday',
37361 renderer : cellRender
37364 xtype: 'ColumnModel',
37366 dataIndex : 'weekday5',
37368 renderer : cellRender
37371 xtype: 'ColumnModel',
37373 dataIndex : 'weekday6',
37374 header : 'Saturday',
37375 renderer : cellRender
37378 this.cm = this.colModel;
37379 this.cm.xmodule = this.xmodule || false;
37383 //this.selModel = new Roo.grid.CellSelectionModel();
37384 //this.sm = this.selModel;
37385 //this.selModel.init(this);
37389 this.container.setWidth(this.width);
37393 this.container.setHeight(this.height);
37400 * The raw click event for the entire grid.
37401 * @param {Roo.EventObject} e
37406 * The raw dblclick event for the entire grid.
37407 * @param {Roo.EventObject} e
37411 * @event contextmenu
37412 * The raw contextmenu event for the entire grid.
37413 * @param {Roo.EventObject} e
37415 "contextmenu" : true,
37418 * The raw mousedown event for the entire grid.
37419 * @param {Roo.EventObject} e
37421 "mousedown" : true,
37424 * The raw mouseup event for the entire grid.
37425 * @param {Roo.EventObject} e
37430 * The raw mouseover event for the entire grid.
37431 * @param {Roo.EventObject} e
37433 "mouseover" : true,
37436 * The raw mouseout event for the entire grid.
37437 * @param {Roo.EventObject} e
37442 * The raw keypress event for the entire grid.
37443 * @param {Roo.EventObject} e
37448 * The raw keydown event for the entire grid.
37449 * @param {Roo.EventObject} e
37457 * Fires when a cell is clicked
37458 * @param {Grid} this
37459 * @param {Number} rowIndex
37460 * @param {Number} columnIndex
37461 * @param {Roo.EventObject} e
37463 "cellclick" : true,
37465 * @event celldblclick
37466 * Fires when a cell is double clicked
37467 * @param {Grid} this
37468 * @param {Number} rowIndex
37469 * @param {Number} columnIndex
37470 * @param {Roo.EventObject} e
37472 "celldblclick" : true,
37475 * Fires when a row is clicked
37476 * @param {Grid} this
37477 * @param {Number} rowIndex
37478 * @param {Roo.EventObject} e
37482 * @event rowdblclick
37483 * Fires when a row is double clicked
37484 * @param {Grid} this
37485 * @param {Number} rowIndex
37486 * @param {Roo.EventObject} e
37488 "rowdblclick" : true,
37490 * @event headerclick
37491 * Fires when a header is clicked
37492 * @param {Grid} this
37493 * @param {Number} columnIndex
37494 * @param {Roo.EventObject} e
37496 "headerclick" : true,
37498 * @event headerdblclick
37499 * Fires when a header cell is double clicked
37500 * @param {Grid} this
37501 * @param {Number} columnIndex
37502 * @param {Roo.EventObject} e
37504 "headerdblclick" : true,
37506 * @event rowcontextmenu
37507 * Fires when a row is right clicked
37508 * @param {Grid} this
37509 * @param {Number} rowIndex
37510 * @param {Roo.EventObject} e
37512 "rowcontextmenu" : true,
37514 * @event cellcontextmenu
37515 * Fires when a cell is right clicked
37516 * @param {Grid} this
37517 * @param {Number} rowIndex
37518 * @param {Number} cellIndex
37519 * @param {Roo.EventObject} e
37521 "cellcontextmenu" : true,
37523 * @event headercontextmenu
37524 * Fires when a header is right clicked
37525 * @param {Grid} this
37526 * @param {Number} columnIndex
37527 * @param {Roo.EventObject} e
37529 "headercontextmenu" : true,
37531 * @event bodyscroll
37532 * Fires when the body element is scrolled
37533 * @param {Number} scrollLeft
37534 * @param {Number} scrollTop
37536 "bodyscroll" : true,
37538 * @event columnresize
37539 * Fires when the user resizes a column
37540 * @param {Number} columnIndex
37541 * @param {Number} newSize
37543 "columnresize" : true,
37545 * @event columnmove
37546 * Fires when the user moves a column
37547 * @param {Number} oldIndex
37548 * @param {Number} newIndex
37550 "columnmove" : true,
37553 * Fires when row(s) start being dragged
37554 * @param {Grid} this
37555 * @param {Roo.GridDD} dd The drag drop object
37556 * @param {event} e The raw browser event
37558 "startdrag" : true,
37561 * Fires when a drag operation is complete
37562 * @param {Grid} this
37563 * @param {Roo.GridDD} dd The drag drop object
37564 * @param {event} e The raw browser event
37569 * Fires when dragged row(s) are dropped on a valid DD target
37570 * @param {Grid} this
37571 * @param {Roo.GridDD} dd The drag drop object
37572 * @param {String} targetId The target drag drop object
37573 * @param {event} e The raw browser event
37578 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37579 * @param {Grid} this
37580 * @param {Roo.GridDD} dd The drag drop object
37581 * @param {String} targetId The target drag drop object
37582 * @param {event} e The raw browser event
37587 * Fires when the dragged row(s) first cross another DD target while being dragged
37588 * @param {Grid} this
37589 * @param {Roo.GridDD} dd The drag drop object
37590 * @param {String} targetId The target drag drop object
37591 * @param {event} e The raw browser event
37593 "dragenter" : true,
37596 * Fires when the dragged row(s) leave another DD target while being dragged
37597 * @param {Grid} this
37598 * @param {Roo.GridDD} dd The drag drop object
37599 * @param {String} targetId The target drag drop object
37600 * @param {event} e The raw browser event
37605 * Fires when a row is rendered, so you can change add a style to it.
37606 * @param {GridView} gridview The grid view
37607 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37613 * Fires when the grid is rendered
37614 * @param {Grid} grid
37619 * Fires when a date is selected
37620 * @param {DatePicker} this
37621 * @param {Date} date The selected date
37625 * @event monthchange
37626 * Fires when the displayed month changes
37627 * @param {DatePicker} this
37628 * @param {Date} date The selected month
37630 'monthchange': true,
37632 * @event evententer
37633 * Fires when mouse over an event
37634 * @param {Calendar} this
37635 * @param {event} Event
37637 'evententer': true,
37639 * @event eventleave
37640 * Fires when the mouse leaves an
37641 * @param {Calendar} this
37644 'eventleave': true,
37646 * @event eventclick
37647 * Fires when the mouse click an
37648 * @param {Calendar} this
37651 'eventclick': true,
37653 * @event eventrender
37654 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37655 * @param {Calendar} this
37656 * @param {data} data to be modified
37658 'eventrender': true
37662 Roo.grid.Grid.superclass.constructor.call(this);
37663 this.on('render', function() {
37664 this.view.el.addClass('x-grid-cal');
37666 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37670 if (!Roo.grid.Calendar.style) {
37671 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37674 '.x-grid-cal .x-grid-col' : {
37675 height: 'auto !important',
37676 'vertical-align': 'top'
37678 '.x-grid-cal .fc-event-hori' : {
37689 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37691 * @cfg {Store} eventStore The store that loads events.
37696 activeDate : false,
37699 monitorWindowResize : false,
37702 resizeColumns : function() {
37703 var col = (this.view.el.getWidth() / 7) - 3;
37704 // loop through cols, and setWidth
37705 for(var i =0 ; i < 7 ; i++){
37706 this.cm.setColumnWidth(i, col);
37709 setDate :function(date) {
37711 Roo.log('setDate?');
37713 this.resizeColumns();
37714 var vd = this.activeDate;
37715 this.activeDate = date;
37716 // if(vd && this.el){
37717 // var t = date.getTime();
37718 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37719 // Roo.log('using add remove');
37721 // this.fireEvent('monthchange', this, date);
37723 // this.cells.removeClass("fc-state-highlight");
37724 // this.cells.each(function(c){
37725 // if(c.dateValue == t){
37726 // c.addClass("fc-state-highlight");
37727 // setTimeout(function(){
37728 // try{c.dom.firstChild.focus();}catch(e){}
37738 var days = date.getDaysInMonth();
37740 var firstOfMonth = date.getFirstDateOfMonth();
37741 var startingPos = firstOfMonth.getDay()-this.startDay;
37743 if(startingPos < this.startDay){
37747 var pm = date.add(Date.MONTH, -1);
37748 var prevStart = pm.getDaysInMonth()-startingPos;
37752 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37754 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37755 //this.cells.addClassOnOver('fc-state-hover');
37757 var cells = this.cells.elements;
37758 var textEls = this.textNodes;
37760 //Roo.each(cells, function(cell){
37761 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37764 days += startingPos;
37766 // convert everything to numbers so it's fast
37767 var day = 86400000;
37768 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37771 //Roo.log(prevStart);
37773 var today = new Date().clearTime().getTime();
37774 var sel = date.clearTime().getTime();
37775 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37776 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37777 var ddMatch = this.disabledDatesRE;
37778 var ddText = this.disabledDatesText;
37779 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37780 var ddaysText = this.disabledDaysText;
37781 var format = this.format;
37783 var setCellClass = function(cal, cell){
37785 //Roo.log('set Cell Class');
37787 var t = d.getTime();
37792 cell.dateValue = t;
37794 cell.className += " fc-today";
37795 cell.className += " fc-state-highlight";
37796 cell.title = cal.todayText;
37799 // disable highlight in other month..
37800 cell.className += " fc-state-highlight";
37805 //cell.className = " fc-state-disabled";
37806 cell.title = cal.minText;
37810 //cell.className = " fc-state-disabled";
37811 cell.title = cal.maxText;
37815 if(ddays.indexOf(d.getDay()) != -1){
37816 // cell.title = ddaysText;
37817 // cell.className = " fc-state-disabled";
37820 if(ddMatch && format){
37821 var fvalue = d.dateFormat(format);
37822 if(ddMatch.test(fvalue)){
37823 cell.title = ddText.replace("%0", fvalue);
37824 cell.className = " fc-state-disabled";
37828 if (!cell.initialClassName) {
37829 cell.initialClassName = cell.dom.className;
37832 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37837 for(; i < startingPos; i++) {
37838 cells[i].dayName = (++prevStart);
37839 Roo.log(textEls[i]);
37840 d.setDate(d.getDate()+1);
37842 //cells[i].className = "fc-past fc-other-month";
37843 setCellClass(this, cells[i]);
37848 for(; i < days; i++){
37849 intDay = i - startingPos + 1;
37850 cells[i].dayName = (intDay);
37851 d.setDate(d.getDate()+1);
37853 cells[i].className = ''; // "x-date-active";
37854 setCellClass(this, cells[i]);
37858 for(; i < 42; i++) {
37859 //textEls[i].innerHTML = (++extraDays);
37861 d.setDate(d.getDate()+1);
37862 cells[i].dayName = (++extraDays);
37863 cells[i].className = "fc-future fc-other-month";
37864 setCellClass(this, cells[i]);
37867 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37869 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37871 // this will cause all the cells to mis
37874 for (var r = 0;r < 6;r++) {
37875 for (var c =0;c < 7;c++) {
37876 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37880 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37881 for(i=0;i<cells.length;i++) {
37883 this.cells.elements[i].dayName = cells[i].dayName ;
37884 this.cells.elements[i].className = cells[i].className;
37885 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37886 this.cells.elements[i].title = cells[i].title ;
37887 this.cells.elements[i].dateValue = cells[i].dateValue ;
37893 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37894 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37896 ////if(totalRows != 6){
37897 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37898 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37901 this.fireEvent('monthchange', this, date);
37906 * Returns the grid's SelectionModel.
37907 * @return {SelectionModel}
37909 getSelectionModel : function(){
37910 if(!this.selModel){
37911 this.selModel = new Roo.grid.CellSelectionModel();
37913 return this.selModel;
37917 this.eventStore.load()
37923 findCell : function(dt) {
37924 dt = dt.clearTime().getTime();
37926 this.cells.each(function(c){
37927 //Roo.log("check " +c.dateValue + '?=' + dt);
37928 if(c.dateValue == dt){
37938 findCells : function(rec) {
37939 var s = rec.data.start_dt.clone().clearTime().getTime();
37941 var e= rec.data.end_dt.clone().clearTime().getTime();
37944 this.cells.each(function(c){
37945 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37947 if(c.dateValue > e){
37950 if(c.dateValue < s){
37959 findBestRow: function(cells)
37963 for (var i =0 ; i < cells.length;i++) {
37964 ret = Math.max(cells[i].rows || 0,ret);
37971 addItem : function(rec)
37973 // look for vertical location slot in
37974 var cells = this.findCells(rec);
37976 rec.row = this.findBestRow(cells);
37978 // work out the location.
37982 for(var i =0; i < cells.length; i++) {
37990 if (crow.start.getY() == cells[i].getY()) {
37992 crow.end = cells[i];
38008 for (var i = 0; i < cells.length;i++) {
38009 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38016 clearEvents: function() {
38018 if (!this.eventStore.getCount()) {
38021 // reset number of rows in cells.
38022 Roo.each(this.cells.elements, function(c){
38026 this.eventStore.each(function(e) {
38027 this.clearEvent(e);
38032 clearEvent : function(ev)
38035 Roo.each(ev.els, function(el) {
38036 el.un('mouseenter' ,this.onEventEnter, this);
38037 el.un('mouseleave' ,this.onEventLeave, this);
38045 renderEvent : function(ev,ctr) {
38047 ctr = this.view.el.select('.fc-event-container',true).first();
38051 this.clearEvent(ev);
38057 var cells = ev.cells;
38058 var rows = ev.rows;
38059 this.fireEvent('eventrender', this, ev);
38061 for(var i =0; i < rows.length; i++) {
38065 cls += ' fc-event-start';
38067 if ((i+1) == rows.length) {
38068 cls += ' fc-event-end';
38071 //Roo.log(ev.data);
38072 // how many rows should it span..
38073 var cg = this.eventTmpl.append(ctr,Roo.apply({
38076 }, ev.data) , true);
38079 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38080 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38081 cg.on('click', this.onEventClick, this, ev);
38085 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38086 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38089 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38090 cg.setWidth(ebox.right - sbox.x -2);
38094 renderEvents: function()
38096 // first make sure there is enough space..
38098 if (!this.eventTmpl) {
38099 this.eventTmpl = new Roo.Template(
38100 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38101 '<div class="fc-event-inner">' +
38102 '<span class="fc-event-time">{time}</span>' +
38103 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38105 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38113 this.cells.each(function(c) {
38114 //Roo.log(c.select('.fc-day-content div',true).first());
38115 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38118 var ctr = this.view.el.select('.fc-event-container',true).first();
38121 this.eventStore.each(function(ev){
38123 this.renderEvent(ev);
38127 this.view.layout();
38131 onEventEnter: function (e, el,event,d) {
38132 this.fireEvent('evententer', this, el, event);
38135 onEventLeave: function (e, el,event,d) {
38136 this.fireEvent('eventleave', this, el, event);
38139 onEventClick: function (e, el,event,d) {
38140 this.fireEvent('eventclick', this, el, event);
38143 onMonthChange: function () {
38147 onLoad: function () {
38149 //Roo.log('calendar onload');
38151 if(this.eventStore.getCount() > 0){
38155 this.eventStore.each(function(d){
38160 if (typeof(add.end_dt) == 'undefined') {
38161 Roo.log("Missing End time in calendar data: ");
38165 if (typeof(add.start_dt) == 'undefined') {
38166 Roo.log("Missing Start time in calendar data: ");
38170 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38171 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38172 add.id = add.id || d.id;
38173 add.title = add.title || '??';
38181 this.renderEvents();
38191 render : function ()
38195 if (!this.view.el.hasClass('course-timesheet')) {
38196 this.view.el.addClass('course-timesheet');
38198 if (this.tsStyle) {
38203 Roo.log(_this.grid.view.el.getWidth());
38206 this.tsStyle = Roo.util.CSS.createStyleSheet({
38207 '.course-timesheet .x-grid-row' : {
38210 '.x-grid-row td' : {
38211 'vertical-align' : 0
38213 '.course-edit-link' : {
38215 'text-overflow' : 'ellipsis',
38216 'overflow' : 'hidden',
38217 'white-space' : 'nowrap',
38218 'cursor' : 'pointer'
38223 '.de-act-sup-link' : {
38224 'color' : 'purple',
38225 'text-decoration' : 'line-through'
38229 'text-decoration' : 'line-through'
38231 '.course-timesheet .course-highlight' : {
38232 'border-top-style': 'dashed !important',
38233 'border-bottom-bottom': 'dashed !important'
38235 '.course-timesheet .course-item' : {
38236 'font-family' : 'tahoma, arial, helvetica',
38237 'font-size' : '11px',
38238 'overflow' : 'hidden',
38239 'padding-left' : '10px',
38240 'padding-right' : '10px',
38241 'padding-top' : '10px'
38249 monitorWindowResize : false,
38250 cellrenderer : function(v,x,r)
38255 xtype: 'CellSelectionModel',
38262 beforeload : function (_self, options)
38264 options.params = options.params || {};
38265 options.params._month = _this.monthField.getValue();
38266 options.params.limit = 9999;
38267 options.params['sort'] = 'when_dt';
38268 options.params['dir'] = 'ASC';
38269 this.proxy.loadResponse = this.loadResponse;
38271 //this.addColumns();
38273 load : function (_self, records, options)
38275 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38276 // if you click on the translation.. you can edit it...
38277 var el = Roo.get(this);
38278 var id = el.dom.getAttribute('data-id');
38279 var d = el.dom.getAttribute('data-date');
38280 var t = el.dom.getAttribute('data-time');
38281 //var id = this.child('span').dom.textContent;
38284 Pman.Dialog.CourseCalendar.show({
38288 productitem_active : id ? 1 : 0
38290 _this.grid.ds.load({});
38295 _this.panel.fireEvent('resize', [ '', '' ]);
38298 loadResponse : function(o, success, response){
38299 // this is overridden on before load..
38301 Roo.log("our code?");
38302 //Roo.log(success);
38303 //Roo.log(response)
38304 delete this.activeRequest;
38306 this.fireEvent("loadexception", this, o, response);
38307 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38312 result = o.reader.read(response);
38314 Roo.log("load exception?");
38315 this.fireEvent("loadexception", this, o, response, e);
38316 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38319 Roo.log("ready...");
38320 // loop through result.records;
38321 // and set this.tdate[date] = [] << array of records..
38323 Roo.each(result.records, function(r){
38325 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38326 _this.tdata[r.data.when_dt.format('j')] = [];
38328 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38331 //Roo.log(_this.tdata);
38333 result.records = [];
38334 result.totalRecords = 6;
38336 // let's generate some duumy records for the rows.
38337 //var st = _this.dateField.getValue();
38339 // work out monday..
38340 //st = st.add(Date.DAY, -1 * st.format('w'));
38342 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38344 var firstOfMonth = date.getFirstDayOfMonth();
38345 var days = date.getDaysInMonth();
38347 var firstAdded = false;
38348 for (var i = 0; i < result.totalRecords ; i++) {
38349 //var d= st.add(Date.DAY, i);
38352 for(var w = 0 ; w < 7 ; w++){
38353 if(!firstAdded && firstOfMonth != w){
38360 var dd = (d > 0 && d < 10) ? "0"+d : d;
38361 row['weekday'+w] = String.format(
38362 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38363 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38365 date.format('Y-m-')+dd
38368 if(typeof(_this.tdata[d]) != 'undefined'){
38369 Roo.each(_this.tdata[d], function(r){
38373 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38374 if(r.parent_id*1>0){
38375 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38378 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38379 deactive = 'de-act-link';
38382 row['weekday'+w] += String.format(
38383 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38385 r.product_id_name, //1
38386 r.when_dt.format('h:ia'), //2
38396 // only do this if something added..
38398 result.records.push(_this.grid.dataSource.reader.newRow(row));
38402 // push it twice. (second one with an hour..
38406 this.fireEvent("load", this, o, o.request.arg);
38407 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38409 sortInfo : {field: 'when_dt', direction : 'ASC' },
38411 xtype: 'HttpProxy',
38414 url : baseURL + '/Roo/Shop_course.php'
38417 xtype: 'JsonReader',
38434 'name': 'parent_id',
38438 'name': 'product_id',
38442 'name': 'productitem_id',
38460 click : function (_self, e)
38462 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38463 sd.setMonth(sd.getMonth()-1);
38464 _this.monthField.setValue(sd.format('Y-m-d'));
38465 _this.grid.ds.load({});
38471 xtype: 'Separator',
38475 xtype: 'MonthField',
38478 render : function (_self)
38480 _this.monthField = _self;
38481 // _this.monthField.set today
38483 select : function (combo, date)
38485 _this.grid.ds.load({});
38488 value : (function() { return new Date(); })()
38491 xtype: 'Separator',
38497 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38507 click : function (_self, e)
38509 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38510 sd.setMonth(sd.getMonth()+1);
38511 _this.monthField.setValue(sd.format('Y-m-d'));
38512 _this.grid.ds.load({});
38525 * Ext JS Library 1.1.1
38526 * Copyright(c) 2006-2007, Ext JS, LLC.
38528 * Originally Released Under LGPL - original licence link has changed is not relivant.
38531 * <script type="text/javascript">
38535 * @class Roo.LoadMask
38536 * A simple utility class for generically masking elements while loading data. If the element being masked has
38537 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38538 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38539 * element's UpdateManager load indicator and will be destroyed after the initial load.
38541 * Create a new LoadMask
38542 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38543 * @param {Object} config The config object
38545 Roo.LoadMask = function(el, config){
38546 this.el = Roo.get(el);
38547 Roo.apply(this, config);
38549 this.store.on('beforeload', this.onBeforeLoad, this);
38550 this.store.on('load', this.onLoad, this);
38551 this.store.on('loadexception', this.onLoadException, this);
38552 this.removeMask = false;
38554 var um = this.el.getUpdateManager();
38555 um.showLoadIndicator = false; // disable the default indicator
38556 um.on('beforeupdate', this.onBeforeLoad, this);
38557 um.on('update', this.onLoad, this);
38558 um.on('failure', this.onLoad, this);
38559 this.removeMask = true;
38563 Roo.LoadMask.prototype = {
38565 * @cfg {Boolean} removeMask
38566 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38567 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38570 * @cfg {String} msg
38571 * The text to display in a centered loading message box (defaults to 'Loading...')
38573 msg : 'Loading...',
38575 * @cfg {String} msgCls
38576 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38578 msgCls : 'x-mask-loading',
38581 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38587 * Disables the mask to prevent it from being displayed
38589 disable : function(){
38590 this.disabled = true;
38594 * Enables the mask so that it can be displayed
38596 enable : function(){
38597 this.disabled = false;
38600 onLoadException : function()
38602 Roo.log(arguments);
38604 if (typeof(arguments[3]) != 'undefined') {
38605 Roo.MessageBox.alert("Error loading",arguments[3]);
38609 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38610 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38617 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38620 onLoad : function()
38622 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38626 onBeforeLoad : function(){
38627 if(!this.disabled){
38628 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38633 destroy : function(){
38635 this.store.un('beforeload', this.onBeforeLoad, this);
38636 this.store.un('load', this.onLoad, this);
38637 this.store.un('loadexception', this.onLoadException, this);
38639 var um = this.el.getUpdateManager();
38640 um.un('beforeupdate', this.onBeforeLoad, this);
38641 um.un('update', this.onLoad, this);
38642 um.un('failure', this.onLoad, this);
38647 * Ext JS Library 1.1.1
38648 * Copyright(c) 2006-2007, Ext JS, LLC.
38650 * Originally Released Under LGPL - original licence link has changed is not relivant.
38653 * <script type="text/javascript">
38658 * @class Roo.XTemplate
38659 * @extends Roo.Template
38660 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38662 var t = new Roo.XTemplate(
38663 '<select name="{name}">',
38664 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38668 // then append, applying the master template values
38671 * Supported features:
38676 {a_variable} - output encoded.
38677 {a_variable.format:("Y-m-d")} - call a method on the variable
38678 {a_variable:raw} - unencoded output
38679 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38680 {a_variable:this.method_on_template(...)} - call a method on the template object.
38685 <tpl for="a_variable or condition.."></tpl>
38686 <tpl if="a_variable or condition"></tpl>
38687 <tpl exec="some javascript"></tpl>
38688 <tpl name="named_template"></tpl> (experimental)
38690 <tpl for="."></tpl> - just iterate the property..
38691 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38695 Roo.XTemplate = function()
38697 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38704 Roo.extend(Roo.XTemplate, Roo.Template, {
38707 * The various sub templates
38712 * basic tag replacing syntax
38715 * // you can fake an object call by doing this
38719 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38722 * compile the template
38724 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38727 compile: function()
38731 s = ['<tpl>', s, '</tpl>'].join('');
38733 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38734 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38735 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38736 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38737 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38742 while(true == !!(m = s.match(re))){
38743 var forMatch = m[0].match(nameRe),
38744 ifMatch = m[0].match(ifRe),
38745 execMatch = m[0].match(execRe),
38746 namedMatch = m[0].match(namedRe),
38751 name = forMatch && forMatch[1] ? forMatch[1] : '';
38754 // if - puts fn into test..
38755 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38757 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38762 // exec - calls a function... returns empty if true is returned.
38763 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38765 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38773 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38774 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38775 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38778 var uid = namedMatch ? namedMatch[1] : id;
38782 id: namedMatch ? namedMatch[1] : id,
38789 s = s.replace(m[0], '');
38791 s = s.replace(m[0], '{xtpl'+ id + '}');
38796 for(var i = tpls.length-1; i >= 0; --i){
38797 this.compileTpl(tpls[i]);
38798 this.tpls[tpls[i].id] = tpls[i];
38800 this.master = tpls[tpls.length-1];
38804 * same as applyTemplate, except it's done to one of the subTemplates
38805 * when using named templates, you can do:
38807 * var str = pl.applySubTemplate('your-name', values);
38810 * @param {Number} id of the template
38811 * @param {Object} values to apply to template
38812 * @param {Object} parent (normaly the instance of this object)
38814 applySubTemplate : function(id, values, parent)
38818 var t = this.tpls[id];
38822 if(t.test && !t.test.call(this, values, parent)){
38826 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38827 Roo.log(e.toString());
38833 if(t.exec && t.exec.call(this, values, parent)){
38837 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38838 Roo.log(e.toString());
38843 var vs = t.target ? t.target.call(this, values, parent) : values;
38844 parent = t.target ? values : parent;
38845 if(t.target && vs instanceof Array){
38847 for(var i = 0, len = vs.length; i < len; i++){
38848 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38850 return buf.join('');
38852 return t.compiled.call(this, vs, parent);
38854 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38855 Roo.log(e.toString());
38856 Roo.log(t.compiled);
38861 compileTpl : function(tpl)
38863 var fm = Roo.util.Format;
38864 var useF = this.disableFormats !== true;
38865 var sep = Roo.isGecko ? "+" : ",";
38866 var undef = function(str) {
38867 Roo.log("Property not found :" + str);
38871 var fn = function(m, name, format, args)
38873 //Roo.log(arguments);
38874 args = args ? args.replace(/\\'/g,"'") : args;
38875 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38876 if (typeof(format) == 'undefined') {
38877 format= 'htmlEncode';
38879 if (format == 'raw' ) {
38883 if(name.substr(0, 4) == 'xtpl'){
38884 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38887 // build an array of options to determine if value is undefined..
38889 // basically get 'xxxx.yyyy' then do
38890 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38891 // (function () { Roo.log("Property not found"); return ''; })() :
38896 Roo.each(name.split('.'), function(st) {
38897 lookfor += (lookfor.length ? '.': '') + st;
38898 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38901 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38904 if(format && useF){
38906 args = args ? ',' + args : "";
38908 if(format.substr(0, 5) != "this."){
38909 format = "fm." + format + '(';
38911 format = 'this.call("'+ format.substr(5) + '", ';
38915 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38919 // called with xxyx.yuu:(test,test)
38921 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38923 // raw.. - :raw modifier..
38924 return "'"+ sep + udef_st + name + ")"+sep+"'";
38928 // branched to use + in gecko and [].join() in others
38930 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38931 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38934 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38935 body.push(tpl.body.replace(/(\r\n|\n)/g,
38936 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38937 body.push("'].join('');};};");
38938 body = body.join('');
38941 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38943 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38949 applyTemplate : function(values){
38950 return this.master.compiled.call(this, values, {});
38951 //var s = this.subs;
38954 apply : function(){
38955 return this.applyTemplate.apply(this, arguments);
38960 Roo.XTemplate.from = function(el){
38961 el = Roo.getDom(el);
38962 return new Roo.XTemplate(el.value || el.innerHTML);