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 [required] 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.DataReader} reader [required] 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>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
672 data : data, // array of key=>value data like JsonReader
679 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680 * passed the following arguments:<ul>
681 * <li>r : Roo.data.Record[]</li>
682 * <li>options: Options object from the load call</li>
683 * <li>success: Boolean success indicator</li></ul></li>
684 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
688 load : function(options){
689 options = options || {};
690 if(this.fireEvent("beforeload", this, options) !== false){
691 this.storeOptions(options);
692 var p = Roo.apply(options.params || {}, this.baseParams);
693 // if meta was not loaded from remote source.. try requesting it.
694 if (!this.reader.metaFromRemote) {
697 if(this.sortInfo && this.remoteSort){
698 var pn = this.paramNames;
699 p[pn["sort"]] = this.sortInfo.field;
700 p[pn["dir"]] = this.sortInfo.direction;
702 if (this.multiSort) {
703 var pn = this.paramNames;
704 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
707 this.proxy.load(p, this.reader, this.loadRecords, this, options);
712 * Reloads the Record cache from the configured Proxy using the configured Reader and
713 * the options from the last load operation performed.
714 * @param {Object} options (optional) An object containing properties which may override the options
715 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716 * the most recently used options are reused).
718 reload : function(options){
719 this.load(Roo.applyIf(options||{}, this.lastOptions));
723 // Called as a callback by the Reader during a load operation.
724 loadRecords : function(o, options, success){
727 if(success !== false){
728 this.fireEvent("load", this, [], options, o);
730 if(options.callback){
731 options.callback.call(options.scope || this, [], options, false);
735 // if data returned failure - throw an exception.
736 if (o.success === false) {
737 // show a message if no listener is registered.
738 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
741 // loadmask wil be hooked into this..
742 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
745 var r = o.records, t = o.totalRecords || r.length;
747 this.fireEvent("beforeloadadd", this, r, options, o);
749 if(!options || options.add !== true){
750 if(this.pruneModifiedRecords){
753 for(var i = 0, len = r.length; i < len; i++){
757 this.data = this.snapshot;
758 delete this.snapshot;
762 this.totalLength = t;
764 this.fireEvent("datachanged", this);
766 this.totalLength = Math.max(t, this.data.length+r.length);
770 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
772 var e = new Roo.data.Record({});
774 e.set(this.parent.displayField, this.parent.emptyTitle);
775 e.set(this.parent.valueField, '');
780 this.fireEvent("load", this, r, options, o);
781 if(options.callback){
782 options.callback.call(options.scope || this, r, options, true);
788 * Loads data from a passed data block. A Reader which understands the format of the data
789 * must have been configured in the constructor.
790 * @param {Object} data The data block from which to read the Records. The format of the data expected
791 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
794 loadData : function(o, append){
795 var r = this.reader.readRecords(o);
796 this.loadRecords(r, {add: append}, true);
800 * using 'cn' the nested child reader read the child array into it's child stores.
801 * @param {Object} rec The record with a 'children array
803 loadDataFromChildren : function(rec)
805 this.loadData(this.reader.toLoadData(rec));
810 * Gets the number of cached records.
812 * <em>If using paging, this may not be the total size of the dataset. If the data object
813 * used by the Reader contains the dataset size, then the getTotalCount() function returns
814 * the data set size</em>
816 getCount : function(){
817 return this.data.length || 0;
821 * Gets the total number of records in the dataset as returned by the server.
823 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824 * the dataset size</em>
826 getTotalCount : function(){
827 return this.totalLength || 0;
831 * Returns the sort state of the Store as an object with two properties:
833 field {String} The name of the field by which the Records are sorted
834 direction {String} The sort order, "ASC" or "DESC"
837 getSortState : function(){
838 return this.sortInfo;
842 applySort : function(){
843 if(this.sortInfo && !this.remoteSort){
844 var s = this.sortInfo, f = s.field;
845 var st = this.fields.get(f).sortType;
846 var fn = function(r1, r2){
847 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
850 this.data.sort(s.direction, fn);
851 if(this.snapshot && this.snapshot != this.data){
852 this.snapshot.sort(s.direction, fn);
858 * Sets the default sort column and order to be used by the next load operation.
859 * @param {String} fieldName The name of the field to sort by.
860 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
862 setDefaultSort : function(field, dir){
863 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
868 * If remote sorting is used, the sort is performed on the server, and the cache is
869 * reloaded. If local sorting is used, the cache is sorted internally.
870 * @param {String} fieldName The name of the field to sort by.
871 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
873 sort : function(fieldName, dir){
874 var f = this.fields.get(fieldName);
876 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
878 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
884 this.sortToggle[f.name] = dir;
885 this.sortInfo = {field: f.name, direction: dir};
886 if(!this.remoteSort){
888 this.fireEvent("datachanged", this);
890 this.load(this.lastOptions);
895 * Calls the specified function for each of the Records in the cache.
896 * @param {Function} fn The function to call. The Record is passed as the first parameter.
897 * Returning <em>false</em> aborts and exits the iteration.
898 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
900 each : function(fn, scope){
901 this.data.each(fn, scope);
905 * Gets all records modified since the last commit. Modified records are persisted across load operations
906 * (e.g., during paging).
907 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
909 getModifiedRecords : function(){
910 return this.modified;
914 createFilterFn : function(property, value, anyMatch){
915 if(!value.exec){ // not a regex
916 value = String(value);
917 if(value.length == 0){
920 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
923 return value.test(r.data[property]);
928 * Sums the value of <i>property</i> for each record between start and end and returns the result.
929 * @param {String} property A field on your records
930 * @param {Number} start The record index to start at (defaults to 0)
931 * @param {Number} end The last record index to include (defaults to length - 1)
932 * @return {Number} The sum
934 sum : function(property, start, end){
935 var rs = this.data.items, v = 0;
937 end = (end || end === 0) ? end : rs.length-1;
939 for(var i = start; i <= end; i++){
940 v += (rs[i].data[property] || 0);
946 * Filter the records by a specified property.
947 * @param {String} field A field on your records
948 * @param {String/RegExp} value Either a string that the field
949 * should start with or a RegExp to test against the field
950 * @param {Boolean} anyMatch True to match any part not just the beginning
952 filter : function(property, value, anyMatch){
953 var fn = this.createFilterFn(property, value, anyMatch);
954 return fn ? this.filterBy(fn) : this.clearFilter();
958 * Filter by a function. The specified function will be called with each
959 * record in this data source. If the function returns true the record is included,
960 * otherwise it is filtered.
961 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962 * @param {Object} scope (optional) The scope of the function (defaults to this)
964 filterBy : function(fn, scope){
965 this.snapshot = this.snapshot || this.data;
966 this.data = this.queryBy(fn, scope||this);
967 this.fireEvent("datachanged", this);
971 * Query the records by a specified property.
972 * @param {String} field A field on your records
973 * @param {String/RegExp} value Either a string that the field
974 * should start with or a RegExp to test against the field
975 * @param {Boolean} anyMatch True to match any part not just the beginning
976 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
978 query : function(property, value, anyMatch){
979 var fn = this.createFilterFn(property, value, anyMatch);
980 return fn ? this.queryBy(fn) : this.data.clone();
984 * Query by a function. The specified function will be called with each
985 * record in this data source. If the function returns true the record is included
987 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988 * @param {Object} scope (optional) The scope of the function (defaults to this)
989 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
991 queryBy : function(fn, scope){
992 var data = this.snapshot || this.data;
993 return data.filterBy(fn, scope||this);
997 * Collects unique values for a particular dataIndex from this store.
998 * @param {String} dataIndex The property to collect
999 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001 * @return {Array} An array of the unique values
1003 collect : function(dataIndex, allowNull, bypassFilter){
1004 var d = (bypassFilter === true && this.snapshot) ?
1005 this.snapshot.items : this.data.items;
1006 var v, sv, r = [], l = {};
1007 for(var i = 0, len = d.length; i < len; i++){
1008 v = d[i].data[dataIndex];
1010 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1019 * Revert to a view of the Record cache with no filtering applied.
1020 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1022 clearFilter : function(suppressEvent){
1023 if(this.snapshot && this.snapshot != this.data){
1024 this.data = this.snapshot;
1025 delete this.snapshot;
1026 if(suppressEvent !== true){
1027 this.fireEvent("datachanged", this);
1033 afterEdit : function(record){
1034 if(this.modified.indexOf(record) == -1){
1035 this.modified.push(record);
1037 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1041 afterReject : function(record){
1042 this.modified.remove(record);
1043 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1047 afterCommit : function(record){
1048 this.modified.remove(record);
1049 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1053 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1056 commitChanges : function(){
1057 var m = this.modified.slice(0);
1059 for(var i = 0, len = m.length; i < len; i++){
1065 * Cancel outstanding changes on all changed records.
1067 rejectChanges : function(){
1068 var m = this.modified.slice(0);
1070 for(var i = 0, len = m.length; i < len; i++){
1075 onMetaChange : function(meta, rtype, o){
1076 this.recordType = rtype;
1077 this.fields = rtype.prototype.fields;
1078 delete this.snapshot;
1079 this.sortInfo = meta.sortInfo || this.sortInfo;
1081 this.fireEvent('metachange', this, this.reader.meta);
1084 moveIndex : function(data, type)
1086 var index = this.indexOf(data);
1088 var newIndex = index + type;
1092 this.insert(newIndex, data);
1097 * Ext JS Library 1.1.1
1098 * Copyright(c) 2006-2007, Ext JS, LLC.
1100 * Originally Released Under LGPL - original licence link has changed is not relivant.
1103 * <script type="text/javascript">
1107 * @class Roo.data.SimpleStore
1108 * @extends Roo.data.Store
1109 * Small helper class to make creating Stores from Array data easier.
1110 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111 * @cfg {Array} fields An array of field definition objects, or field name strings.
1112 * @cfg {Object} an existing reader (eg. copied from another store)
1113 * @cfg {Array} data The multi-dimensional array of data
1114 * @cfg {Roo.data.DataProxy} proxy [not-required]
1115 * @cfg {Roo.data.Reader} reader [not-required]
1117 * @param {Object} config
1119 Roo.data.SimpleStore = function(config)
1121 Roo.data.SimpleStore.superclass.constructor.call(this, {
1123 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1126 Roo.data.Record.create(config.fields)
1128 proxy : new Roo.data.MemoryProxy(config.data)
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1134 * Ext JS Library 1.1.1
1135 * Copyright(c) 2006-2007, Ext JS, LLC.
1137 * Originally Released Under LGPL - original licence link has changed is not relivant.
1140 * <script type="text/javascript">
1145 * @extends Roo.data.Store
1146 * @class Roo.data.JsonStore
1147 * Small helper class to make creating Stores for JSON data easier. <br/>
1149 var store = new Roo.data.JsonStore({
1150 url: 'get-images.php',
1152 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1155 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156 * JsonReader and HttpProxy (unless inline data is provided).</b>
1157 * @cfg {Array} fields An array of field definition objects, or field name strings.
1159 * @param {Object} config
1161 Roo.data.JsonStore = function(c){
1162 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164 reader: new Roo.data.JsonReader(c, c.fields)
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1169 * Ext JS Library 1.1.1
1170 * Copyright(c) 2006-2007, Ext JS, LLC.
1172 * Originally Released Under LGPL - original licence link has changed is not relivant.
1175 * <script type="text/javascript">
1179 Roo.data.Field = function(config){
1180 if(typeof config == "string"){
1181 config = {name: config};
1183 Roo.apply(this, config);
1189 var st = Roo.data.SortTypes;
1190 // named sortTypes are supported, here we look them up
1191 if(typeof this.sortType == "string"){
1192 this.sortType = st[this.sortType];
1195 // set default sortType for strings and dates
1199 this.sortType = st.asUCString;
1202 this.sortType = st.asDate;
1205 this.sortType = st.none;
1210 var stripRe = /[\$,%]/g;
1212 // prebuilt conversion function for this field, instead of
1213 // switching every time we're reading a value
1215 var cv, dateFormat = this.dateFormat;
1220 cv = function(v){ return v; };
1223 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1227 return v !== undefined && v !== null && v !== '' ?
1228 parseInt(String(v).replace(stripRe, ""), 10) : '';
1233 return v !== undefined && v !== null && v !== '' ?
1234 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1239 cv = function(v){ return v === true || v === "true" || v == 1; };
1246 if(v instanceof Date){
1250 if(dateFormat == "timestamp"){
1251 return new Date(v*1000);
1253 return Date.parseDate(v, dateFormat);
1255 var parsed = Date.parse(v);
1256 return parsed ? new Date(parsed) : null;
1265 Roo.data.Field.prototype = {
1273 * Ext JS Library 1.1.1
1274 * Copyright(c) 2006-2007, Ext JS, LLC.
1276 * Originally Released Under LGPL - original licence link has changed is not relivant.
1279 * <script type="text/javascript">
1282 // Base class for reading structured data from a data source. This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1286 * @class Roo.data.DataReader
1288 * Base class for reading structured data from a data source. This class is intended to be
1289 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1292 Roo.data.DataReader = function(meta, recordType){
1296 this.recordType = recordType instanceof Array ?
1297 Roo.data.Record.create(recordType) : recordType;
1300 Roo.data.DataReader.prototype = {
1303 readerType : 'Data',
1305 * Create an empty record
1306 * @param {Object} data (optional) - overlay some values
1307 * @return {Roo.data.Record} record created.
1309 newRow : function(d) {
1311 this.recordType.prototype.fields.each(function(c) {
1313 case 'int' : da[c.name] = 0; break;
1314 case 'date' : da[c.name] = new Date(); break;
1315 case 'float' : da[c.name] = 0.0; break;
1316 case 'boolean' : da[c.name] = false; break;
1317 default : da[c.name] = ""; break;
1321 return new this.recordType(Roo.apply(da, d));
1327 * Ext JS Library 1.1.1
1328 * Copyright(c) 2006-2007, Ext JS, LLC.
1330 * Originally Released Under LGPL - original licence link has changed is not relivant.
1333 * <script type="text/javascript">
1337 * @class Roo.data.DataProxy
1338 * @extends Roo.util.Observable
1340 * This class is an abstract base class for implementations which provide retrieval of
1341 * unformatted data objects.<br>
1343 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344 * (of the appropriate type which knows how to parse the data object) to provide a block of
1345 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1347 * Custom implementations must implement the load method as described in
1348 * {@link Roo.data.HttpProxy#load}.
1350 Roo.data.DataProxy = function(){
1354 * Fires before a network request is made to retrieve a data object.
1355 * @param {Object} This DataProxy object.
1356 * @param {Object} params The params parameter to the load function.
1361 * Fires before the load method's callback is called.
1362 * @param {Object} This DataProxy object.
1363 * @param {Object} o The data object.
1364 * @param {Object} arg The callback argument object passed to the load function.
1368 * @event loadexception
1369 * Fires if an Exception occurs during data retrieval.
1370 * @param {Object} This DataProxy object.
1371 * @param {Object} o The data object.
1372 * @param {Object} arg The callback argument object passed to the load function.
1373 * @param {Object} e The Exception.
1375 loadexception : true
1377 Roo.data.DataProxy.superclass.constructor.call(this);
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1383 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1387 * Ext JS Library 1.1.1
1388 * Copyright(c) 2006-2007, Ext JS, LLC.
1390 * Originally Released Under LGPL - original licence link has changed is not relivant.
1393 * <script type="text/javascript">
1396 * @class Roo.data.MemoryProxy
1397 * @extends Roo.data.DataProxy
1398 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1399 * to the Reader when its load method is called.
1401 * @param {Object} config A config object containing the objects needed for the Store to access data,
1403 Roo.data.MemoryProxy = function(data){
1404 if (typeof(data) != 'undefined' && typeof(data.data) != 'undefined') {
1407 Roo.data.MemoryProxy.superclass.constructor.call(this);
1411 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1414 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1417 * Load data from the requested source (in this case an in-memory
1418 * data object passed to the constructor), read the data object into
1419 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1420 * process that block using the passed callback.
1421 * @param {Object} params This parameter is not used by the MemoryProxy class.
1422 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1423 * object into a block of Roo.data.Records.
1424 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1425 * The function must be passed <ul>
1426 * <li>The Record block object</li>
1427 * <li>The "arg" argument from the load function</li>
1428 * <li>A boolean success indicator</li>
1430 * @param {Object} scope The scope in which to call the callback
1431 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1433 load : function(params, reader, callback, scope, arg){
1434 params = params || {};
1437 result = reader.readRecords(params.data ? params.data :this.data);
1439 this.fireEvent("loadexception", this, arg, null, e);
1440 callback.call(scope, null, arg, false);
1443 callback.call(scope, result, arg, true);
1447 update : function(params, records){
1452 * Ext JS Library 1.1.1
1453 * Copyright(c) 2006-2007, Ext JS, LLC.
1455 * Originally Released Under LGPL - original licence link has changed is not relivant.
1458 * <script type="text/javascript">
1461 * @class Roo.data.HttpProxy
1462 * @extends Roo.data.DataProxy
1463 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1464 * configured to reference a certain URL.<br><br>
1466 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1467 * from which the running page was served.<br><br>
1469 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1471 * Be aware that to enable the browser to parse an XML document, the server must set
1472 * the Content-Type header in the HTTP response to "text/xml".
1474 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1475 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1476 * will be used to make the request.
1478 Roo.data.HttpProxy = function(conn){
1479 Roo.data.HttpProxy.superclass.constructor.call(this);
1480 // is conn a conn config or a real conn?
1482 this.useAjax = !conn || !conn.events;
1486 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1487 // thse are take from connection...
1490 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1493 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1494 * extra parameters to each request made by this object. (defaults to undefined)
1497 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1498 * to each request made by this object. (defaults to undefined)
1501 * @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)
1504 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1507 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1513 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1517 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1518 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1519 * a finer-grained basis than the DataProxy events.
1521 getConnection : function(){
1522 return this.useAjax ? Roo.Ajax : this.conn;
1526 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1527 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1528 * process that block using the passed callback.
1529 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1530 * for the request to the remote server.
1531 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1532 * object into a block of Roo.data.Records.
1533 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1534 * The function must be passed <ul>
1535 * <li>The Record block object</li>
1536 * <li>The "arg" argument from the load function</li>
1537 * <li>A boolean success indicator</li>
1539 * @param {Object} scope The scope in which to call the callback
1540 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1542 load : function(params, reader, callback, scope, arg){
1543 if(this.fireEvent("beforeload", this, params) !== false){
1545 params : params || {},
1547 callback : callback,
1552 callback : this.loadResponse,
1556 Roo.applyIf(o, this.conn);
1557 if(this.activeRequest){
1558 Roo.Ajax.abort(this.activeRequest);
1560 this.activeRequest = Roo.Ajax.request(o);
1562 this.conn.request(o);
1565 callback.call(scope||this, null, arg, false);
1570 loadResponse : function(o, success, response){
1571 delete this.activeRequest;
1573 this.fireEvent("loadexception", this, o, response);
1574 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1579 result = o.reader.read(response);
1582 o.raw = { errorMsg : response.responseText };
1583 this.fireEvent("loadexception", this, o, response, e);
1584 o.request.callback.call(o.request.scope, o, o.request.arg, false);
1588 this.fireEvent("load", this, o, o.request.arg);
1589 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1593 update : function(dataSet){
1598 updateResponse : function(dataSet){
1603 * Ext JS Library 1.1.1
1604 * Copyright(c) 2006-2007, Ext JS, LLC.
1606 * Originally Released Under LGPL - original licence link has changed is not relivant.
1609 * <script type="text/javascript">
1613 * @class Roo.data.ScriptTagProxy
1614 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1615 * other than the originating domain of the running page.<br><br>
1617 * <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
1618 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1620 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1621 * source code that is used as the source inside a <script> tag.<br><br>
1623 * In order for the browser to process the returned data, the server must wrap the data object
1624 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1625 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1626 * depending on whether the callback name was passed:
1629 boolean scriptTag = false;
1630 String cb = request.getParameter("callback");
1633 response.setContentType("text/javascript");
1635 response.setContentType("application/x-json");
1637 Writer out = response.getWriter();
1639 out.write(cb + "(");
1641 out.print(dataBlock.toJsonString());
1648 * @param {Object} config A configuration object.
1650 Roo.data.ScriptTagProxy = function(config){
1651 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1652 Roo.apply(this, config);
1653 this.head = document.getElementsByTagName("head")[0];
1656 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1658 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1660 * @cfg {String} url The URL from which to request the data object.
1663 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1667 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1668 * the server the name of the callback function set up by the load call to process the returned data object.
1669 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1670 * javascript output which calls this named function passing the data object as its only parameter.
1672 callbackParam : "callback",
1674 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1675 * name to the request.
1680 * Load data from the configured URL, read the data object into
1681 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1682 * process that block using the passed callback.
1683 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1684 * for the request to the remote server.
1685 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1686 * object into a block of Roo.data.Records.
1687 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1688 * The function must be passed <ul>
1689 * <li>The Record block object</li>
1690 * <li>The "arg" argument from the load function</li>
1691 * <li>A boolean success indicator</li>
1693 * @param {Object} scope The scope in which to call the callback
1694 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1696 load : function(params, reader, callback, scope, arg){
1697 if(this.fireEvent("beforeload", this, params) !== false){
1699 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1702 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1704 url += "&_dc=" + (new Date().getTime());
1706 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1709 cb : "stcCallback"+transId,
1710 scriptId : "stcScript"+transId,
1714 callback : callback,
1720 window[trans.cb] = function(o){
1721 conn.handleResponse(o, trans);
1724 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1726 if(this.autoAbort !== false){
1730 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1732 var script = document.createElement("script");
1733 script.setAttribute("src", url);
1734 script.setAttribute("type", "text/javascript");
1735 script.setAttribute("id", trans.scriptId);
1736 this.head.appendChild(script);
1740 callback.call(scope||this, null, arg, false);
1745 isLoading : function(){
1746 return this.trans ? true : false;
1750 * Abort the current server request.
1753 if(this.isLoading()){
1754 this.destroyTrans(this.trans);
1759 destroyTrans : function(trans, isLoaded){
1760 this.head.removeChild(document.getElementById(trans.scriptId));
1761 clearTimeout(trans.timeoutId);
1763 window[trans.cb] = undefined;
1765 delete window[trans.cb];
1768 // if hasn't been loaded, wait for load to remove it to prevent script error
1769 window[trans.cb] = function(){
1770 window[trans.cb] = undefined;
1772 delete window[trans.cb];
1779 handleResponse : function(o, trans){
1781 this.destroyTrans(trans, true);
1784 result = trans.reader.readRecords(o);
1786 this.fireEvent("loadexception", this, o, trans.arg, e);
1787 trans.callback.call(trans.scope||window, null, trans.arg, false);
1790 this.fireEvent("load", this, o, trans.arg);
1791 trans.callback.call(trans.scope||window, result, trans.arg, true);
1795 handleFailure : function(trans){
1797 this.destroyTrans(trans, false);
1798 this.fireEvent("loadexception", this, null, trans.arg);
1799 trans.callback.call(trans.scope||window, null, trans.arg, false);
1803 * Ext JS Library 1.1.1
1804 * Copyright(c) 2006-2007, Ext JS, LLC.
1806 * Originally Released Under LGPL - original licence link has changed is not relivant.
1809 * <script type="text/javascript">
1813 * @class Roo.data.JsonReader
1814 * @extends Roo.data.DataReader
1815 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1816 * based on mappings in a provided Roo.data.Record constructor.
1818 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1819 * in the reply previously.
1824 var RecordDef = Roo.data.Record.create([
1825 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1826 {name: 'occupation'} // This field will use "occupation" as the mapping.
1828 var myReader = new Roo.data.JsonReader({
1829 totalProperty: "results", // The property which contains the total dataset size (optional)
1830 root: "rows", // The property which contains an Array of row objects
1831 id: "id" // The property within each row object that provides an ID for the record (optional)
1835 * This would consume a JSON file like this:
1837 { 'results': 2, 'rows': [
1838 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1839 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1842 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1843 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1844 * paged from the remote server.
1845 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1846 * @cfg {String} root name of the property which contains the Array of row objects.
1847 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1848 * @cfg {Array} fields Array of field definition objects
1850 * Create a new JsonReader
1851 * @param {Object} meta Metadata configuration options
1852 * @param {Object} recordType Either an Array of field definition objects,
1853 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1855 Roo.data.JsonReader = function(meta, recordType){
1858 // set some defaults:
1860 totalProperty: 'total',
1861 successProperty : 'success',
1866 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1868 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1870 readerType : 'Json',
1873 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1874 * Used by Store query builder to append _requestMeta to params.
1877 metaFromRemote : false,
1879 * This method is only used by a DataProxy which has retrieved data from a remote server.
1880 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1881 * @return {Object} data A data block which is used by an Roo.data.Store object as
1882 * a cache of Roo.data.Records.
1884 read : function(response){
1885 var json = response.responseText;
1887 var o = /* eval:var:o */ eval("("+json+")");
1889 throw {message: "JsonReader.read: Json object not found"};
1895 this.metaFromRemote = true;
1896 this.meta = o.metaData;
1897 this.recordType = Roo.data.Record.create(o.metaData.fields);
1898 this.onMetaChange(this.meta, this.recordType, o);
1900 return this.readRecords(o);
1903 // private function a store will implement
1904 onMetaChange : function(meta, recordType, o){
1911 simpleAccess: function(obj, subsc) {
1918 getJsonAccessor: function(){
1920 return function(expr) {
1922 return(re.test(expr))
1923 ? new Function("obj", "return obj." + expr)
1933 * Create a data block containing Roo.data.Records from an XML document.
1934 * @param {Object} o An object which contains an Array of row objects in the property specified
1935 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1936 * which contains the total size of the dataset.
1937 * @return {Object} data A data block which is used by an Roo.data.Store object as
1938 * a cache of Roo.data.Records.
1940 readRecords : function(o){
1942 * After any data loads, the raw JSON data is available for further custom processing.
1946 var s = this.meta, Record = this.recordType,
1947 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1949 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1951 if(s.totalProperty) {
1952 this.getTotal = this.getJsonAccessor(s.totalProperty);
1954 if(s.successProperty) {
1955 this.getSuccess = this.getJsonAccessor(s.successProperty);
1957 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1959 var g = this.getJsonAccessor(s.id);
1960 this.getId = function(rec) {
1962 return (r === undefined || r === "") ? null : r;
1965 this.getId = function(){return null;};
1968 for(var jj = 0; jj < fl; jj++){
1970 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1971 this.ef[jj] = this.getJsonAccessor(map);
1975 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1976 if(s.totalProperty){
1977 var vt = parseInt(this.getTotal(o), 10);
1982 if(s.successProperty){
1983 var vs = this.getSuccess(o);
1984 if(vs === false || vs === 'false'){
1989 for(var i = 0; i < c; i++){
1992 var id = this.getId(n);
1993 for(var j = 0; j < fl; j++){
1995 var v = this.ef[j](n);
1997 Roo.log('missing convert for ' + f.name);
2001 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
2005 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2011 var record = new Record(values, id);
2013 records[i] = record;
2019 totalRecords : totalRecords
2022 // used when loading children.. @see loadDataFromChildren
2023 toLoadData: function(rec)
2025 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2026 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2027 return { data : data, total : data.length };
2032 * Ext JS Library 1.1.1
2033 * Copyright(c) 2006-2007, Ext JS, LLC.
2035 * Originally Released Under LGPL - original licence link has changed is not relivant.
2038 * <script type="text/javascript">
2042 * @class Roo.data.XmlReader
2043 * @extends Roo.data.DataReader
2044 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2045 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2047 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2048 * header in the HTTP response must be set to "text/xml".</em>
2052 var RecordDef = Roo.data.Record.create([
2053 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2054 {name: 'occupation'} // This field will use "occupation" as the mapping.
2056 var myReader = new Roo.data.XmlReader({
2057 totalRecords: "results", // The element which contains the total dataset size (optional)
2058 record: "row", // The repeated element which contains row information
2059 id: "id" // The element within the row that provides an ID for the record (optional)
2063 * This would consume an XML file like this:
2067 <results>2</results>
2070 <name>Bill</name>
2071 <occupation>Gardener</occupation>
2075 <name>Ben</name>
2076 <occupation>Horticulturalist</occupation>
2080 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2081 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2082 * paged from the remote server.
2083 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2084 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2085 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2086 * a record identifier value.
2088 * Create a new XmlReader
2089 * @param {Object} meta Metadata configuration options
2090 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2091 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2092 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2094 Roo.data.XmlReader = function(meta, recordType){
2096 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2098 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2103 * This method is only used by a DataProxy which has retrieved data from a remote server.
2104 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2105 * to contain a method called 'responseXML' that returns an XML document object.
2106 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2107 * a cache of Roo.data.Records.
2109 read : function(response){
2110 var doc = response.responseXML;
2112 throw {message: "XmlReader.read: XML Document not available"};
2114 return this.readRecords(doc);
2118 * Create a data block containing Roo.data.Records from an XML document.
2119 * @param {Object} doc A parsed XML document.
2120 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2121 * a cache of Roo.data.Records.
2123 readRecords : function(doc){
2125 * After any data loads/reads, the raw XML Document is available for further custom processing.
2129 var root = doc.documentElement || doc;
2130 var q = Roo.DomQuery;
2131 var recordType = this.recordType, fields = recordType.prototype.fields;
2132 var sid = this.meta.id;
2133 var totalRecords = 0, success = true;
2134 if(this.meta.totalRecords){
2135 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2138 if(this.meta.success){
2139 var sv = q.selectValue(this.meta.success, root, true);
2140 success = sv !== false && sv !== 'false';
2143 var ns = q.select(this.meta.record, root);
2144 for(var i = 0, len = ns.length; i < len; i++) {
2147 var id = sid ? q.selectValue(sid, n) : undefined;
2148 for(var j = 0, jlen = fields.length; j < jlen; j++){
2149 var f = fields.items[j];
2150 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2154 var record = new recordType(values, id);
2156 records[records.length] = record;
2162 totalRecords : totalRecords || records.length
2167 * Ext JS Library 1.1.1
2168 * Copyright(c) 2006-2007, Ext JS, LLC.
2170 * Originally Released Under LGPL - original licence link has changed is not relivant.
2173 * <script type="text/javascript">
2177 * @class Roo.data.ArrayReader
2178 * @extends Roo.data.DataReader
2179 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2180 * Each element of that Array represents a row of data fields. The
2181 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2182 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2186 var RecordDef = Roo.data.Record.create([
2187 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2188 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2190 var myReader = new Roo.data.ArrayReader({
2191 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2195 * This would consume an Array like this:
2197 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2201 * Create a new JsonReader
2202 * @param {Object} meta Metadata configuration options.
2203 * @param {Object|Array} recordType Either an Array of field definition objects
2205 * @cfg {Array} fields Array of field definition objects
2206 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2207 * as specified to {@link Roo.data.Record#create},
2208 * or an {@link Roo.data.Record} object
2211 * created using {@link Roo.data.Record#create}.
2213 Roo.data.ArrayReader = function(meta, recordType)
2215 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2218 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2221 * Create a data block containing Roo.data.Records from an XML document.
2222 * @param {Object} o An Array of row objects which represents the dataset.
2223 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2224 * a cache of Roo.data.Records.
2226 readRecords : function(o)
2228 var sid = this.meta ? this.meta.id : null;
2229 var recordType = this.recordType, fields = recordType.prototype.fields;
2232 for(var i = 0; i < root.length; i++){
2235 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2236 for(var j = 0, jlen = fields.length; j < jlen; j++){
2237 var f = fields.items[j];
2238 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2239 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2243 var record = new recordType(values, id);
2245 records[records.length] = record;
2249 totalRecords : records.length
2252 // used when loading children.. @see loadDataFromChildren
2253 toLoadData: function(rec)
2255 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2256 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2263 * Ext JS Library 1.1.1
2264 * Copyright(c) 2006-2007, Ext JS, LLC.
2266 * Originally Released Under LGPL - original licence link has changed is not relivant.
2269 * <script type="text/javascript">
2274 * @class Roo.data.Tree
2275 * @extends Roo.util.Observable
2276 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2277 * in the tree have most standard DOM functionality.
2279 * @param {Node} root (optional) The root node
2281 Roo.data.Tree = function(root){
2284 * The root node for this tree
2289 this.setRootNode(root);
2294 * Fires when a new child node is appended to a node in this tree.
2295 * @param {Tree} tree The owner tree
2296 * @param {Node} parent The parent node
2297 * @param {Node} node The newly appended node
2298 * @param {Number} index The index of the newly appended node
2303 * Fires when a child node is removed from a node in this tree.
2304 * @param {Tree} tree The owner tree
2305 * @param {Node} parent The parent node
2306 * @param {Node} node The child node removed
2311 * Fires when a node is moved to a new location in the tree
2312 * @param {Tree} tree The owner tree
2313 * @param {Node} node The node moved
2314 * @param {Node} oldParent The old parent of this node
2315 * @param {Node} newParent The new parent of this node
2316 * @param {Number} index The index it was moved to
2321 * Fires when a new child node is inserted in a node in this tree.
2322 * @param {Tree} tree The owner tree
2323 * @param {Node} parent The parent node
2324 * @param {Node} node The child node inserted
2325 * @param {Node} refNode The child node the node was inserted before
2329 * @event beforeappend
2330 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2331 * @param {Tree} tree The owner tree
2332 * @param {Node} parent The parent node
2333 * @param {Node} node The child node to be appended
2335 "beforeappend" : true,
2337 * @event beforeremove
2338 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2339 * @param {Tree} tree The owner tree
2340 * @param {Node} parent The parent node
2341 * @param {Node} node The child node to be removed
2343 "beforeremove" : true,
2346 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2347 * @param {Tree} tree The owner tree
2348 * @param {Node} node The node being moved
2349 * @param {Node} oldParent The parent of the node
2350 * @param {Node} newParent The new parent the node is moving to
2351 * @param {Number} index The index it is being moved to
2353 "beforemove" : true,
2355 * @event beforeinsert
2356 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2357 * @param {Tree} tree The owner tree
2358 * @param {Node} parent The parent node
2359 * @param {Node} node The child node to be inserted
2360 * @param {Node} refNode The child node the node is being inserted before
2362 "beforeinsert" : true
2365 Roo.data.Tree.superclass.constructor.call(this);
2368 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2371 proxyNodeEvent : function(){
2372 return this.fireEvent.apply(this, arguments);
2376 * Returns the root node for this tree.
2379 getRootNode : function(){
2384 * Sets the root node for this tree.
2385 * @param {Node} node
2388 setRootNode : function(node){
2390 node.ownerTree = this;
2392 this.registerNode(node);
2397 * Gets a node in this tree by its id.
2398 * @param {String} id
2401 getNodeById : function(id){
2402 return this.nodeHash[id];
2405 registerNode : function(node){
2406 this.nodeHash[node.id] = node;
2409 unregisterNode : function(node){
2410 delete this.nodeHash[node.id];
2413 toString : function(){
2414 return "[Tree"+(this.id?" "+this.id:"")+"]";
2419 * @class Roo.data.Node
2420 * @extends Roo.util.Observable
2421 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2422 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2424 * @param {Object} attributes The attributes/config for the node
2426 Roo.data.Node = function(attributes){
2428 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2431 this.attributes = attributes || {};
2432 this.leaf = this.attributes.leaf;
2434 * The node id. @type String
2436 this.id = this.attributes.id;
2438 this.id = Roo.id(null, "ynode-");
2439 this.attributes.id = this.id;
2444 * All child nodes of this node. @type Array
2446 this.childNodes = [];
2447 if(!this.childNodes.indexOf){ // indexOf is a must
2448 this.childNodes.indexOf = function(o){
2449 for(var i = 0, len = this.length; i < len; i++){
2458 * The parent node for this node. @type Node
2460 this.parentNode = null;
2462 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2464 this.firstChild = null;
2466 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2468 this.lastChild = null;
2470 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2472 this.previousSibling = null;
2474 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2476 this.nextSibling = null;
2481 * Fires when a new child node is appended
2482 * @param {Tree} tree The owner tree
2483 * @param {Node} this This node
2484 * @param {Node} node The newly appended node
2485 * @param {Number} index The index of the newly appended node
2490 * Fires when a child node is removed
2491 * @param {Tree} tree The owner tree
2492 * @param {Node} this This node
2493 * @param {Node} node The removed node
2498 * Fires when this node is moved to a new location in the tree
2499 * @param {Tree} tree The owner tree
2500 * @param {Node} this This node
2501 * @param {Node} oldParent The old parent of this node
2502 * @param {Node} newParent The new parent of this node
2503 * @param {Number} index The index it was moved to
2508 * Fires when a new child node is inserted.
2509 * @param {Tree} tree The owner tree
2510 * @param {Node} this This node
2511 * @param {Node} node The child node inserted
2512 * @param {Node} refNode The child node the node was inserted before
2516 * @event beforeappend
2517 * Fires before a new child is appended, return false to cancel the append.
2518 * @param {Tree} tree The owner tree
2519 * @param {Node} this This node
2520 * @param {Node} node The child node to be appended
2522 "beforeappend" : true,
2524 * @event beforeremove
2525 * Fires before a child is removed, return false to cancel the remove.
2526 * @param {Tree} tree The owner tree
2527 * @param {Node} this This node
2528 * @param {Node} node The child node to be removed
2530 "beforeremove" : true,
2533 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2534 * @param {Tree} tree The owner tree
2535 * @param {Node} this This node
2536 * @param {Node} oldParent The parent of this node
2537 * @param {Node} newParent The new parent this node is moving to
2538 * @param {Number} index The index it is being moved to
2540 "beforemove" : true,
2542 * @event beforeinsert
2543 * Fires before a new child is inserted, return false to cancel the insert.
2544 * @param {Tree} tree The owner tree
2545 * @param {Node} this This node
2546 * @param {Node} node The child node to be inserted
2547 * @param {Node} refNode The child node the node is being inserted before
2549 "beforeinsert" : true
2551 this.listeners = this.attributes.listeners;
2552 Roo.data.Node.superclass.constructor.call(this);
2555 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2556 fireEvent : function(evtName){
2557 // first do standard event for this node
2558 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2561 // then bubble it up to the tree if the event wasn't cancelled
2562 var ot = this.getOwnerTree();
2564 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2572 * Returns true if this node is a leaf
2575 isLeaf : function(){
2576 return this.leaf === true;
2580 setFirstChild : function(node){
2581 this.firstChild = node;
2585 setLastChild : function(node){
2586 this.lastChild = node;
2591 * Returns true if this node is the last child of its parent
2594 isLast : function(){
2595 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2599 * Returns true if this node is the first child of its parent
2602 isFirst : function(){
2603 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2606 hasChildNodes : function(){
2607 return !this.isLeaf() && this.childNodes.length > 0;
2611 * Insert node(s) as the last child node of this node.
2612 * @param {Node/Array} node The node or Array of nodes to append
2613 * @return {Node} The appended node if single append, or null if an array was passed
2615 appendChild : function(node){
2617 if(node instanceof Array){
2619 }else if(arguments.length > 1){
2623 // if passed an array or multiple args do them one by one
2625 for(var i = 0, len = multi.length; i < len; i++) {
2626 this.appendChild(multi[i]);
2629 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2632 var index = this.childNodes.length;
2633 var oldParent = node.parentNode;
2634 // it's a move, make sure we move it cleanly
2636 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2639 oldParent.removeChild(node);
2642 index = this.childNodes.length;
2644 this.setFirstChild(node);
2646 this.childNodes.push(node);
2647 node.parentNode = this;
2648 var ps = this.childNodes[index-1];
2650 node.previousSibling = ps;
2651 ps.nextSibling = node;
2653 node.previousSibling = null;
2655 node.nextSibling = null;
2656 this.setLastChild(node);
2657 node.setOwnerTree(this.getOwnerTree());
2658 this.fireEvent("append", this.ownerTree, this, node, index);
2659 if(this.ownerTree) {
2660 this.ownerTree.fireEvent("appendnode", this, node, index);
2663 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2670 * Removes a child node from this node.
2671 * @param {Node} node The node to remove
2672 * @return {Node} The removed node
2674 removeChild : function(node){
2675 var index = this.childNodes.indexOf(node);
2679 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2683 // remove it from childNodes collection
2684 this.childNodes.splice(index, 1);
2687 if(node.previousSibling){
2688 node.previousSibling.nextSibling = node.nextSibling;
2690 if(node.nextSibling){
2691 node.nextSibling.previousSibling = node.previousSibling;
2694 // update child refs
2695 if(this.firstChild == node){
2696 this.setFirstChild(node.nextSibling);
2698 if(this.lastChild == node){
2699 this.setLastChild(node.previousSibling);
2702 node.setOwnerTree(null);
2703 // clear any references from the node
2704 node.parentNode = null;
2705 node.previousSibling = null;
2706 node.nextSibling = null;
2707 this.fireEvent("remove", this.ownerTree, this, node);
2712 * Inserts the first node before the second node in this nodes childNodes collection.
2713 * @param {Node} node The node to insert
2714 * @param {Node} refNode The node to insert before (if null the node is appended)
2715 * @return {Node} The inserted node
2717 insertBefore : function(node, refNode){
2718 if(!refNode){ // like standard Dom, refNode can be null for append
2719 return this.appendChild(node);
2722 if(node == refNode){
2726 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2729 var index = this.childNodes.indexOf(refNode);
2730 var oldParent = node.parentNode;
2731 var refIndex = index;
2733 // when moving internally, indexes will change after remove
2734 if(oldParent == this && this.childNodes.indexOf(node) < index){
2738 // it's a move, make sure we move it cleanly
2740 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2743 oldParent.removeChild(node);
2746 this.setFirstChild(node);
2748 this.childNodes.splice(refIndex, 0, node);
2749 node.parentNode = this;
2750 var ps = this.childNodes[refIndex-1];
2752 node.previousSibling = ps;
2753 ps.nextSibling = node;
2755 node.previousSibling = null;
2757 node.nextSibling = refNode;
2758 refNode.previousSibling = node;
2759 node.setOwnerTree(this.getOwnerTree());
2760 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2762 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2768 * Returns the child node at the specified index.
2769 * @param {Number} index
2772 item : function(index){
2773 return this.childNodes[index];
2777 * Replaces one child node in this node with another.
2778 * @param {Node} newChild The replacement node
2779 * @param {Node} oldChild The node to replace
2780 * @return {Node} The replaced node
2782 replaceChild : function(newChild, oldChild){
2783 this.insertBefore(newChild, oldChild);
2784 this.removeChild(oldChild);
2789 * Returns the index of a child node
2790 * @param {Node} node
2791 * @return {Number} The index of the node or -1 if it was not found
2793 indexOf : function(child){
2794 return this.childNodes.indexOf(child);
2798 * Returns the tree this node is in.
2801 getOwnerTree : function(){
2802 // if it doesn't have one, look for one
2803 if(!this.ownerTree){
2807 this.ownerTree = p.ownerTree;
2813 return this.ownerTree;
2817 * Returns depth of this node (the root node has a depth of 0)
2820 getDepth : function(){
2823 while(p.parentNode){
2831 setOwnerTree : function(tree){
2832 // if it's move, we need to update everyone
2833 if(tree != this.ownerTree){
2835 this.ownerTree.unregisterNode(this);
2837 this.ownerTree = tree;
2838 var cs = this.childNodes;
2839 for(var i = 0, len = cs.length; i < len; i++) {
2840 cs[i].setOwnerTree(tree);
2843 tree.registerNode(this);
2849 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2850 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2851 * @return {String} The path
2853 getPath : function(attr){
2854 attr = attr || "id";
2855 var p = this.parentNode;
2856 var b = [this.attributes[attr]];
2858 b.unshift(p.attributes[attr]);
2861 var sep = this.getOwnerTree().pathSeparator;
2862 return sep + b.join(sep);
2866 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2867 * function call will be the scope provided or the current node. The arguments to the function
2868 * will be the args provided or the current node. If the function returns false at any point,
2869 * the bubble is stopped.
2870 * @param {Function} fn The function to call
2871 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2872 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2874 bubble : function(fn, scope, args){
2877 if(fn.call(scope || p, args || p) === false){
2885 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2886 * function call will be the scope provided or the current node. The arguments to the function
2887 * will be the args provided or the current node. If the function returns false at any point,
2888 * the cascade is stopped on that branch.
2889 * @param {Function} fn The function to call
2890 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2891 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2893 cascade : function(fn, scope, args){
2894 if(fn.call(scope || this, args || this) !== false){
2895 var cs = this.childNodes;
2896 for(var i = 0, len = cs.length; i < len; i++) {
2897 cs[i].cascade(fn, scope, args);
2903 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2904 * function call will be the scope provided or the current node. The arguments to the function
2905 * will be the args provided or the current node. If the function returns false at any point,
2906 * the iteration stops.
2907 * @param {Function} fn The function to call
2908 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2909 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2911 eachChild : function(fn, scope, args){
2912 var cs = this.childNodes;
2913 for(var i = 0, len = cs.length; i < len; i++) {
2914 if(fn.call(scope || this, args || cs[i]) === false){
2921 * Finds the first child that has the attribute with the specified value.
2922 * @param {String} attribute The attribute name
2923 * @param {Mixed} value The value to search for
2924 * @return {Node} The found child or null if none was found
2926 findChild : function(attribute, value){
2927 var cs = this.childNodes;
2928 for(var i = 0, len = cs.length; i < len; i++) {
2929 if(cs[i].attributes[attribute] == value){
2937 * Finds the first child by a custom function. The child matches if the function passed
2939 * @param {Function} fn
2940 * @param {Object} scope (optional)
2941 * @return {Node} The found child or null if none was found
2943 findChildBy : function(fn, scope){
2944 var cs = this.childNodes;
2945 for(var i = 0, len = cs.length; i < len; i++) {
2946 if(fn.call(scope||cs[i], cs[i]) === true){
2954 * Sorts this nodes children using the supplied sort function
2955 * @param {Function} fn
2956 * @param {Object} scope (optional)
2958 sort : function(fn, scope){
2959 var cs = this.childNodes;
2960 var len = cs.length;
2962 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2964 for(var i = 0; i < len; i++){
2966 n.previousSibling = cs[i-1];
2967 n.nextSibling = cs[i+1];
2969 this.setFirstChild(n);
2972 this.setLastChild(n);
2979 * Returns true if this node is an ancestor (at any point) of the passed node.
2980 * @param {Node} node
2983 contains : function(node){
2984 return node.isAncestor(this);
2988 * Returns true if the passed node is an ancestor (at any point) of this node.
2989 * @param {Node} node
2992 isAncestor : function(node){
2993 var p = this.parentNode;
3003 toString : function(){
3004 return "[Node"+(this.id?" "+this.id:"")+"]";
3008 * Ext JS Library 1.1.1
3009 * Copyright(c) 2006-2007, Ext JS, LLC.
3011 * Originally Released Under LGPL - original licence link has changed is not relivant.
3014 * <script type="text/javascript">
3020 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3021 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3022 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3024 * Create a new Shadow
3025 * @param {Object} config The config object
3027 Roo.Shadow = function(config){
3028 Roo.apply(this, config);
3029 if(typeof this.mode != "string"){
3030 this.mode = this.defaultMode;
3032 var o = this.offset, a = {h: 0};
3033 var rad = Math.floor(this.offset/2);
3034 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3040 a.l -= this.offset + rad;
3041 a.t -= this.offset + rad;
3052 a.l -= (this.offset - rad);
3053 a.t -= this.offset + rad;
3055 a.w -= (this.offset - rad)*2;
3066 a.l -= (this.offset - rad);
3067 a.t -= (this.offset - rad);
3069 a.w -= (this.offset + rad + 1);
3070 a.h -= (this.offset + rad);
3079 Roo.Shadow.prototype = {
3081 * @cfg {String} mode
3082 * The shadow display mode. Supports the following options:<br />
3083 * sides: Shadow displays on both sides and bottom only<br />
3084 * frame: Shadow displays equally on all four sides<br />
3085 * drop: Traditional bottom-right drop shadow (default)
3089 * @cfg {String} offset
3090 * The number of pixels to offset the shadow from the element (defaults to 4)
3095 defaultMode: "drop",
3098 * Displays the shadow under the target element
3099 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3101 show : function(target){
3102 target = Roo.get(target);
3104 this.el = Roo.Shadow.Pool.pull();
3105 if(this.el.dom.nextSibling != target.dom){
3106 this.el.insertBefore(target);
3109 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3111 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3114 target.getLeft(true),
3115 target.getTop(true),
3119 this.el.dom.style.display = "block";
3123 * Returns true if the shadow is visible, else false
3125 isVisible : function(){
3126 return this.el ? true : false;
3130 * Direct alignment when values are already available. Show must be called at least once before
3131 * calling this method to ensure it is initialized.
3132 * @param {Number} left The target element left position
3133 * @param {Number} top The target element top position
3134 * @param {Number} width The target element width
3135 * @param {Number} height The target element height
3137 realign : function(l, t, w, h){
3141 var a = this.adjusts, d = this.el.dom, s = d.style;
3143 s.left = (l+a.l)+"px";
3144 s.top = (t+a.t)+"px";
3145 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3147 if(s.width != sws || s.height != shs){
3151 var cn = d.childNodes;
3152 var sww = Math.max(0, (sw-12))+"px";
3153 cn[0].childNodes[1].style.width = sww;
3154 cn[1].childNodes[1].style.width = sww;
3155 cn[2].childNodes[1].style.width = sww;
3156 cn[1].style.height = Math.max(0, (sh-12))+"px";
3166 this.el.dom.style.display = "none";
3167 Roo.Shadow.Pool.push(this.el);
3173 * Adjust the z-index of this shadow
3174 * @param {Number} zindex The new z-index
3176 setZIndex : function(z){
3179 this.el.setStyle("z-index", z);
3184 // Private utility class that manages the internal Shadow cache
3185 Roo.Shadow.Pool = function(){
3187 var markup = Roo.isIE ?
3188 '<div class="x-ie-shadow"></div>' :
3189 '<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>';
3194 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3195 sh.autoBoxAdjust = false;
3200 push : function(sh){
3206 * Ext JS Library 1.1.1
3207 * Copyright(c) 2006-2007, Ext JS, LLC.
3209 * Originally Released Under LGPL - original licence link has changed is not relivant.
3212 * <script type="text/javascript">
3217 * @class Roo.SplitBar
3218 * @extends Roo.util.Observable
3219 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3223 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3224 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3225 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3226 split.minSize = 100;
3227 split.maxSize = 600;
3228 split.animate = true;
3229 split.on('moved', splitterMoved);
3232 * Create a new SplitBar
3233 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3234 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3235 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3236 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3237 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3238 position of the SplitBar).
3240 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3243 this.el = Roo.get(dragElement, true);
3244 this.el.dom.unselectable = "on";
3246 this.resizingEl = Roo.get(resizingElement, true);
3250 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3251 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3254 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3257 * The minimum size of the resizing element. (Defaults to 0)
3263 * The maximum size of the resizing element. (Defaults to 2000)
3266 this.maxSize = 2000;
3269 * Whether to animate the transition to the new size
3272 this.animate = false;
3275 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3278 this.useShim = false;
3285 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3287 this.proxy = Roo.get(existingProxy).dom;
3290 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3293 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3296 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3299 this.dragSpecs = {};
3302 * @private The adapter to use to positon and resize elements
3304 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3305 this.adapter.init(this);
3307 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3309 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3310 this.el.addClass("x-splitbar-h");
3313 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3314 this.el.addClass("x-splitbar-v");
3320 * Fires when the splitter is moved (alias for {@link #event-moved})
3321 * @param {Roo.SplitBar} this
3322 * @param {Number} newSize the new width or height
3327 * Fires when the splitter is moved
3328 * @param {Roo.SplitBar} this
3329 * @param {Number} newSize the new width or height
3333 * @event beforeresize
3334 * Fires before the splitter is dragged
3335 * @param {Roo.SplitBar} this
3337 "beforeresize" : true,
3339 "beforeapply" : true
3342 Roo.util.Observable.call(this);
3345 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3346 onStartProxyDrag : function(x, y){
3347 this.fireEvent("beforeresize", this);
3349 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3351 o.enableDisplayMode("block");
3352 // all splitbars share the same overlay
3353 Roo.SplitBar.prototype.overlay = o;
3355 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3356 this.overlay.show();
3357 Roo.get(this.proxy).setDisplayed("block");
3358 var size = this.adapter.getElementSize(this);
3359 this.activeMinSize = this.getMinimumSize();;
3360 this.activeMaxSize = this.getMaximumSize();;
3361 var c1 = size - this.activeMinSize;
3362 var c2 = Math.max(this.activeMaxSize - size, 0);
3363 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3364 this.dd.resetConstraints();
3365 this.dd.setXConstraint(
3366 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3367 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3369 this.dd.setYConstraint(0, 0);
3371 this.dd.resetConstraints();
3372 this.dd.setXConstraint(0, 0);
3373 this.dd.setYConstraint(
3374 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3375 this.placement == Roo.SplitBar.TOP ? c2 : c1
3378 this.dragSpecs.startSize = size;
3379 this.dragSpecs.startPoint = [x, y];
3380 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3384 * @private Called after the drag operation by the DDProxy
3386 onEndProxyDrag : function(e){
3387 Roo.get(this.proxy).setDisplayed(false);
3388 var endPoint = Roo.lib.Event.getXY(e);
3390 this.overlay.hide();
3393 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3394 newSize = this.dragSpecs.startSize +
3395 (this.placement == Roo.SplitBar.LEFT ?
3396 endPoint[0] - this.dragSpecs.startPoint[0] :
3397 this.dragSpecs.startPoint[0] - endPoint[0]
3400 newSize = this.dragSpecs.startSize +
3401 (this.placement == Roo.SplitBar.TOP ?
3402 endPoint[1] - this.dragSpecs.startPoint[1] :
3403 this.dragSpecs.startPoint[1] - endPoint[1]
3406 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3407 if(newSize != this.dragSpecs.startSize){
3408 if(this.fireEvent('beforeapply', this, newSize) !== false){
3409 this.adapter.setElementSize(this, newSize);
3410 this.fireEvent("moved", this, newSize);
3411 this.fireEvent("resize", this, newSize);
3417 * Get the adapter this SplitBar uses
3418 * @return The adapter object
3420 getAdapter : function(){
3421 return this.adapter;
3425 * Set the adapter this SplitBar uses
3426 * @param {Object} adapter A SplitBar adapter object
3428 setAdapter : function(adapter){
3429 this.adapter = adapter;
3430 this.adapter.init(this);
3434 * Gets the minimum size for the resizing element
3435 * @return {Number} The minimum size
3437 getMinimumSize : function(){
3438 return this.minSize;
3442 * Sets the minimum size for the resizing element
3443 * @param {Number} minSize The minimum size
3445 setMinimumSize : function(minSize){
3446 this.minSize = minSize;
3450 * Gets the maximum size for the resizing element
3451 * @return {Number} The maximum size
3453 getMaximumSize : function(){
3454 return this.maxSize;
3458 * Sets the maximum size for the resizing element
3459 * @param {Number} maxSize The maximum size
3461 setMaximumSize : function(maxSize){
3462 this.maxSize = maxSize;
3466 * Sets the initialize size for the resizing element
3467 * @param {Number} size The initial size
3469 setCurrentSize : function(size){
3470 var oldAnimate = this.animate;
3471 this.animate = false;
3472 this.adapter.setElementSize(this, size);
3473 this.animate = oldAnimate;
3477 * Destroy this splitbar.
3478 * @param {Boolean} removeEl True to remove the element
3480 destroy : function(removeEl){
3485 this.proxy.parentNode.removeChild(this.proxy);
3493 * @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.
3495 Roo.SplitBar.createProxy = function(dir){
3496 var proxy = new Roo.Element(document.createElement("div"));
3497 proxy.unselectable();
3498 var cls = 'x-splitbar-proxy';
3499 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3500 document.body.appendChild(proxy.dom);
3505 * @class Roo.SplitBar.BasicLayoutAdapter
3506 * Default Adapter. It assumes the splitter and resizing element are not positioned
3507 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3509 Roo.SplitBar.BasicLayoutAdapter = function(){
3512 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3513 // do nothing for now
3518 * Called before drag operations to get the current size of the resizing element.
3519 * @param {Roo.SplitBar} s The SplitBar using this adapter
3521 getElementSize : function(s){
3522 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3523 return s.resizingEl.getWidth();
3525 return s.resizingEl.getHeight();
3530 * Called after drag operations to set the size of the resizing element.
3531 * @param {Roo.SplitBar} s The SplitBar using this adapter
3532 * @param {Number} newSize The new size to set
3533 * @param {Function} onComplete A function to be invoked when resizing is complete
3535 setElementSize : function(s, newSize, onComplete){
3536 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3538 s.resizingEl.setWidth(newSize);
3540 onComplete(s, newSize);
3543 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3548 s.resizingEl.setHeight(newSize);
3550 onComplete(s, newSize);
3553 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3560 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3561 * @extends Roo.SplitBar.BasicLayoutAdapter
3562 * Adapter that moves the splitter element to align with the resized sizing element.
3563 * Used with an absolute positioned SplitBar.
3564 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3565 * document.body, make sure you assign an id to the body element.
3567 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3568 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3569 this.container = Roo.get(container);
3572 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3577 getElementSize : function(s){
3578 return this.basic.getElementSize(s);
3581 setElementSize : function(s, newSize, onComplete){
3582 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3585 moveSplitter : function(s){
3586 var yes = Roo.SplitBar;
3587 switch(s.placement){
3589 s.el.setX(s.resizingEl.getRight());
3592 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3595 s.el.setY(s.resizingEl.getBottom());
3598 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3605 * Orientation constant - Create a vertical SplitBar
3609 Roo.SplitBar.VERTICAL = 1;
3612 * Orientation constant - Create a horizontal SplitBar
3616 Roo.SplitBar.HORIZONTAL = 2;
3619 * Placement constant - The resizing element is to the left of the splitter element
3623 Roo.SplitBar.LEFT = 1;
3626 * Placement constant - The resizing element is to the right of the splitter element
3630 Roo.SplitBar.RIGHT = 2;
3633 * Placement constant - The resizing element is positioned above the splitter element
3637 Roo.SplitBar.TOP = 3;
3640 * Placement constant - The resizing element is positioned under splitter element
3644 Roo.SplitBar.BOTTOM = 4;
3647 * Ext JS Library 1.1.1
3648 * Copyright(c) 2006-2007, Ext JS, LLC.
3650 * Originally Released Under LGPL - original licence link has changed is not relivant.
3653 * <script type="text/javascript">
3658 * @extends Roo.util.Observable
3659 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
3660 * This class also supports single and multi selection modes. <br>
3661 * Create a data model bound view:
3663 var store = new Roo.data.Store(...);
3665 var view = new Roo.View({
3667 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
3670 selectedClass: "ydataview-selected",
3674 // listen for node click?
3675 view.on("click", function(vw, index, node, e){
3676 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3680 dataModel.load("foobar.xml");
3682 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3684 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3685 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3687 * Note: old style constructor is still suported (container, template, config)
3691 * @param {Object} config The config object
3694 Roo.View = function(config, depreciated_tpl, depreciated_config){
3696 this.parent = false;
3698 if (typeof(depreciated_tpl) == 'undefined') {
3699 // new way.. - universal constructor.
3700 Roo.apply(this, config);
3701 this.el = Roo.get(this.el);
3704 this.el = Roo.get(config);
3705 this.tpl = depreciated_tpl;
3706 Roo.apply(this, depreciated_config);
3708 this.wrapEl = this.el.wrap().wrap();
3709 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3712 if(typeof(this.tpl) == "string"){
3713 this.tpl = new Roo.Template(this.tpl);
3715 // support xtype ctors..
3716 this.tpl = new Roo.factory(this.tpl, Roo);
3725 * @event beforeclick
3726 * Fires before a click is processed. Returns false to cancel the default action.
3727 * @param {Roo.View} this
3728 * @param {Number} index The index of the target node
3729 * @param {HTMLElement} node The target node
3730 * @param {Roo.EventObject} e The raw event object
3732 "beforeclick" : true,
3735 * Fires when a template node is clicked.
3736 * @param {Roo.View} this
3737 * @param {Number} index The index of the target node
3738 * @param {HTMLElement} node The target node
3739 * @param {Roo.EventObject} e The raw event object
3744 * Fires when a template node is double clicked.
3745 * @param {Roo.View} this
3746 * @param {Number} index The index of the target node
3747 * @param {HTMLElement} node The target node
3748 * @param {Roo.EventObject} e The raw event object
3752 * @event contextmenu
3753 * Fires when a template node is right clicked.
3754 * @param {Roo.View} this
3755 * @param {Number} index The index of the target node
3756 * @param {HTMLElement} node The target node
3757 * @param {Roo.EventObject} e The raw event object
3759 "contextmenu" : true,
3761 * @event selectionchange
3762 * Fires when the selected nodes change.
3763 * @param {Roo.View} this
3764 * @param {Array} selections Array of the selected nodes
3766 "selectionchange" : true,
3769 * @event beforeselect
3770 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3771 * @param {Roo.View} this
3772 * @param {HTMLElement} node The node to be selected
3773 * @param {Array} selections Array of currently selected nodes
3775 "beforeselect" : true,
3777 * @event preparedata
3778 * Fires on every row to render, to allow you to change the data.
3779 * @param {Roo.View} this
3780 * @param {Object} data to be rendered (change this)
3782 "preparedata" : true
3790 "click": this.onClick,
3791 "dblclick": this.onDblClick,
3792 "contextmenu": this.onContextMenu,
3796 this.selections = [];
3798 this.cmp = new Roo.CompositeElementLite([]);
3800 this.store = Roo.factory(this.store, Roo.data);
3801 this.setStore(this.store, true);
3804 if ( this.footer && this.footer.xtype) {
3806 var fctr = this.wrapEl.appendChild(document.createElement("div"));
3808 this.footer.dataSource = this.store;
3809 this.footer.container = fctr;
3810 this.footer = Roo.factory(this.footer, Roo);
3811 fctr.insertFirst(this.el);
3813 // this is a bit insane - as the paging toolbar seems to detach the el..
3814 // dom.parentNode.parentNode.parentNode
3815 // they get detached?
3819 Roo.View.superclass.constructor.call(this);
3824 Roo.extend(Roo.View, Roo.util.Observable, {
3827 * @cfg {Roo.data.Store} store Data store to load data from.
3832 * @cfg {String|Roo.Element} el The container element.
3837 * @cfg {String|Roo.Template} tpl The template used by this View
3841 * @cfg {String} dataName the named area of the template to use as the data area
3842 * Works with domtemplates roo-name="name"
3846 * @cfg {String} selectedClass The css class to add to selected nodes
3848 selectedClass : "x-view-selected",
3850 * @cfg {String} emptyText The empty text to show when nothing is loaded.
3855 * @cfg {String} text to display on mask (default Loading)
3859 * @cfg {Boolean} multiSelect Allow multiple selection
3861 multiSelect : false,
3863 * @cfg {Boolean} singleSelect Allow single selection
3865 singleSelect: false,
3868 * @cfg {Boolean} toggleSelect - selecting
3870 toggleSelect : false,
3873 * @cfg {Boolean} tickable - selecting
3878 * Returns the element this view is bound to.
3879 * @return {Roo.Element}
3888 * Refreshes the view. - called by datachanged on the store. - do not call directly.
3890 refresh : function(){
3891 //Roo.log('refresh');
3894 // if we are using something like 'domtemplate', then
3895 // the what gets used is:
3896 // t.applySubtemplate(NAME, data, wrapping data..)
3897 // the outer template then get' applied with
3898 // the store 'extra data'
3899 // and the body get's added to the
3900 // roo-name="data" node?
3901 // <span class='roo-tpl-{name}'></span> ?????
3905 this.clearSelections();
3908 var records = this.store.getRange();
3909 if(records.length < 1) {
3911 // is this valid?? = should it render a template??
3913 this.el.update(this.emptyText);
3917 if (this.dataName) {
3918 this.el.update(t.apply(this.store.meta)); //????
3919 el = this.el.child('.roo-tpl-' + this.dataName);
3922 for(var i = 0, len = records.length; i < len; i++){
3923 var data = this.prepareData(records[i].data, i, records[i]);
3924 this.fireEvent("preparedata", this, data, i, records[i]);
3926 var d = Roo.apply({}, data);
3929 Roo.apply(d, {'roo-id' : Roo.id()});
3933 Roo.each(this.parent.item, function(item){
3934 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3937 Roo.apply(d, {'roo-data-checked' : 'checked'});
3941 html[html.length] = Roo.util.Format.trim(
3943 t.applySubtemplate(this.dataName, d, this.store.meta) :
3950 el.update(html.join(""));
3951 this.nodes = el.dom.childNodes;
3952 this.updateIndexes(0);
3957 * Function to override to reformat the data that is sent to
3958 * the template for each node.
3959 * DEPRICATED - use the preparedata event handler.
3960 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3961 * a JSON object for an UpdateManager bound view).
3963 prepareData : function(data, index, record)
3965 this.fireEvent("preparedata", this, data, index, record);
3969 onUpdate : function(ds, record){
3970 // Roo.log('on update');
3971 this.clearSelections();
3972 var index = this.store.indexOf(record);
3973 var n = this.nodes[index];
3974 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3975 n.parentNode.removeChild(n);
3976 this.updateIndexes(index, index);
3982 onAdd : function(ds, records, index)
3984 //Roo.log(['on Add', ds, records, index] );
3985 this.clearSelections();
3986 if(this.nodes.length == 0){
3990 var n = this.nodes[index];
3991 for(var i = 0, len = records.length; i < len; i++){
3992 var d = this.prepareData(records[i].data, i, records[i]);
3994 this.tpl.insertBefore(n, d);
3997 this.tpl.append(this.el, d);
4000 this.updateIndexes(index);
4003 onRemove : function(ds, record, index){
4004 // Roo.log('onRemove');
4005 this.clearSelections();
4006 var el = this.dataName ?
4007 this.el.child('.roo-tpl-' + this.dataName) :
4010 el.dom.removeChild(this.nodes[index]);
4011 this.updateIndexes(index);
4015 * Refresh an individual node.
4016 * @param {Number} index
4018 refreshNode : function(index){
4019 this.onUpdate(this.store, this.store.getAt(index));
4022 updateIndexes : function(startIndex, endIndex){
4023 var ns = this.nodes;
4024 startIndex = startIndex || 0;
4025 endIndex = endIndex || ns.length - 1;
4026 for(var i = startIndex; i <= endIndex; i++){
4027 ns[i].nodeIndex = i;
4032 * Changes the data store this view uses and refresh the view.
4033 * @param {Store} store
4035 setStore : function(store, initial){
4036 if(!initial && this.store){
4037 this.store.un("datachanged", this.refresh);
4038 this.store.un("add", this.onAdd);
4039 this.store.un("remove", this.onRemove);
4040 this.store.un("update", this.onUpdate);
4041 this.store.un("clear", this.refresh);
4042 this.store.un("beforeload", this.onBeforeLoad);
4043 this.store.un("load", this.onLoad);
4044 this.store.un("loadexception", this.onLoad);
4048 store.on("datachanged", this.refresh, this);
4049 store.on("add", this.onAdd, this);
4050 store.on("remove", this.onRemove, this);
4051 store.on("update", this.onUpdate, this);
4052 store.on("clear", this.refresh, this);
4053 store.on("beforeload", this.onBeforeLoad, this);
4054 store.on("load", this.onLoad, this);
4055 store.on("loadexception", this.onLoad, this);
4063 * onbeforeLoad - masks the loading area.
4066 onBeforeLoad : function(store,opts)
4068 //Roo.log('onBeforeLoad');
4072 this.el.mask(this.mask ? this.mask : "Loading" );
4074 onLoad : function ()
4081 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4082 * @param {HTMLElement} node
4083 * @return {HTMLElement} The template node
4085 findItemFromChild : function(node){
4086 var el = this.dataName ?
4087 this.el.child('.roo-tpl-' + this.dataName,true) :
4090 if(!node || node.parentNode == el){
4093 var p = node.parentNode;
4094 while(p && p != el){
4095 if(p.parentNode == el){
4104 onClick : function(e){
4105 var item = this.findItemFromChild(e.getTarget());
4107 var index = this.indexOf(item);
4108 if(this.onItemClick(item, index, e) !== false){
4109 this.fireEvent("click", this, index, item, e);
4112 this.clearSelections();
4117 onContextMenu : function(e){
4118 var item = this.findItemFromChild(e.getTarget());
4120 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4125 onDblClick : function(e){
4126 var item = this.findItemFromChild(e.getTarget());
4128 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4132 onItemClick : function(item, index, e)
4134 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4137 if (this.toggleSelect) {
4138 var m = this.isSelected(item) ? 'unselect' : 'select';
4141 _t[m](item, true, false);
4144 if(this.multiSelect || this.singleSelect){
4145 if(this.multiSelect && e.shiftKey && this.lastSelection){
4146 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4148 this.select(item, this.multiSelect && e.ctrlKey);
4149 this.lastSelection = item;
4161 * Get the number of selected nodes.
4164 getSelectionCount : function(){
4165 return this.selections.length;
4169 * Get the currently selected nodes.
4170 * @return {Array} An array of HTMLElements
4172 getSelectedNodes : function(){
4173 return this.selections;
4177 * Get the indexes of the selected nodes.
4180 getSelectedIndexes : function(){
4181 var indexes = [], s = this.selections;
4182 for(var i = 0, len = s.length; i < len; i++){
4183 indexes.push(s[i].nodeIndex);
4189 * Clear all selections
4190 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4192 clearSelections : function(suppressEvent){
4193 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4194 this.cmp.elements = this.selections;
4195 this.cmp.removeClass(this.selectedClass);
4196 this.selections = [];
4198 this.fireEvent("selectionchange", this, this.selections);
4204 * Returns true if the passed node is selected
4205 * @param {HTMLElement/Number} node The node or node index
4208 isSelected : function(node){
4209 var s = this.selections;
4213 node = this.getNode(node);
4214 return s.indexOf(node) !== -1;
4219 * @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
4220 * @param {Boolean} keepExisting (optional) true to keep existing selections
4221 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4223 select : function(nodeInfo, keepExisting, suppressEvent){
4224 if(nodeInfo instanceof Array){
4226 this.clearSelections(true);
4228 for(var i = 0, len = nodeInfo.length; i < len; i++){
4229 this.select(nodeInfo[i], true, true);
4233 var node = this.getNode(nodeInfo);
4234 if(!node || this.isSelected(node)){
4235 return; // already selected.
4238 this.clearSelections(true);
4241 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4242 Roo.fly(node).addClass(this.selectedClass);
4243 this.selections.push(node);
4245 this.fireEvent("selectionchange", this, this.selections);
4253 * @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
4254 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4255 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4257 unselect : function(nodeInfo, keepExisting, suppressEvent)
4259 if(nodeInfo instanceof Array){
4260 Roo.each(this.selections, function(s) {
4261 this.unselect(s, nodeInfo);
4265 var node = this.getNode(nodeInfo);
4266 if(!node || !this.isSelected(node)){
4267 //Roo.log("not selected");
4268 return; // not selected.
4272 Roo.each(this.selections, function(s) {
4274 Roo.fly(node).removeClass(this.selectedClass);
4281 this.selections= ns;
4282 this.fireEvent("selectionchange", this, this.selections);
4286 * Gets a template node.
4287 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4288 * @return {HTMLElement} The node or null if it wasn't found
4290 getNode : function(nodeInfo){
4291 if(typeof nodeInfo == "string"){
4292 return document.getElementById(nodeInfo);
4293 }else if(typeof nodeInfo == "number"){
4294 return this.nodes[nodeInfo];
4300 * Gets a range template nodes.
4301 * @param {Number} startIndex
4302 * @param {Number} endIndex
4303 * @return {Array} An array of nodes
4305 getNodes : function(start, end){
4306 var ns = this.nodes;
4308 end = typeof end == "undefined" ? ns.length - 1 : end;
4311 for(var i = start; i <= end; i++){
4315 for(var i = start; i >= end; i--){
4323 * Finds the index of the passed node
4324 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4325 * @return {Number} The index of the node or -1
4327 indexOf : function(node){
4328 node = this.getNode(node);
4329 if(typeof node.nodeIndex == "number"){
4330 return node.nodeIndex;
4332 var ns = this.nodes;
4333 for(var i = 0, len = ns.length; i < len; i++){
4343 * Ext JS Library 1.1.1
4344 * Copyright(c) 2006-2007, Ext JS, LLC.
4346 * Originally Released Under LGPL - original licence link has changed is not relivant.
4349 * <script type="text/javascript">
4353 * @class Roo.JsonView
4355 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4357 var view = new Roo.JsonView({
4358 container: "my-element",
4359 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4364 // listen for node click?
4365 view.on("click", function(vw, index, node, e){
4366 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4369 // direct load of JSON data
4370 view.load("foobar.php");
4372 // Example from my blog list
4373 var tpl = new Roo.Template(
4374 '<div class="entry">' +
4375 '<a class="entry-title" href="{link}">{title}</a>' +
4376 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4377 "</div><hr />"
4380 var moreView = new Roo.JsonView({
4381 container : "entry-list",
4385 moreView.on("beforerender", this.sortEntries, this);
4387 url: "/blog/get-posts.php",
4388 params: "allposts=true",
4389 text: "Loading Blog Entries..."
4393 * Note: old code is supported with arguments : (container, template, config)
4397 * Create a new JsonView
4399 * @param {Object} config The config object
4402 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4405 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4407 var um = this.el.getUpdateManager();
4408 um.setRenderer(this);
4409 um.on("update", this.onLoad, this);
4410 um.on("failure", this.onLoadException, this);
4413 * @event beforerender
4414 * Fires before rendering of the downloaded JSON data.
4415 * @param {Roo.JsonView} this
4416 * @param {Object} data The JSON data loaded
4420 * Fires when data is loaded.
4421 * @param {Roo.JsonView} this
4422 * @param {Object} data The JSON data loaded
4423 * @param {Object} response The raw Connect response object
4426 * @event loadexception
4427 * Fires when loading fails.
4428 * @param {Roo.JsonView} this
4429 * @param {Object} response The raw Connect response object
4432 'beforerender' : true,
4434 'loadexception' : true
4437 Roo.extend(Roo.JsonView, Roo.View, {
4439 * @type {String} The root property in the loaded JSON object that contains the data
4444 * Refreshes the view.
4446 refresh : function(){
4447 this.clearSelections();
4450 var o = this.jsonData;
4451 if(o && o.length > 0){
4452 for(var i = 0, len = o.length; i < len; i++){
4453 var data = this.prepareData(o[i], i, o);
4454 html[html.length] = this.tpl.apply(data);
4457 html.push(this.emptyText);
4459 this.el.update(html.join(""));
4460 this.nodes = this.el.dom.childNodes;
4461 this.updateIndexes(0);
4465 * 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.
4466 * @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:
4469 url: "your-url.php",
4470 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4471 callback: yourFunction,
4472 scope: yourObject, //(optional scope)
4480 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4481 * 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.
4482 * @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}
4483 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4484 * @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.
4487 var um = this.el.getUpdateManager();
4488 um.update.apply(um, arguments);
4491 // note - render is a standard framework call...
4492 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4493 render : function(el, response){
4495 this.clearSelections();
4499 if (response != '') {
4500 o = Roo.util.JSON.decode(response.responseText);
4503 o = o[this.jsonRoot];
4509 * The current JSON data or null
4512 this.beforeRender();
4517 * Get the number of records in the current JSON dataset
4520 getCount : function(){
4521 return this.jsonData ? this.jsonData.length : 0;
4525 * Returns the JSON object for the specified node(s)
4526 * @param {HTMLElement/Array} node The node or an array of nodes
4527 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4528 * you get the JSON object for the node
4530 getNodeData : function(node){
4531 if(node instanceof Array){
4533 for(var i = 0, len = node.length; i < len; i++){
4534 data.push(this.getNodeData(node[i]));
4538 return this.jsonData[this.indexOf(node)] || null;
4541 beforeRender : function(){
4542 this.snapshot = this.jsonData;
4544 this.sort.apply(this, this.sortInfo);
4546 this.fireEvent("beforerender", this, this.jsonData);
4549 onLoad : function(el, o){
4550 this.fireEvent("load", this, this.jsonData, o);
4553 onLoadException : function(el, o){
4554 this.fireEvent("loadexception", this, o);
4558 * Filter the data by a specific property.
4559 * @param {String} property A property on your JSON objects
4560 * @param {String/RegExp} value Either string that the property values
4561 * should start with, or a RegExp to test against the property
4563 filter : function(property, value){
4566 var ss = this.snapshot;
4567 if(typeof value == "string"){
4568 var vlen = value.length;
4573 value = value.toLowerCase();
4574 for(var i = 0, len = ss.length; i < len; i++){
4576 if(o[property].substr(0, vlen).toLowerCase() == value){
4580 } else if(value.exec){ // regex?
4581 for(var i = 0, len = ss.length; i < len; i++){
4583 if(value.test(o[property])){
4590 this.jsonData = data;
4596 * Filter by a function. The passed function will be called with each
4597 * object in the current dataset. If the function returns true the value is kept,
4598 * otherwise it is filtered.
4599 * @param {Function} fn
4600 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4602 filterBy : function(fn, scope){
4605 var ss = this.snapshot;
4606 for(var i = 0, len = ss.length; i < len; i++){
4608 if(fn.call(scope || this, o)){
4612 this.jsonData = data;
4618 * Clears the current filter.
4620 clearFilter : function(){
4621 if(this.snapshot && this.jsonData != this.snapshot){
4622 this.jsonData = this.snapshot;
4629 * Sorts the data for this view and refreshes it.
4630 * @param {String} property A property on your JSON objects to sort on
4631 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4632 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4634 sort : function(property, dir, sortType){
4635 this.sortInfo = Array.prototype.slice.call(arguments, 0);
4638 var dsc = dir && dir.toLowerCase() == "desc";
4639 var f = function(o1, o2){
4640 var v1 = sortType ? sortType(o1[p]) : o1[p];
4641 var v2 = sortType ? sortType(o2[p]) : o2[p];
4644 return dsc ? +1 : -1;
4646 return dsc ? -1 : +1;
4651 this.jsonData.sort(f);
4653 if(this.jsonData != this.snapshot){
4654 this.snapshot.sort(f);
4660 * Ext JS Library 1.1.1
4661 * Copyright(c) 2006-2007, Ext JS, LLC.
4663 * Originally Released Under LGPL - original licence link has changed is not relivant.
4666 * <script type="text/javascript">
4671 * @class Roo.ColorPalette
4672 * @extends Roo.Component
4673 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
4674 * Here's an example of typical usage:
4676 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
4677 cp.render('my-div');
4679 cp.on('select', function(palette, selColor){
4680 // do something with selColor
4684 * Create a new ColorPalette
4685 * @param {Object} config The config object
4687 Roo.ColorPalette = function(config){
4688 Roo.ColorPalette.superclass.constructor.call(this, config);
4692 * Fires when a color is selected
4693 * @param {ColorPalette} this
4694 * @param {String} color The 6-digit color hex code (without the # symbol)
4700 this.on("select", this.handler, this.scope, true);
4703 Roo.extend(Roo.ColorPalette, Roo.Component, {
4705 * @cfg {String} itemCls
4706 * The CSS class to apply to the containing element (defaults to "x-color-palette")
4708 itemCls : "x-color-palette",
4710 * @cfg {String} value
4711 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
4712 * the hex codes are case-sensitive.
4717 ctype: "Roo.ColorPalette",
4720 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4722 allowReselect : false,
4725 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
4726 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
4727 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4728 * of colors with the width setting until the box is symmetrical.</p>
4729 * <p>You can override individual colors if needed:</p>
4731 var cp = new Roo.ColorPalette();
4732 cp.colors[0] = "FF0000"; // change the first box to red
4735 Or you can provide a custom array of your own for complete control:
4737 var cp = new Roo.ColorPalette();
4738 cp.colors = ["000000", "993300", "333300"];
4743 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4744 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4745 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4746 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4747 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4751 onRender : function(container, position){
4752 var t = new Roo.MasterTemplate(
4753 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
4755 var c = this.colors;
4756 for(var i = 0, len = c.length; i < len; i++){
4759 var el = document.createElement("div");
4760 el.className = this.itemCls;
4762 container.dom.insertBefore(el, position);
4763 this.el = Roo.get(el);
4764 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
4765 if(this.clickEvent != 'click'){
4766 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
4771 afterRender : function(){
4772 Roo.ColorPalette.superclass.afterRender.call(this);
4781 handleClick : function(e, t){
4784 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4785 this.select(c.toUpperCase());
4790 * Selects the specified color in the palette (fires the select event)
4791 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4793 select : function(color){
4794 color = color.replace("#", "");
4795 if(color != this.value || this.allowReselect){
4798 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4800 el.child("a.color-"+color).addClass("x-color-palette-sel");
4802 this.fireEvent("select", this, color);
4807 * Ext JS Library 1.1.1
4808 * Copyright(c) 2006-2007, Ext JS, LLC.
4810 * Originally Released Under LGPL - original licence link has changed is not relivant.
4813 * <script type="text/javascript">
4817 * @class Roo.DatePicker
4818 * @extends Roo.Component
4819 * Simple date picker class.
4821 * Create a new DatePicker
4822 * @param {Object} config The config object
4824 Roo.DatePicker = function(config){
4825 Roo.DatePicker.superclass.constructor.call(this, config);
4827 this.value = config && config.value ?
4828 config.value.clearTime() : new Date().clearTime();
4833 * Fires when a date is selected
4834 * @param {DatePicker} this
4835 * @param {Date} date The selected date
4839 * @event monthchange
4840 * Fires when the displayed month changes
4841 * @param {DatePicker} this
4842 * @param {Date} date The selected month
4848 this.on("select", this.handler, this.scope || this);
4850 // build the disabledDatesRE
4851 if(!this.disabledDatesRE && this.disabledDates){
4852 var dd = this.disabledDates;
4854 for(var i = 0; i < dd.length; i++){
4856 if(i != dd.length-1) {
4860 this.disabledDatesRE = new RegExp(re + ")");
4864 Roo.extend(Roo.DatePicker, Roo.Component, {
4866 * @cfg {String} todayText
4867 * The text to display on the button that selects the current date (defaults to "Today")
4869 todayText : "Today",
4871 * @cfg {String} okText
4872 * The text to display on the ok button
4874 okText : " OK ", //   to give the user extra clicking room
4876 * @cfg {String} cancelText
4877 * The text to display on the cancel button
4879 cancelText : "Cancel",
4881 * @cfg {String} todayTip
4882 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4884 todayTip : "{0} (Spacebar)",
4886 * @cfg {Date} minDate
4887 * Minimum allowable date (JavaScript date object, defaults to null)
4891 * @cfg {Date} maxDate
4892 * Maximum allowable date (JavaScript date object, defaults to null)
4896 * @cfg {String} minText
4897 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4899 minText : "This date is before the minimum date",
4901 * @cfg {String} maxText
4902 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4904 maxText : "This date is after the maximum date",
4906 * @cfg {String} format
4907 * The default date format string which can be overriden for localization support. The format must be
4908 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4912 * @cfg {Array} disabledDays
4913 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4915 disabledDays : null,
4917 * @cfg {String} disabledDaysText
4918 * The tooltip to display when the date falls on a disabled day (defaults to "")
4920 disabledDaysText : "",
4922 * @cfg {RegExp} disabledDatesRE
4923 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4925 disabledDatesRE : null,
4927 * @cfg {String} disabledDatesText
4928 * The tooltip text to display when the date falls on a disabled date (defaults to "")
4930 disabledDatesText : "",
4932 * @cfg {Boolean} constrainToViewport
4933 * True to constrain the date picker to the viewport (defaults to true)
4935 constrainToViewport : true,
4937 * @cfg {Array} monthNames
4938 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4940 monthNames : Date.monthNames,
4942 * @cfg {Array} dayNames
4943 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4945 dayNames : Date.dayNames,
4947 * @cfg {String} nextText
4948 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4950 nextText: 'Next Month (Control+Right)',
4952 * @cfg {String} prevText
4953 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4955 prevText: 'Previous Month (Control+Left)',
4957 * @cfg {String} monthYearText
4958 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4960 monthYearText: 'Choose a month (Control+Up/Down to move years)',
4962 * @cfg {Number} startDay
4963 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4967 * @cfg {Bool} showClear
4968 * Show a clear button (usefull for date form elements that can be blank.)
4974 * Sets the value of the date field
4975 * @param {Date} value The date to set
4977 setValue : function(value){
4978 var old = this.value;
4980 if (typeof(value) == 'string') {
4982 value = Date.parseDate(value, this.format);
4988 this.value = value.clearTime(true);
4990 this.update(this.value);
4995 * Gets the current selected value of the date field
4996 * @return {Date} The selected date
4998 getValue : function(){
5005 this.update(this.activeDate);
5010 onRender : function(container, position){
5013 '<table cellspacing="0">',
5014 '<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>',
5015 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5016 var dn = this.dayNames;
5017 for(var i = 0; i < 7; i++){
5018 var d = this.startDay+i;
5022 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5024 m[m.length] = "</tr></thead><tbody><tr>";
5025 for(var i = 0; i < 42; i++) {
5026 if(i % 7 == 0 && i != 0){
5027 m[m.length] = "</tr><tr>";
5029 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5031 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5032 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5034 var el = document.createElement("div");
5035 el.className = "x-date-picker";
5036 el.innerHTML = m.join("");
5038 container.dom.insertBefore(el, position);
5040 this.el = Roo.get(el);
5041 this.eventEl = Roo.get(el.firstChild);
5043 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5044 handler: this.showPrevMonth,
5046 preventDefault:true,
5050 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5051 handler: this.showNextMonth,
5053 preventDefault:true,
5057 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5059 this.monthPicker = this.el.down('div.x-date-mp');
5060 this.monthPicker.enableDisplayMode('block');
5062 var kn = new Roo.KeyNav(this.eventEl, {
5063 "left" : function(e){
5065 this.showPrevMonth() :
5066 this.update(this.activeDate.add("d", -1));
5069 "right" : function(e){
5071 this.showNextMonth() :
5072 this.update(this.activeDate.add("d", 1));
5077 this.showNextYear() :
5078 this.update(this.activeDate.add("d", -7));
5081 "down" : function(e){
5083 this.showPrevYear() :
5084 this.update(this.activeDate.add("d", 7));
5087 "pageUp" : function(e){
5088 this.showNextMonth();
5091 "pageDown" : function(e){
5092 this.showPrevMonth();
5095 "enter" : function(e){
5096 e.stopPropagation();
5103 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5105 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5107 this.el.unselectable();
5109 this.cells = this.el.select("table.x-date-inner tbody td");
5110 this.textNodes = this.el.query("table.x-date-inner tbody span");
5112 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5114 tooltip: this.monthYearText
5117 this.mbtn.on('click', this.showMonthPicker, this);
5118 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5121 var today = (new Date()).dateFormat(this.format);
5123 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5124 if (this.showClear) {
5125 baseTb.add( new Roo.Toolbar.Fill());
5128 text: String.format(this.todayText, today),
5129 tooltip: String.format(this.todayTip, today),
5130 handler: this.selectToday,
5134 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5137 if (this.showClear) {
5139 baseTb.add( new Roo.Toolbar.Fill());
5142 cls: 'x-btn-icon x-btn-clear',
5143 handler: function() {
5145 this.fireEvent("select", this, '');
5155 this.update(this.value);
5158 createMonthPicker : function(){
5159 if(!this.monthPicker.dom.firstChild){
5160 var buf = ['<table border="0" cellspacing="0">'];
5161 for(var i = 0; i < 6; i++){
5163 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5164 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5166 '<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>' :
5167 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5171 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5173 '</button><button type="button" class="x-date-mp-cancel">',
5175 '</button></td></tr>',
5178 this.monthPicker.update(buf.join(''));
5179 this.monthPicker.on('click', this.onMonthClick, this);
5180 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5182 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5183 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5185 this.mpMonths.each(function(m, a, i){
5188 m.dom.xmonth = 5 + Math.round(i * .5);
5190 m.dom.xmonth = Math.round((i-1) * .5);
5196 showMonthPicker : function(){
5197 this.createMonthPicker();
5198 var size = this.el.getSize();
5199 this.monthPicker.setSize(size);
5200 this.monthPicker.child('table').setSize(size);
5202 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5203 this.updateMPMonth(this.mpSelMonth);
5204 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5205 this.updateMPYear(this.mpSelYear);
5207 this.monthPicker.slideIn('t', {duration:.2});
5210 updateMPYear : function(y){
5212 var ys = this.mpYears.elements;
5213 for(var i = 1; i <= 10; i++){
5214 var td = ys[i-1], y2;
5216 y2 = y + Math.round(i * .5);
5217 td.firstChild.innerHTML = y2;
5220 y2 = y - (5-Math.round(i * .5));
5221 td.firstChild.innerHTML = y2;
5224 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5228 updateMPMonth : function(sm){
5229 this.mpMonths.each(function(m, a, i){
5230 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5234 selectMPMonth: function(m){
5238 onMonthClick : function(e, t){
5240 var el = new Roo.Element(t), pn;
5241 if(el.is('button.x-date-mp-cancel')){
5242 this.hideMonthPicker();
5244 else if(el.is('button.x-date-mp-ok')){
5245 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5246 this.hideMonthPicker();
5248 else if(pn = el.up('td.x-date-mp-month', 2)){
5249 this.mpMonths.removeClass('x-date-mp-sel');
5250 pn.addClass('x-date-mp-sel');
5251 this.mpSelMonth = pn.dom.xmonth;
5253 else if(pn = el.up('td.x-date-mp-year', 2)){
5254 this.mpYears.removeClass('x-date-mp-sel');
5255 pn.addClass('x-date-mp-sel');
5256 this.mpSelYear = pn.dom.xyear;
5258 else if(el.is('a.x-date-mp-prev')){
5259 this.updateMPYear(this.mpyear-10);
5261 else if(el.is('a.x-date-mp-next')){
5262 this.updateMPYear(this.mpyear+10);
5266 onMonthDblClick : function(e, t){
5268 var el = new Roo.Element(t), pn;
5269 if(pn = el.up('td.x-date-mp-month', 2)){
5270 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5271 this.hideMonthPicker();
5273 else if(pn = el.up('td.x-date-mp-year', 2)){
5274 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5275 this.hideMonthPicker();
5279 hideMonthPicker : function(disableAnim){
5280 if(this.monthPicker){
5281 if(disableAnim === true){
5282 this.monthPicker.hide();
5284 this.monthPicker.slideOut('t', {duration:.2});
5290 showPrevMonth : function(e){
5291 this.update(this.activeDate.add("mo", -1));
5295 showNextMonth : function(e){
5296 this.update(this.activeDate.add("mo", 1));
5300 showPrevYear : function(){
5301 this.update(this.activeDate.add("y", -1));
5305 showNextYear : function(){
5306 this.update(this.activeDate.add("y", 1));
5310 handleMouseWheel : function(e){
5311 var delta = e.getWheelDelta();
5313 this.showPrevMonth();
5315 } else if(delta < 0){
5316 this.showNextMonth();
5322 handleDateClick : function(e, t){
5324 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5325 this.setValue(new Date(t.dateValue));
5326 this.fireEvent("select", this, this.value);
5331 selectToday : function(){
5332 this.setValue(new Date().clearTime());
5333 this.fireEvent("select", this, this.value);
5337 update : function(date)
5339 var vd = this.activeDate;
5340 this.activeDate = date;
5342 var t = date.getTime();
5343 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5344 this.cells.removeClass("x-date-selected");
5345 this.cells.each(function(c){
5346 if(c.dom.firstChild.dateValue == t){
5347 c.addClass("x-date-selected");
5348 setTimeout(function(){
5349 try{c.dom.firstChild.focus();}catch(e){}
5358 var days = date.getDaysInMonth();
5359 var firstOfMonth = date.getFirstDateOfMonth();
5360 var startingPos = firstOfMonth.getDay()-this.startDay;
5362 if(startingPos <= this.startDay){
5366 var pm = date.add("mo", -1);
5367 var prevStart = pm.getDaysInMonth()-startingPos;
5369 var cells = this.cells.elements;
5370 var textEls = this.textNodes;
5371 days += startingPos;
5373 // convert everything to numbers so it's fast
5375 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5376 var today = new Date().clearTime().getTime();
5377 var sel = date.clearTime().getTime();
5378 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5379 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5380 var ddMatch = this.disabledDatesRE;
5381 var ddText = this.disabledDatesText;
5382 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5383 var ddaysText = this.disabledDaysText;
5384 var format = this.format;
5386 var setCellClass = function(cal, cell){
5388 var t = d.getTime();
5389 cell.firstChild.dateValue = t;
5391 cell.className += " x-date-today";
5392 cell.title = cal.todayText;
5395 cell.className += " x-date-selected";
5396 setTimeout(function(){
5397 try{cell.firstChild.focus();}catch(e){}
5402 cell.className = " x-date-disabled";
5403 cell.title = cal.minText;
5407 cell.className = " x-date-disabled";
5408 cell.title = cal.maxText;
5412 if(ddays.indexOf(d.getDay()) != -1){
5413 cell.title = ddaysText;
5414 cell.className = " x-date-disabled";
5417 if(ddMatch && format){
5418 var fvalue = d.dateFormat(format);
5419 if(ddMatch.test(fvalue)){
5420 cell.title = ddText.replace("%0", fvalue);
5421 cell.className = " x-date-disabled";
5427 for(; i < startingPos; i++) {
5428 textEls[i].innerHTML = (++prevStart);
5429 d.setDate(d.getDate()+1);
5430 cells[i].className = "x-date-prevday";
5431 setCellClass(this, cells[i]);
5433 for(; i < days; i++){
5434 intDay = i - startingPos + 1;
5435 textEls[i].innerHTML = (intDay);
5436 d.setDate(d.getDate()+1);
5437 cells[i].className = "x-date-active";
5438 setCellClass(this, cells[i]);
5441 for(; i < 42; i++) {
5442 textEls[i].innerHTML = (++extraDays);
5443 d.setDate(d.getDate()+1);
5444 cells[i].className = "x-date-nextday";
5445 setCellClass(this, cells[i]);
5448 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5449 this.fireEvent('monthchange', this, date);
5451 if(!this.internalRender){
5452 var main = this.el.dom.firstChild;
5453 var w = main.offsetWidth;
5454 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5455 Roo.fly(main).setWidth(w);
5456 this.internalRender = true;
5457 // opera does not respect the auto grow header center column
5458 // then, after it gets a width opera refuses to recalculate
5459 // without a second pass
5460 if(Roo.isOpera && !this.secondPass){
5461 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5462 this.secondPass = true;
5463 this.update.defer(10, this, [date]);
5471 * Ext JS Library 1.1.1
5472 * Copyright(c) 2006-2007, Ext JS, LLC.
5474 * Originally Released Under LGPL - original licence link has changed is not relivant.
5477 * <script type="text/javascript">
5480 * @class Roo.TabPanel
5481 * @extends Roo.util.Observable
5482 * A lightweight tab container.
5486 // basic tabs 1, built from existing content
5487 var tabs = new Roo.TabPanel("tabs1");
5488 tabs.addTab("script", "View Script");
5489 tabs.addTab("markup", "View Markup");
5490 tabs.activate("script");
5492 // more advanced tabs, built from javascript
5493 var jtabs = new Roo.TabPanel("jtabs");
5494 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5496 // set up the UpdateManager
5497 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5498 var updater = tab2.getUpdateManager();
5499 updater.setDefaultUrl("ajax1.htm");
5500 tab2.on('activate', updater.refresh, updater, true);
5502 // Use setUrl for Ajax loading
5503 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5504 tab3.setUrl("ajax2.htm", null, true);
5507 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5510 jtabs.activate("jtabs-1");
5513 * Create a new TabPanel.
5514 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5515 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5517 Roo.TabPanel = function(container, config){
5519 * The container element for this TabPanel.
5522 this.el = Roo.get(container, true);
5524 if(typeof config == "boolean"){
5525 this.tabPosition = config ? "bottom" : "top";
5527 Roo.apply(this, config);
5530 if(this.tabPosition == "bottom"){
5531 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5532 this.el.addClass("x-tabs-bottom");
5534 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5535 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5536 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5538 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5540 if(this.tabPosition != "bottom"){
5541 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5544 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5545 this.el.addClass("x-tabs-top");
5549 this.bodyEl.setStyle("position", "relative");
5552 this.activateDelegate = this.activate.createDelegate(this);
5557 * Fires when the active tab changes
5558 * @param {Roo.TabPanel} this
5559 * @param {Roo.TabPanelItem} activePanel The new active tab
5563 * @event beforetabchange
5564 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5565 * @param {Roo.TabPanel} this
5566 * @param {Object} e Set cancel to true on this object to cancel the tab change
5567 * @param {Roo.TabPanelItem} tab The tab being changed to
5569 "beforetabchange" : true
5572 Roo.EventManager.onWindowResize(this.onResize, this);
5573 this.cpad = this.el.getPadding("lr");
5574 this.hiddenCount = 0;
5577 // toolbar on the tabbar support...
5579 var tcfg = this.toolbar;
5580 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5581 this.toolbar = new Roo.Toolbar(tcfg);
5583 var tbl = tcfg.container.child('table', true);
5584 tbl.setAttribute('width', '100%');
5591 Roo.TabPanel.superclass.constructor.call(this);
5594 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5596 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5598 tabPosition : "top",
5600 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5602 currentTabWidth : 0,
5604 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5608 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5612 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5614 preferredTabWidth : 175,
5616 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5620 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5622 monitorResize : true,
5624 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
5629 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5630 * @param {String} id The id of the div to use <b>or create</b>
5631 * @param {String} text The text for the tab
5632 * @param {String} content (optional) Content to put in the TabPanelItem body
5633 * @param {Boolean} closable (optional) True to create a close icon on the tab
5634 * @return {Roo.TabPanelItem} The created TabPanelItem
5636 addTab : function(id, text, content, closable){
5637 var item = new Roo.TabPanelItem(this, id, text, closable);
5638 this.addTabItem(item);
5640 item.setContent(content);
5646 * Returns the {@link Roo.TabPanelItem} with the specified id/index
5647 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5648 * @return {Roo.TabPanelItem}
5650 getTab : function(id){
5651 return this.items[id];
5655 * Hides the {@link Roo.TabPanelItem} with the specified id/index
5656 * @param {String/Number} id The id or index of the TabPanelItem to hide.
5658 hideTab : function(id){
5659 var t = this.items[id];
5663 this.autoSizeTabs();
5668 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5669 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5671 unhideTab : function(id){
5672 var t = this.items[id];
5676 this.autoSizeTabs();
5681 * Adds an existing {@link Roo.TabPanelItem}.
5682 * @param {Roo.TabPanelItem} item The TabPanelItem to add
5684 addTabItem : function(item){
5685 this.items[item.id] = item;
5686 this.items.push(item);
5687 if(this.resizeTabs){
5688 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5689 this.autoSizeTabs();
5696 * Removes a {@link Roo.TabPanelItem}.
5697 * @param {String/Number} id The id or index of the TabPanelItem to remove.
5699 removeTab : function(id){
5700 var items = this.items;
5701 var tab = items[id];
5702 if(!tab) { return; }
5703 var index = items.indexOf(tab);
5704 if(this.active == tab && items.length > 1){
5705 var newTab = this.getNextAvailable(index);
5710 this.stripEl.dom.removeChild(tab.pnode.dom);
5711 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5712 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5714 items.splice(index, 1);
5715 delete this.items[tab.id];
5716 tab.fireEvent("close", tab);
5717 tab.purgeListeners();
5718 this.autoSizeTabs();
5721 getNextAvailable : function(start){
5722 var items = this.items;
5724 // look for a next tab that will slide over to
5725 // replace the one being removed
5726 while(index < items.length){
5727 var item = items[++index];
5728 if(item && !item.isHidden()){
5732 // if one isn't found select the previous tab (on the left)
5735 var item = items[--index];
5736 if(item && !item.isHidden()){
5744 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5745 * @param {String/Number} id The id or index of the TabPanelItem to disable.
5747 disableTab : function(id){
5748 var tab = this.items[id];
5749 if(tab && this.active != tab){
5755 * Enables a {@link Roo.TabPanelItem} that is disabled.
5756 * @param {String/Number} id The id or index of the TabPanelItem to enable.
5758 enableTab : function(id){
5759 var tab = this.items[id];
5764 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5765 * @param {String/Number} id The id or index of the TabPanelItem to activate.
5766 * @return {Roo.TabPanelItem} The TabPanelItem.
5768 activate : function(id){
5769 var tab = this.items[id];
5773 if(tab == this.active || tab.disabled){
5777 this.fireEvent("beforetabchange", this, e, tab);
5778 if(e.cancel !== true && !tab.disabled){
5782 this.active = this.items[id];
5784 this.fireEvent("tabchange", this, this.active);
5790 * Gets the active {@link Roo.TabPanelItem}.
5791 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5793 getActiveTab : function(){
5798 * Updates the tab body element to fit the height of the container element
5799 * for overflow scrolling
5800 * @param {Number} targetHeight (optional) Override the starting height from the elements height
5802 syncHeight : function(targetHeight){
5803 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5804 var bm = this.bodyEl.getMargins();
5805 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5806 this.bodyEl.setHeight(newHeight);
5810 onResize : function(){
5811 if(this.monitorResize){
5812 this.autoSizeTabs();
5817 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5819 beginUpdate : function(){
5820 this.updating = true;
5824 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5826 endUpdate : function(){
5827 this.updating = false;
5828 this.autoSizeTabs();
5832 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5834 autoSizeTabs : function(){
5835 var count = this.items.length;
5836 var vcount = count - this.hiddenCount;
5837 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5840 var w = Math.max(this.el.getWidth() - this.cpad, 10);
5841 var availWidth = Math.floor(w / vcount);
5842 var b = this.stripBody;
5843 if(b.getWidth() > w){
5844 var tabs = this.items;
5845 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5846 if(availWidth < this.minTabWidth){
5847 /*if(!this.sleft){ // incomplete scrolling code
5848 this.createScrollButtons();
5851 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5854 if(this.currentTabWidth < this.preferredTabWidth){
5855 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5861 * Returns the number of tabs in this TabPanel.
5864 getCount : function(){
5865 return this.items.length;
5869 * Resizes all the tabs to the passed width
5870 * @param {Number} The new width
5872 setTabWidth : function(width){
5873 this.currentTabWidth = width;
5874 for(var i = 0, len = this.items.length; i < len; i++) {
5875 if(!this.items[i].isHidden()) {
5876 this.items[i].setWidth(width);
5882 * Destroys this TabPanel
5883 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5885 destroy : function(removeEl){
5886 Roo.EventManager.removeResizeListener(this.onResize, this);
5887 for(var i = 0, len = this.items.length; i < len; i++){
5888 this.items[i].purgeListeners();
5890 if(removeEl === true){
5898 * @class Roo.TabPanelItem
5899 * @extends Roo.util.Observable
5900 * Represents an individual item (tab plus body) in a TabPanel.
5901 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5902 * @param {String} id The id of this TabPanelItem
5903 * @param {String} text The text for the tab of this TabPanelItem
5904 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5906 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5908 * The {@link Roo.TabPanel} this TabPanelItem belongs to
5909 * @type Roo.TabPanel
5911 this.tabPanel = tabPanel;
5913 * The id for this TabPanelItem
5918 this.disabled = false;
5922 this.loaded = false;
5923 this.closable = closable;
5926 * The body element for this TabPanelItem.
5929 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5930 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5931 this.bodyEl.setStyle("display", "block");
5932 this.bodyEl.setStyle("zoom", "1");
5935 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5937 this.el = Roo.get(els.el, true);
5938 this.inner = Roo.get(els.inner, true);
5939 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5940 this.pnode = Roo.get(els.el.parentNode, true);
5941 this.el.on("mousedown", this.onTabMouseDown, this);
5942 this.el.on("click", this.onTabClick, this);
5945 var c = Roo.get(els.close, true);
5946 c.dom.title = this.closeText;
5947 c.addClassOnOver("close-over");
5948 c.on("click", this.closeClick, this);
5954 * Fires when this tab becomes the active tab.
5955 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5956 * @param {Roo.TabPanelItem} this
5960 * @event beforeclose
5961 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5962 * @param {Roo.TabPanelItem} this
5963 * @param {Object} e Set cancel to true on this object to cancel the close.
5965 "beforeclose": true,
5968 * Fires when this tab is closed.
5969 * @param {Roo.TabPanelItem} this
5974 * Fires when this tab is no longer the active tab.
5975 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5976 * @param {Roo.TabPanelItem} this
5980 this.hidden = false;
5982 Roo.TabPanelItem.superclass.constructor.call(this);
5985 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5986 purgeListeners : function(){
5987 Roo.util.Observable.prototype.purgeListeners.call(this);
5988 this.el.removeAllListeners();
5991 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5994 this.pnode.addClass("on");
5997 this.tabPanel.stripWrap.repaint();
5999 this.fireEvent("activate", this.tabPanel, this);
6003 * Returns true if this tab is the active tab.
6006 isActive : function(){
6007 return this.tabPanel.getActiveTab() == this;
6011 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6014 this.pnode.removeClass("on");
6016 this.fireEvent("deactivate", this.tabPanel, this);
6019 hideAction : function(){
6021 this.bodyEl.setStyle("position", "absolute");
6022 this.bodyEl.setLeft("-20000px");
6023 this.bodyEl.setTop("-20000px");
6026 showAction : function(){
6027 this.bodyEl.setStyle("position", "relative");
6028 this.bodyEl.setTop("");
6029 this.bodyEl.setLeft("");
6034 * Set the tooltip for the tab.
6035 * @param {String} tooltip The tab's tooltip
6037 setTooltip : function(text){
6038 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6039 this.textEl.dom.qtip = text;
6040 this.textEl.dom.removeAttribute('title');
6042 this.textEl.dom.title = text;
6046 onTabClick : function(e){
6048 this.tabPanel.activate(this.id);
6051 onTabMouseDown : function(e){
6053 this.tabPanel.activate(this.id);
6056 getWidth : function(){
6057 return this.inner.getWidth();
6060 setWidth : function(width){
6061 var iwidth = width - this.pnode.getPadding("lr");
6062 this.inner.setWidth(iwidth);
6063 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6064 this.pnode.setWidth(width);
6068 * Show or hide the tab
6069 * @param {Boolean} hidden True to hide or false to show.
6071 setHidden : function(hidden){
6072 this.hidden = hidden;
6073 this.pnode.setStyle("display", hidden ? "none" : "");
6077 * Returns true if this tab is "hidden"
6080 isHidden : function(){
6085 * Returns the text for this tab
6088 getText : function(){
6092 autoSize : function(){
6093 //this.el.beginMeasure();
6094 this.textEl.setWidth(1);
6096 * #2804 [new] Tabs in Roojs
6097 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6099 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6100 //this.el.endMeasure();
6104 * Sets the text for the tab (Note: this also sets the tooltip text)
6105 * @param {String} text The tab's text and tooltip
6107 setText : function(text){
6109 this.textEl.update(text);
6110 this.setTooltip(text);
6111 if(!this.tabPanel.resizeTabs){
6116 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6118 activate : function(){
6119 this.tabPanel.activate(this.id);
6123 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6125 disable : function(){
6126 if(this.tabPanel.active != this){
6127 this.disabled = true;
6128 this.pnode.addClass("disabled");
6133 * Enables this TabPanelItem if it was previously disabled.
6135 enable : function(){
6136 this.disabled = false;
6137 this.pnode.removeClass("disabled");
6141 * Sets the content for this TabPanelItem.
6142 * @param {String} content The content
6143 * @param {Boolean} loadScripts true to look for and load scripts
6145 setContent : function(content, loadScripts){
6146 this.bodyEl.update(content, loadScripts);
6150 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6151 * @return {Roo.UpdateManager} The UpdateManager
6153 getUpdateManager : function(){
6154 return this.bodyEl.getUpdateManager();
6158 * Set a URL to be used to load the content for this TabPanelItem.
6159 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6160 * @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)
6161 * @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)
6162 * @return {Roo.UpdateManager} The UpdateManager
6164 setUrl : function(url, params, loadOnce){
6165 if(this.refreshDelegate){
6166 this.un('activate', this.refreshDelegate);
6168 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6169 this.on("activate", this.refreshDelegate);
6170 return this.bodyEl.getUpdateManager();
6174 _handleRefresh : function(url, params, loadOnce){
6175 if(!loadOnce || !this.loaded){
6176 var updater = this.bodyEl.getUpdateManager();
6177 updater.update(url, params, this._setLoaded.createDelegate(this));
6182 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6183 * Will fail silently if the setUrl method has not been called.
6184 * This does not activate the panel, just updates its content.
6186 refresh : function(){
6187 if(this.refreshDelegate){
6188 this.loaded = false;
6189 this.refreshDelegate();
6194 _setLoaded : function(){
6199 closeClick : function(e){
6202 this.fireEvent("beforeclose", this, o);
6203 if(o.cancel !== true){
6204 this.tabPanel.removeTab(this.id);
6208 * The text displayed in the tooltip for the close icon.
6211 closeText : "Close this tab"
6215 Roo.TabPanel.prototype.createStrip = function(container){
6216 var strip = document.createElement("div");
6217 strip.className = "x-tabs-wrap";
6218 container.appendChild(strip);
6222 Roo.TabPanel.prototype.createStripList = function(strip){
6223 // div wrapper for retard IE
6224 // returns the "tr" element.
6225 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6226 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6227 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6228 return strip.firstChild.firstChild.firstChild.firstChild;
6231 Roo.TabPanel.prototype.createBody = function(container){
6232 var body = document.createElement("div");
6233 Roo.id(body, "tab-body");
6234 Roo.fly(body).addClass("x-tabs-body");
6235 container.appendChild(body);
6239 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6240 var body = Roo.getDom(id);
6242 body = document.createElement("div");
6245 Roo.fly(body).addClass("x-tabs-item-body");
6246 bodyEl.insertBefore(body, bodyEl.firstChild);
6250 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6251 var td = document.createElement("td");
6252 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6253 //stripEl.appendChild(td);
6255 td.className = "x-tabs-closable";
6257 this.closeTpl = new Roo.Template(
6258 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6259 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6260 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6263 var el = this.closeTpl.overwrite(td, {"text": text});
6264 var close = el.getElementsByTagName("div")[0];
6265 var inner = el.getElementsByTagName("em")[0];
6266 return {"el": el, "close": close, "inner": inner};
6269 this.tabTpl = new Roo.Template(
6270 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6271 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6274 var el = this.tabTpl.overwrite(td, {"text": text});
6275 var inner = el.getElementsByTagName("em")[0];
6276 return {"el": el, "inner": inner};
6280 * Ext JS Library 1.1.1
6281 * Copyright(c) 2006-2007, Ext JS, LLC.
6283 * Originally Released Under LGPL - original licence link has changed is not relivant.
6286 * <script type="text/javascript">
6291 * @extends Roo.util.Observable
6292 * Simple Button class
6293 * @cfg {String} text The button text
6294 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6295 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6296 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6297 * @cfg {Object} scope The scope of the handler
6298 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6299 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6300 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6301 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6302 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6303 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6304 applies if enableToggle = true)
6305 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6306 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6307 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6309 * Create a new button
6310 * @param {Object} config The config object
6312 Roo.Button = function(renderTo, config)
6316 renderTo = config.renderTo || false;
6319 Roo.apply(this, config);
6323 * Fires when this button is clicked
6324 * @param {Button} this
6325 * @param {EventObject} e The click event
6330 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6331 * @param {Button} this
6332 * @param {Boolean} pressed
6337 * Fires when the mouse hovers over the button
6338 * @param {Button} this
6339 * @param {Event} e The event object
6344 * Fires when the mouse exits the button
6345 * @param {Button} this
6346 * @param {Event} e The event object
6351 * Fires when the button is rendered
6352 * @param {Button} this
6357 this.menu = Roo.menu.MenuMgr.get(this.menu);
6359 // register listeners first!! - so render can be captured..
6360 Roo.util.Observable.call(this);
6362 this.render(renderTo);
6368 Roo.extend(Roo.Button, Roo.util.Observable, {
6374 * Read-only. True if this button is hidden
6379 * Read-only. True if this button is disabled
6384 * Read-only. True if this button is pressed (only if enableToggle = true)
6390 * @cfg {Number} tabIndex
6391 * The DOM tabIndex for this button (defaults to undefined)
6393 tabIndex : undefined,
6396 * @cfg {Boolean} enableToggle
6397 * True to enable pressed/not pressed toggling (defaults to false)
6399 enableToggle: false,
6401 * @cfg {Roo.menu.Menu} menu
6402 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6406 * @cfg {String} menuAlign
6407 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6409 menuAlign : "tl-bl?",
6412 * @cfg {String} iconCls
6413 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6415 iconCls : undefined,
6417 * @cfg {String} type
6418 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6423 menuClassTarget: 'tr',
6426 * @cfg {String} clickEvent
6427 * The type of event to map to the button's event handler (defaults to 'click')
6429 clickEvent : 'click',
6432 * @cfg {Boolean} handleMouseEvents
6433 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6435 handleMouseEvents : true,
6438 * @cfg {String} tooltipType
6439 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6441 tooltipType : 'qtip',
6445 * A CSS class to apply to the button's main element.
6449 * @cfg {Roo.Template} template (Optional)
6450 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6451 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6452 * require code modifications if required elements (e.g. a button) aren't present.
6456 render : function(renderTo){
6458 if(this.hideParent){
6459 this.parentEl = Roo.get(renderTo);
6463 if(!Roo.Button.buttonTemplate){
6464 // hideous table template
6465 Roo.Button.buttonTemplate = new Roo.Template(
6466 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6467 '<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>',
6468 "</tr></tbody></table>");
6470 this.template = Roo.Button.buttonTemplate;
6472 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6473 var btnEl = btn.child("button:first");
6474 btnEl.on('focus', this.onFocus, this);
6475 btnEl.on('blur', this.onBlur, this);
6477 btn.addClass(this.cls);
6480 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6483 btnEl.addClass(this.iconCls);
6485 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6488 if(this.tabIndex !== undefined){
6489 btnEl.dom.tabIndex = this.tabIndex;
6492 if(typeof this.tooltip == 'object'){
6493 Roo.QuickTips.tips(Roo.apply({
6497 btnEl.dom[this.tooltipType] = this.tooltip;
6501 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6505 this.el.dom.id = this.el.id = this.id;
6508 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6509 this.menu.on("show", this.onMenuShow, this);
6510 this.menu.on("hide", this.onMenuHide, this);
6512 btn.addClass("x-btn");
6513 if(Roo.isIE && !Roo.isIE7){
6514 this.autoWidth.defer(1, this);
6518 if(this.handleMouseEvents){
6519 btn.on("mouseover", this.onMouseOver, this);
6520 btn.on("mouseout", this.onMouseOut, this);
6521 btn.on("mousedown", this.onMouseDown, this);
6523 btn.on(this.clickEvent, this.onClick, this);
6524 //btn.on("mouseup", this.onMouseUp, this);
6531 Roo.ButtonToggleMgr.register(this);
6533 this.el.addClass("x-btn-pressed");
6536 var repeater = new Roo.util.ClickRepeater(btn,
6537 typeof this.repeat == "object" ? this.repeat : {}
6539 repeater.on("click", this.onClick, this);
6542 this.fireEvent('render', this);
6546 * Returns the button's underlying element
6547 * @return {Roo.Element} The element
6554 * Destroys this Button and removes any listeners.
6556 destroy : function(){
6557 Roo.ButtonToggleMgr.unregister(this);
6558 this.el.removeAllListeners();
6559 this.purgeListeners();
6564 autoWidth : function(){
6566 this.el.setWidth("auto");
6567 if(Roo.isIE7 && Roo.isStrict){
6568 var ib = this.el.child('button');
6569 if(ib && ib.getWidth() > 20){
6571 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6576 this.el.beginMeasure();
6578 if(this.el.getWidth() < this.minWidth){
6579 this.el.setWidth(this.minWidth);
6582 this.el.endMeasure();
6589 * Assigns this button's click handler
6590 * @param {Function} handler The function to call when the button is clicked
6591 * @param {Object} scope (optional) Scope for the function passed in
6593 setHandler : function(handler, scope){
6594 this.handler = handler;
6599 * Sets this button's text
6600 * @param {String} text The button text
6602 setText : function(text){
6605 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6611 * Gets the text for this button
6612 * @return {String} The button text
6614 getText : function(){
6622 this.hidden = false;
6624 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6634 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6639 * Convenience function for boolean show/hide
6640 * @param {Boolean} visible True to show, false to hide
6642 setVisible: function(visible){
6650 * Similar to toggle, but does not trigger event.
6651 * @param {Boolean} state [required] Force a particular state
6653 setPressed : function(state)
6655 if(state != this.pressed){
6657 this.el.addClass("x-btn-pressed");
6658 this.pressed = true;
6660 this.el.removeClass("x-btn-pressed");
6661 this.pressed = false;
6667 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6668 * @param {Boolean} state (optional) Force a particular state
6670 toggle : function(state){
6671 state = state === undefined ? !this.pressed : state;
6672 if(state != this.pressed){
6674 this.el.addClass("x-btn-pressed");
6675 this.pressed = true;
6676 this.fireEvent("toggle", this, true);
6678 this.el.removeClass("x-btn-pressed");
6679 this.pressed = false;
6680 this.fireEvent("toggle", this, false);
6682 if(this.toggleHandler){
6683 this.toggleHandler.call(this.scope || this, this, state);
6694 this.el.child('button:first').focus();
6698 * Disable this button
6700 disable : function(){
6702 this.el.addClass("x-btn-disabled");
6704 this.disabled = true;
6708 * Enable this button
6710 enable : function(){
6712 this.el.removeClass("x-btn-disabled");
6714 this.disabled = false;
6718 * Convenience function for boolean enable/disable
6719 * @param {Boolean} enabled True to enable, false to disable
6721 setDisabled : function(v){
6722 this[v !== true ? "enable" : "disable"]();
6726 onClick : function(e)
6735 if(this.enableToggle){
6738 if(this.menu && !this.menu.isVisible()){
6739 this.menu.show(this.el, this.menuAlign);
6741 this.fireEvent("click", this, e);
6743 this.el.removeClass("x-btn-over");
6744 this.handler.call(this.scope || this, this, e);
6749 onMouseOver : function(e){
6751 this.el.addClass("x-btn-over");
6752 this.fireEvent('mouseover', this, e);
6756 onMouseOut : function(e){
6757 if(!e.within(this.el, true)){
6758 this.el.removeClass("x-btn-over");
6759 this.fireEvent('mouseout', this, e);
6763 onFocus : function(e){
6765 this.el.addClass("x-btn-focus");
6769 onBlur : function(e){
6770 this.el.removeClass("x-btn-focus");
6773 onMouseDown : function(e){
6774 if(!this.disabled && e.button == 0){
6775 this.el.addClass("x-btn-click");
6776 Roo.get(document).on('mouseup', this.onMouseUp, this);
6780 onMouseUp : function(e){
6782 this.el.removeClass("x-btn-click");
6783 Roo.get(document).un('mouseup', this.onMouseUp, this);
6787 onMenuShow : function(e){
6788 this.el.addClass("x-btn-menu-active");
6791 onMenuHide : function(e){
6792 this.el.removeClass("x-btn-menu-active");
6796 // Private utility class used by Button
6797 Roo.ButtonToggleMgr = function(){
6800 function toggleGroup(btn, state){
6802 var g = groups[btn.toggleGroup];
6803 for(var i = 0, l = g.length; i < l; i++){
6812 register : function(btn){
6813 if(!btn.toggleGroup){
6816 var g = groups[btn.toggleGroup];
6818 g = groups[btn.toggleGroup] = [];
6821 btn.on("toggle", toggleGroup);
6824 unregister : function(btn){
6825 if(!btn.toggleGroup){
6828 var g = groups[btn.toggleGroup];
6831 btn.un("toggle", toggleGroup);
6837 * Ext JS Library 1.1.1
6838 * Copyright(c) 2006-2007, Ext JS, LLC.
6840 * Originally Released Under LGPL - original licence link has changed is not relivant.
6843 * <script type="text/javascript">
6847 * @class Roo.SplitButton
6848 * @extends Roo.Button
6849 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6850 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
6851 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6852 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6853 * @cfg {String} arrowTooltip The title attribute of the arrow
6855 * Create a new menu button
6856 * @param {String/HTMLElement/Element} renderTo The element to append the button to
6857 * @param {Object} config The config object
6859 Roo.SplitButton = function(renderTo, config){
6860 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6863 * Fires when this button's arrow is clicked
6864 * @param {SplitButton} this
6865 * @param {EventObject} e The click event
6867 this.addEvents({"arrowclick":true});
6870 Roo.extend(Roo.SplitButton, Roo.Button, {
6871 render : function(renderTo){
6872 // this is one sweet looking template!
6873 var tpl = new Roo.Template(
6874 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6875 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6876 '<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>',
6877 "</tbody></table></td><td>",
6878 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6879 '<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>',
6880 "</tbody></table></td></tr></table>"
6882 var btn = tpl.append(renderTo, [this.text, this.type], true);
6883 var btnEl = btn.child("button");
6885 btn.addClass(this.cls);
6888 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6891 btnEl.addClass(this.iconCls);
6893 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6897 if(this.handleMouseEvents){
6898 btn.on("mouseover", this.onMouseOver, this);
6899 btn.on("mouseout", this.onMouseOut, this);
6900 btn.on("mousedown", this.onMouseDown, this);
6901 btn.on("mouseup", this.onMouseUp, this);
6903 btn.on(this.clickEvent, this.onClick, this);
6905 if(typeof this.tooltip == 'object'){
6906 Roo.QuickTips.tips(Roo.apply({
6910 btnEl.dom[this.tooltipType] = this.tooltip;
6913 if(this.arrowTooltip){
6914 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6923 this.el.addClass("x-btn-pressed");
6925 if(Roo.isIE && !Roo.isIE7){
6926 this.autoWidth.defer(1, this);
6931 this.menu.on("show", this.onMenuShow, this);
6932 this.menu.on("hide", this.onMenuHide, this);
6934 this.fireEvent('render', this);
6938 autoWidth : function(){
6940 var tbl = this.el.child("table:first");
6941 var tbl2 = this.el.child("table:last");
6942 this.el.setWidth("auto");
6943 tbl.setWidth("auto");
6944 if(Roo.isIE7 && Roo.isStrict){
6945 var ib = this.el.child('button:first');
6946 if(ib && ib.getWidth() > 20){
6948 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6953 this.el.beginMeasure();
6955 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6956 tbl.setWidth(this.minWidth-tbl2.getWidth());
6959 this.el.endMeasure();
6962 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6966 * Sets this button's click handler
6967 * @param {Function} handler The function to call when the button is clicked
6968 * @param {Object} scope (optional) Scope for the function passed above
6970 setHandler : function(handler, scope){
6971 this.handler = handler;
6976 * Sets this button's arrow click handler
6977 * @param {Function} handler The function to call when the arrow is clicked
6978 * @param {Object} scope (optional) Scope for the function passed above
6980 setArrowHandler : function(handler, scope){
6981 this.arrowHandler = handler;
6990 this.el.child("button:first").focus();
6995 onClick : function(e){
6998 if(e.getTarget(".x-btn-menu-arrow-wrap")){
6999 if(this.menu && !this.menu.isVisible()){
7000 this.menu.show(this.el, this.menuAlign);
7002 this.fireEvent("arrowclick", this, e);
7003 if(this.arrowHandler){
7004 this.arrowHandler.call(this.scope || this, this, e);
7007 this.fireEvent("click", this, e);
7009 this.handler.call(this.scope || this, this, e);
7015 onMouseDown : function(e){
7017 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7021 onMouseUp : function(e){
7022 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7028 Roo.MenuButton = Roo.SplitButton;/*
7030 * Ext JS Library 1.1.1
7031 * Copyright(c) 2006-2007, Ext JS, LLC.
7033 * Originally Released Under LGPL - original licence link has changed is not relivant.
7036 * <script type="text/javascript">
7040 * @class Roo.Toolbar
7041 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7042 * Basic Toolbar class.
7044 * Creates a new Toolbar
7045 * @param {Object} container The config object
7047 Roo.Toolbar = function(container, buttons, config)
7049 /// old consturctor format still supported..
7050 if(container instanceof Array){ // omit the container for later rendering
7051 buttons = container;
7055 if (typeof(container) == 'object' && container.xtype) {
7057 container = config.container;
7058 buttons = config.buttons || []; // not really - use items!!
7061 if (config && config.items) {
7062 xitems = config.items;
7063 delete config.items;
7065 Roo.apply(this, config);
7066 this.buttons = buttons;
7069 this.render(container);
7071 this.xitems = xitems;
7072 Roo.each(xitems, function(b) {
7078 Roo.Toolbar.prototype = {
7080 * @cfg {Array} items
7081 * array of button configs or elements to add (will be converted to a MixedCollection)
7085 * @cfg {String/HTMLElement/Element} container
7086 * The id or element that will contain the toolbar
7089 render : function(ct){
7090 this.el = Roo.get(ct);
7092 this.el.addClass(this.cls);
7094 // using a table allows for vertical alignment
7095 // 100% width is needed by Safari...
7096 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7097 this.tr = this.el.child("tr", true);
7099 this.items = new Roo.util.MixedCollection(false, function(o){
7100 return o.id || ("item" + (++autoId));
7103 this.add.apply(this, this.buttons);
7104 delete this.buttons;
7109 * Adds element(s) to the toolbar -- this function takes a variable number of
7110 * arguments of mixed type and adds them to the toolbar.
7111 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7113 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7114 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7115 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7116 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7117 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7118 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7119 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7120 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7121 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7123 * @param {Mixed} arg2
7124 * @param {Mixed} etc.
7127 var a = arguments, l = a.length;
7128 for(var i = 0; i < l; i++){
7133 _add : function(el) {
7136 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7139 if (el.applyTo){ // some kind of form field
7140 return this.addField(el);
7142 if (el.render){ // some kind of Toolbar.Item
7143 return this.addItem(el);
7145 if (typeof el == "string"){ // string
7146 if(el == "separator" || el == "-"){
7147 return this.addSeparator();
7150 return this.addSpacer();
7153 return this.addFill();
7155 return this.addText(el);
7158 if(el.tagName){ // element
7159 return this.addElement(el);
7161 if(typeof el == "object"){ // must be button config?
7162 return this.addButton(el);
7170 * Add an Xtype element
7171 * @param {Object} xtype Xtype Object
7172 * @return {Object} created Object
7174 addxtype : function(e){
7179 * Returns the Element for this toolbar.
7180 * @return {Roo.Element}
7188 * @return {Roo.Toolbar.Item} The separator item
7190 addSeparator : function(){
7191 return this.addItem(new Roo.Toolbar.Separator());
7195 * Adds a spacer element
7196 * @return {Roo.Toolbar.Spacer} The spacer item
7198 addSpacer : function(){
7199 return this.addItem(new Roo.Toolbar.Spacer());
7203 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7204 * @return {Roo.Toolbar.Fill} The fill item
7206 addFill : function(){
7207 return this.addItem(new Roo.Toolbar.Fill());
7211 * Adds any standard HTML element to the toolbar
7212 * @param {String/HTMLElement/Element} el The element or id of the element to add
7213 * @return {Roo.Toolbar.Item} The element's item
7215 addElement : function(el){
7216 return this.addItem(new Roo.Toolbar.Item(el));
7219 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7220 * @type Roo.util.MixedCollection
7225 * Adds any Toolbar.Item or subclass
7226 * @param {Roo.Toolbar.Item} item
7227 * @return {Roo.Toolbar.Item} The item
7229 addItem : function(item){
7230 var td = this.nextBlock();
7232 this.items.add(item);
7237 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7238 * @param {Object/Array} config A button config or array of configs
7239 * @return {Roo.Toolbar.Button/Array}
7241 addButton : function(config){
7242 if(config instanceof Array){
7244 for(var i = 0, len = config.length; i < len; i++) {
7245 buttons.push(this.addButton(config[i]));
7250 if(!(config instanceof Roo.Toolbar.Button)){
7252 new Roo.Toolbar.SplitButton(config) :
7253 new Roo.Toolbar.Button(config);
7255 var td = this.nextBlock();
7262 * Adds text to the toolbar
7263 * @param {String} text The text to add
7264 * @return {Roo.Toolbar.Item} The element's item
7266 addText : function(text){
7267 return this.addItem(new Roo.Toolbar.TextItem(text));
7271 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7272 * @param {Number} index The index where the item is to be inserted
7273 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7274 * @return {Roo.Toolbar.Button/Item}
7276 insertButton : function(index, item){
7277 if(item instanceof Array){
7279 for(var i = 0, len = item.length; i < len; i++) {
7280 buttons.push(this.insertButton(index + i, item[i]));
7284 if (!(item instanceof Roo.Toolbar.Button)){
7285 item = new Roo.Toolbar.Button(item);
7287 var td = document.createElement("td");
7288 this.tr.insertBefore(td, this.tr.childNodes[index]);
7290 this.items.insert(index, item);
7295 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7296 * @param {Object} config
7297 * @return {Roo.Toolbar.Item} The element's item
7299 addDom : function(config, returnEl){
7300 var td = this.nextBlock();
7301 Roo.DomHelper.overwrite(td, config);
7302 var ti = new Roo.Toolbar.Item(td.firstChild);
7309 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7310 * @type Roo.util.MixedCollection
7315 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7316 * Note: the field should not have been rendered yet. For a field that has already been
7317 * rendered, use {@link #addElement}.
7318 * @param {Roo.form.Field} field
7319 * @return {Roo.ToolbarItem}
7323 addField : function(field) {
7326 this.fields = new Roo.util.MixedCollection(false, function(o){
7327 return o.id || ("item" + (++autoId));
7332 var td = this.nextBlock();
7334 var ti = new Roo.Toolbar.Item(td.firstChild);
7337 this.fields.add(field);
7348 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7349 this.el.child('div').hide();
7357 this.el.child('div').show();
7361 nextBlock : function(){
7362 var td = document.createElement("td");
7363 this.tr.appendChild(td);
7368 destroy : function(){
7369 if(this.items){ // rendered?
7370 Roo.destroy.apply(Roo, this.items.items);
7372 if(this.fields){ // rendered?
7373 Roo.destroy.apply(Roo, this.fields.items);
7375 Roo.Element.uncache(this.el, this.tr);
7380 * @class Roo.Toolbar.Item
7381 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7383 * Creates a new Item
7384 * @param {HTMLElement} el
7386 Roo.Toolbar.Item = function(el){
7388 if (typeof (el.xtype) != 'undefined') {
7393 this.el = Roo.getDom(el);
7394 this.id = Roo.id(this.el);
7395 this.hidden = false;
7400 * Fires when the button is rendered
7401 * @param {Button} this
7405 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7407 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7408 //Roo.Toolbar.Item.prototype = {
7411 * Get this item's HTML Element
7412 * @return {HTMLElement}
7419 render : function(td){
7422 td.appendChild(this.el);
7424 this.fireEvent('render', this);
7428 * Removes and destroys this item.
7430 destroy : function(){
7431 this.td.parentNode.removeChild(this.td);
7438 this.hidden = false;
7439 this.td.style.display = "";
7447 this.td.style.display = "none";
7451 * Convenience function for boolean show/hide.
7452 * @param {Boolean} visible true to show/false to hide
7454 setVisible: function(visible){
7463 * Try to focus this item.
7466 Roo.fly(this.el).focus();
7470 * Disables this item.
7472 disable : function(){
7473 Roo.fly(this.td).addClass("x-item-disabled");
7474 this.disabled = true;
7475 this.el.disabled = true;
7479 * Enables this item.
7481 enable : function(){
7482 Roo.fly(this.td).removeClass("x-item-disabled");
7483 this.disabled = false;
7484 this.el.disabled = false;
7490 * @class Roo.Toolbar.Separator
7491 * @extends Roo.Toolbar.Item
7492 * A simple toolbar separator class
7494 * Creates a new Separator
7496 Roo.Toolbar.Separator = function(cfg){
7498 var s = document.createElement("span");
7499 s.className = "ytb-sep";
7504 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7506 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7508 disable:Roo.emptyFn,
7513 * @class Roo.Toolbar.Spacer
7514 * @extends Roo.Toolbar.Item
7515 * A simple element that adds extra horizontal space to a toolbar.
7517 * Creates a new Spacer
7519 Roo.Toolbar.Spacer = function(cfg){
7520 var s = document.createElement("div");
7521 s.className = "ytb-spacer";
7525 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7527 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7529 disable:Roo.emptyFn,
7534 * @class Roo.Toolbar.Fill
7535 * @extends Roo.Toolbar.Spacer
7536 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7538 * Creates a new Spacer
7540 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7542 render : function(td){
7543 td.style.width = '100%';
7544 Roo.Toolbar.Fill.superclass.render.call(this, td);
7549 * @class Roo.Toolbar.TextItem
7550 * @extends Roo.Toolbar.Item
7551 * A simple class that renders text directly into a toolbar.
7553 * Creates a new TextItem
7554 * @cfg {string} text
7556 Roo.Toolbar.TextItem = function(cfg){
7557 var text = cfg || "";
7558 if (typeof(cfg) == 'object') {
7559 text = cfg.text || "";
7563 var s = document.createElement("span");
7564 s.className = "ytb-text";
7570 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7572 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7576 disable:Roo.emptyFn,
7582 this.hidden = false;
7583 this.el.style.display = "";
7591 this.el.style.display = "none";
7597 * @class Roo.Toolbar.Button
7598 * @extends Roo.Button
7599 * A button that renders into a toolbar.
7601 * Creates a new Button
7602 * @param {Object} config A standard {@link Roo.Button} config object
7604 Roo.Toolbar.Button = function(config){
7605 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7607 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7611 render : function(td){
7613 Roo.Toolbar.Button.superclass.render.call(this, td);
7617 * Removes and destroys this button
7619 destroy : function(){
7620 Roo.Toolbar.Button.superclass.destroy.call(this);
7621 this.td.parentNode.removeChild(this.td);
7628 this.hidden = false;
7629 this.td.style.display = "";
7637 this.td.style.display = "none";
7641 * Disables this item
7643 disable : function(){
7644 Roo.fly(this.td).addClass("x-item-disabled");
7645 this.disabled = true;
7651 enable : function(){
7652 Roo.fly(this.td).removeClass("x-item-disabled");
7653 this.disabled = false;
7657 Roo.ToolbarButton = Roo.Toolbar.Button;
7660 * @class Roo.Toolbar.SplitButton
7661 * @extends Roo.SplitButton
7662 * A menu button that renders into a toolbar.
7664 * Creates a new SplitButton
7665 * @param {Object} config A standard {@link Roo.SplitButton} config object
7667 Roo.Toolbar.SplitButton = function(config){
7668 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7670 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7671 render : function(td){
7673 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7677 * Removes and destroys this button
7679 destroy : function(){
7680 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7681 this.td.parentNode.removeChild(this.td);
7688 this.hidden = false;
7689 this.td.style.display = "";
7697 this.td.style.display = "none";
7702 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7704 * Ext JS Library 1.1.1
7705 * Copyright(c) 2006-2007, Ext JS, LLC.
7707 * Originally Released Under LGPL - original licence link has changed is not relivant.
7710 * <script type="text/javascript">
7714 * @class Roo.PagingToolbar
7715 * @extends Roo.Toolbar
7716 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7717 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7719 * Create a new PagingToolbar
7720 * @param {Object} config The config object
7722 Roo.PagingToolbar = function(el, ds, config)
7724 // old args format still supported... - xtype is prefered..
7725 if (typeof(el) == 'object' && el.xtype) {
7726 // created from xtype...
7729 el = config.container;
7733 items = config.items;
7737 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7740 this.renderButtons(this.el);
7743 // supprot items array.
7745 Roo.each(items, function(e) {
7746 this.add(Roo.factory(e));
7751 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7754 * @cfg {String/HTMLElement/Element} container
7755 * container The id or element that will contain the toolbar
7758 * @cfg {Boolean} displayInfo
7759 * True to display the displayMsg (defaults to false)
7764 * @cfg {Number} pageSize
7765 * The number of records to display per page (defaults to 20)
7769 * @cfg {String} displayMsg
7770 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7772 displayMsg : 'Displaying {0} - {1} of {2}',
7774 * @cfg {String} emptyMsg
7775 * The message to display when no records are found (defaults to "No data to display")
7777 emptyMsg : 'No data to display',
7779 * Customizable piece of the default paging text (defaults to "Page")
7782 beforePageText : "Page",
7784 * Customizable piece of the default paging text (defaults to "of %0")
7787 afterPageText : "of {0}",
7789 * Customizable piece of the default paging text (defaults to "First Page")
7792 firstText : "First Page",
7794 * Customizable piece of the default paging text (defaults to "Previous Page")
7797 prevText : "Previous Page",
7799 * Customizable piece of the default paging text (defaults to "Next Page")
7802 nextText : "Next Page",
7804 * Customizable piece of the default paging text (defaults to "Last Page")
7807 lastText : "Last Page",
7809 * Customizable piece of the default paging text (defaults to "Refresh")
7812 refreshText : "Refresh",
7815 renderButtons : function(el){
7816 Roo.PagingToolbar.superclass.render.call(this, el);
7817 this.first = this.addButton({
7818 tooltip: this.firstText,
7819 cls: "x-btn-icon x-grid-page-first",
7821 handler: this.onClick.createDelegate(this, ["first"])
7823 this.prev = this.addButton({
7824 tooltip: this.prevText,
7825 cls: "x-btn-icon x-grid-page-prev",
7827 handler: this.onClick.createDelegate(this, ["prev"])
7829 //this.addSeparator();
7830 this.add(this.beforePageText);
7831 this.field = Roo.get(this.addDom({
7836 cls: "x-grid-page-number"
7838 this.field.on("keydown", this.onPagingKeydown, this);
7839 this.field.on("focus", function(){this.dom.select();});
7840 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7841 this.field.setHeight(18);
7842 //this.addSeparator();
7843 this.next = this.addButton({
7844 tooltip: this.nextText,
7845 cls: "x-btn-icon x-grid-page-next",
7847 handler: this.onClick.createDelegate(this, ["next"])
7849 this.last = this.addButton({
7850 tooltip: this.lastText,
7851 cls: "x-btn-icon x-grid-page-last",
7853 handler: this.onClick.createDelegate(this, ["last"])
7855 //this.addSeparator();
7856 this.loading = this.addButton({
7857 tooltip: this.refreshText,
7858 cls: "x-btn-icon x-grid-loading",
7859 handler: this.onClick.createDelegate(this, ["refresh"])
7862 if(this.displayInfo){
7863 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7868 updateInfo : function(){
7870 var count = this.ds.getCount();
7871 var msg = count == 0 ?
7875 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
7877 this.displayEl.update(msg);
7882 onLoad : function(ds, r, o){
7883 this.cursor = o.params ? o.params.start : 0;
7884 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7886 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7887 this.field.dom.value = ap;
7888 this.first.setDisabled(ap == 1);
7889 this.prev.setDisabled(ap == 1);
7890 this.next.setDisabled(ap == ps);
7891 this.last.setDisabled(ap == ps);
7892 this.loading.enable();
7897 getPageData : function(){
7898 var total = this.ds.getTotalCount();
7901 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7902 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7907 onLoadError : function(){
7908 this.loading.enable();
7912 onPagingKeydown : function(e){
7914 var d = this.getPageData();
7916 var v = this.field.dom.value, pageNum;
7917 if(!v || isNaN(pageNum = parseInt(v, 10))){
7918 this.field.dom.value = d.activePage;
7921 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7922 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7925 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))
7927 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7928 this.field.dom.value = pageNum;
7929 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7932 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7934 var v = this.field.dom.value, pageNum;
7935 var increment = (e.shiftKey) ? 10 : 1;
7936 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7939 if(!v || isNaN(pageNum = parseInt(v, 10))) {
7940 this.field.dom.value = d.activePage;
7943 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7945 this.field.dom.value = parseInt(v, 10) + increment;
7946 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7947 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7954 beforeLoad : function(){
7956 this.loading.disable();
7960 * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7961 * @param {String} which (first|prev|next|last|refresh) which button to press.
7965 onClick : function(which){
7969 ds.load({params:{start: 0, limit: this.pageSize}});
7972 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7975 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7978 var total = ds.getTotalCount();
7979 var extra = total % this.pageSize;
7980 var lastStart = extra ? (total - extra) : total-this.pageSize;
7981 ds.load({params:{start: lastStart, limit: this.pageSize}});
7984 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7990 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7991 * @param {Roo.data.Store} store The data store to unbind
7993 unbind : function(ds){
7994 ds.un("beforeload", this.beforeLoad, this);
7995 ds.un("load", this.onLoad, this);
7996 ds.un("loadexception", this.onLoadError, this);
7997 ds.un("remove", this.updateInfo, this);
7998 ds.un("add", this.updateInfo, this);
7999 this.ds = undefined;
8003 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8004 * @param {Roo.data.Store} store The data store to bind
8006 bind : function(ds){
8007 ds.on("beforeload", this.beforeLoad, this);
8008 ds.on("load", this.onLoad, this);
8009 ds.on("loadexception", this.onLoadError, this);
8010 ds.on("remove", this.updateInfo, this);
8011 ds.on("add", this.updateInfo, this);
8016 * Ext JS Library 1.1.1
8017 * Copyright(c) 2006-2007, Ext JS, LLC.
8019 * Originally Released Under LGPL - original licence link has changed is not relivant.
8022 * <script type="text/javascript">
8026 * @class Roo.Resizable
8027 * @extends Roo.util.Observable
8028 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8029 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8030 * 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
8031 * the element will be wrapped for you automatically.</p>
8032 * <p>Here is the list of valid resize handles:</p>
8035 ------ -------------------
8044 'hd' horizontal drag
8047 * <p>Here's an example showing the creation of a typical Resizable:</p>
8049 var resizer = new Roo.Resizable("element-id", {
8057 resizer.on("resize", myHandler);
8059 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8060 * resizer.east.setDisplayed(false);</p>
8061 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8062 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8063 * resize operation's new size (defaults to [0, 0])
8064 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8065 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8066 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8067 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8068 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8069 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8070 * @cfg {Number} width The width of the element in pixels (defaults to null)
8071 * @cfg {Number} height The height of the element in pixels (defaults to null)
8072 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8073 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8074 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8075 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8076 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8077 * in favor of the handles config option (defaults to false)
8078 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8079 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8080 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8081 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8082 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8083 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8084 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8085 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8086 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8087 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8088 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8090 * Create a new resizable component
8091 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8092 * @param {Object} config configuration options
8094 Roo.Resizable = function(el, config)
8096 this.el = Roo.get(el);
8098 if(config && config.wrap){
8099 config.resizeChild = this.el;
8100 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8101 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8102 this.el.setStyle("overflow", "hidden");
8103 this.el.setPositioning(config.resizeChild.getPositioning());
8104 config.resizeChild.clearPositioning();
8105 if(!config.width || !config.height){
8106 var csize = config.resizeChild.getSize();
8107 this.el.setSize(csize.width, csize.height);
8109 if(config.pinned && !config.adjustments){
8110 config.adjustments = "auto";
8114 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8115 this.proxy.unselectable();
8116 this.proxy.enableDisplayMode('block');
8118 Roo.apply(this, config);
8121 this.disableTrackOver = true;
8122 this.el.addClass("x-resizable-pinned");
8124 // if the element isn't positioned, make it relative
8125 var position = this.el.getStyle("position");
8126 if(position != "absolute" && position != "fixed"){
8127 this.el.setStyle("position", "relative");
8129 if(!this.handles){ // no handles passed, must be legacy style
8130 this.handles = 's,e,se';
8131 if(this.multiDirectional){
8132 this.handles += ',n,w';
8135 if(this.handles == "all"){
8136 this.handles = "n s e w ne nw se sw";
8138 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8139 var ps = Roo.Resizable.positions;
8140 for(var i = 0, len = hs.length; i < len; i++){
8141 if(hs[i] && ps[hs[i]]){
8142 var pos = ps[hs[i]];
8143 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8147 this.corner = this.southeast;
8149 // updateBox = the box can move..
8150 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8151 this.updateBox = true;
8154 this.activeHandle = null;
8156 if(this.resizeChild){
8157 if(typeof this.resizeChild == "boolean"){
8158 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8160 this.resizeChild = Roo.get(this.resizeChild, true);
8164 if(this.adjustments == "auto"){
8165 var rc = this.resizeChild;
8166 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8167 if(rc && (hw || hn)){
8168 rc.position("relative");
8169 rc.setLeft(hw ? hw.el.getWidth() : 0);
8170 rc.setTop(hn ? hn.el.getHeight() : 0);
8172 this.adjustments = [
8173 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8174 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8179 this.dd = this.dynamic ?
8180 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8181 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8187 * @event beforeresize
8188 * Fired before resize is allowed. Set enabled to false to cancel resize.
8189 * @param {Roo.Resizable} this
8190 * @param {Roo.EventObject} e The mousedown event
8192 "beforeresize" : true,
8196 * @param {Roo.Resizable} this
8197 * @param {Number} x The new x position
8198 * @param {Number} y The new y position
8199 * @param {Number} w The new w width
8200 * @param {Number} h The new h hight
8201 * @param {Roo.EventObject} e The mouseup event
8206 * Fired after a resize.
8207 * @param {Roo.Resizable} this
8208 * @param {Number} width The new width
8209 * @param {Number} height The new height
8210 * @param {Roo.EventObject} e The mouseup event
8215 if(this.width !== null && this.height !== null){
8216 this.resizeTo(this.width, this.height);
8218 this.updateChildSize();
8221 this.el.dom.style.zoom = 1;
8223 Roo.Resizable.superclass.constructor.call(this);
8226 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8227 resizeChild : false,
8228 adjustments : [0, 0],
8238 multiDirectional : false,
8239 disableTrackOver : false,
8240 easing : 'easeOutStrong',
8242 heightIncrement : 0,
8246 preserveRatio : false,
8253 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8255 constrainTo: undefined,
8257 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8259 resizeRegion: undefined,
8263 * Perform a manual resize
8264 * @param {Number} width
8265 * @param {Number} height
8267 resizeTo : function(width, height){
8268 this.el.setSize(width, height);
8269 this.updateChildSize();
8270 this.fireEvent("resize", this, width, height, null);
8274 startSizing : function(e, handle){
8275 this.fireEvent("beforeresize", this, e);
8276 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8279 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8280 this.overlay.unselectable();
8281 this.overlay.enableDisplayMode("block");
8282 this.overlay.on("mousemove", this.onMouseMove, this);
8283 this.overlay.on("mouseup", this.onMouseUp, this);
8285 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8287 this.resizing = true;
8288 this.startBox = this.el.getBox();
8289 this.startPoint = e.getXY();
8290 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8291 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8293 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8294 this.overlay.show();
8296 if(this.constrainTo) {
8297 var ct = Roo.get(this.constrainTo);
8298 this.resizeRegion = ct.getRegion().adjust(
8299 ct.getFrameWidth('t'),
8300 ct.getFrameWidth('l'),
8301 -ct.getFrameWidth('b'),
8302 -ct.getFrameWidth('r')
8306 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8308 this.proxy.setBox(this.startBox);
8310 this.proxy.setStyle('visibility', 'visible');
8316 onMouseDown : function(handle, e){
8319 this.activeHandle = handle;
8320 this.startSizing(e, handle);
8325 onMouseUp : function(e){
8326 var size = this.resizeElement();
8327 this.resizing = false;
8329 this.overlay.hide();
8331 this.fireEvent("resize", this, size.width, size.height, e);
8335 updateChildSize : function(){
8337 if(this.resizeChild){
8339 var child = this.resizeChild;
8340 var adj = this.adjustments;
8341 if(el.dom.offsetWidth){
8342 var b = el.getSize(true);
8343 child.setSize(b.width+adj[0], b.height+adj[1]);
8345 // Second call here for IE
8346 // The first call enables instant resizing and
8347 // the second call corrects scroll bars if they
8350 setTimeout(function(){
8351 if(el.dom.offsetWidth){
8352 var b = el.getSize(true);
8353 child.setSize(b.width+adj[0], b.height+adj[1]);
8361 snap : function(value, inc, min){
8362 if(!inc || !value) {
8365 var newValue = value;
8366 var m = value % inc;
8369 newValue = value + (inc-m);
8371 newValue = value - m;
8374 return Math.max(min, newValue);
8378 resizeElement : function(){
8379 var box = this.proxy.getBox();
8381 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8383 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8385 this.updateChildSize();
8393 constrain : function(v, diff, m, mx){
8396 }else if(v - diff > mx){
8403 onMouseMove : function(e){
8406 try{// try catch so if something goes wrong the user doesn't get hung
8408 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8412 //var curXY = this.startPoint;
8413 var curSize = this.curSize || this.startBox;
8414 var x = this.startBox.x, y = this.startBox.y;
8416 var w = curSize.width, h = curSize.height;
8418 var mw = this.minWidth, mh = this.minHeight;
8419 var mxw = this.maxWidth, mxh = this.maxHeight;
8420 var wi = this.widthIncrement;
8421 var hi = this.heightIncrement;
8423 var eventXY = e.getXY();
8424 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8425 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8427 var pos = this.activeHandle.position;
8432 w = Math.min(Math.max(mw, w), mxw);
8437 h = Math.min(Math.max(mh, h), mxh);
8442 w = Math.min(Math.max(mw, w), mxw);
8443 h = Math.min(Math.max(mh, h), mxh);
8446 diffY = this.constrain(h, diffY, mh, mxh);
8453 var adiffX = Math.abs(diffX);
8454 var sub = (adiffX % wi); // how much
8455 if (sub > (wi/2)) { // far enough to snap
8456 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8458 // remove difference..
8459 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8463 x = Math.max(this.minX, x);
8466 diffX = this.constrain(w, diffX, mw, mxw);
8472 w = Math.min(Math.max(mw, w), mxw);
8473 diffY = this.constrain(h, diffY, mh, mxh);
8478 diffX = this.constrain(w, diffX, mw, mxw);
8479 diffY = this.constrain(h, diffY, mh, mxh);
8486 diffX = this.constrain(w, diffX, mw, mxw);
8488 h = Math.min(Math.max(mh, h), mxh);
8494 var sw = this.snap(w, wi, mw);
8495 var sh = this.snap(h, hi, mh);
8496 if(sw != w || sh != h){
8519 if(this.preserveRatio){
8524 h = Math.min(Math.max(mh, h), mxh);
8529 w = Math.min(Math.max(mw, w), mxw);
8534 w = Math.min(Math.max(mw, w), mxw);
8540 w = Math.min(Math.max(mw, w), mxw);
8546 h = Math.min(Math.max(mh, h), mxh);
8554 h = Math.min(Math.max(mh, h), mxh);
8564 h = Math.min(Math.max(mh, h), mxh);
8572 if (pos == 'hdrag') {
8575 this.proxy.setBounds(x, y, w, h);
8577 this.resizeElement();
8581 this.fireEvent("resizing", this, x, y, w, h, e);
8585 handleOver : function(){
8587 this.el.addClass("x-resizable-over");
8592 handleOut : function(){
8594 this.el.removeClass("x-resizable-over");
8599 * Returns the element this component is bound to.
8600 * @return {Roo.Element}
8607 * Returns the resizeChild element (or null).
8608 * @return {Roo.Element}
8610 getResizeChild : function(){
8611 return this.resizeChild;
8613 groupHandler : function()
8618 * Destroys this resizable. If the element was wrapped and
8619 * removeEl is not true then the element remains.
8620 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8622 destroy : function(removeEl){
8623 this.proxy.remove();
8625 this.overlay.removeAllListeners();
8626 this.overlay.remove();
8628 var ps = Roo.Resizable.positions;
8630 if(typeof ps[k] != "function" && this[ps[k]]){
8631 var h = this[ps[k]];
8632 h.el.removeAllListeners();
8644 // hash to map config positions to true positions
8645 Roo.Resizable.positions = {
8646 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8651 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8653 // only initialize the template if resizable is used
8654 var tpl = Roo.DomHelper.createTemplate(
8655 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8658 Roo.Resizable.Handle.prototype.tpl = tpl;
8660 this.position = pos;
8662 // show north drag fro topdra
8663 var handlepos = pos == 'hdrag' ? 'north' : pos;
8665 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8666 if (pos == 'hdrag') {
8667 this.el.setStyle('cursor', 'pointer');
8669 this.el.unselectable();
8671 this.el.setOpacity(0);
8673 this.el.on("mousedown", this.onMouseDown, this);
8674 if(!disableTrackOver){
8675 this.el.on("mouseover", this.onMouseOver, this);
8676 this.el.on("mouseout", this.onMouseOut, this);
8681 Roo.Resizable.Handle.prototype = {
8682 afterResize : function(rz){
8687 onMouseDown : function(e){
8688 this.rz.onMouseDown(this, e);
8691 onMouseOver : function(e){
8692 this.rz.handleOver(this, e);
8695 onMouseOut : function(e){
8696 this.rz.handleOut(this, e);
8700 * Ext JS Library 1.1.1
8701 * Copyright(c) 2006-2007, Ext JS, LLC.
8703 * Originally Released Under LGPL - original licence link has changed is not relivant.
8706 * <script type="text/javascript">
8711 * @extends Roo.Component
8712 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8714 * Create a new Editor
8715 * @param {Roo.form.Field} field The Field object (or descendant)
8716 * @param {Object} config The config object
8718 Roo.Editor = function(field, config){
8719 Roo.Editor.superclass.constructor.call(this, config);
8723 * @event beforestartedit
8724 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
8725 * false from the handler of this event.
8726 * @param {Editor} this
8727 * @param {Roo.Element} boundEl The underlying element bound to this editor
8728 * @param {Mixed} value The field value being set
8730 "beforestartedit" : true,
8733 * Fires when this editor is displayed
8734 * @param {Roo.Element} boundEl The underlying element bound to this editor
8735 * @param {Mixed} value The starting field value
8739 * @event beforecomplete
8740 * Fires after a change has been made to the field, but before the change is reflected in the underlying
8741 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
8742 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8743 * event will not fire since no edit actually occurred.
8744 * @param {Editor} this
8745 * @param {Mixed} value The current field value
8746 * @param {Mixed} startValue The original field value
8748 "beforecomplete" : true,
8751 * Fires after editing is complete and any changed value has been written to the underlying field.
8752 * @param {Editor} this
8753 * @param {Mixed} value The current field value
8754 * @param {Mixed} startValue The original field value
8759 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8760 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8761 * @param {Roo.form.Field} this
8762 * @param {Roo.EventObject} e The event object
8768 Roo.extend(Roo.Editor, Roo.Component, {
8770 * @cfg {Boolean/String} autosize
8771 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8772 * or "height" to adopt the height only (defaults to false)
8775 * @cfg {Boolean} revertInvalid
8776 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8777 * validation fails (defaults to true)
8780 * @cfg {Boolean} ignoreNoChange
8781 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8782 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
8783 * will never be ignored.
8786 * @cfg {Boolean} hideEl
8787 * False to keep the bound element visible while the editor is displayed (defaults to true)
8790 * @cfg {Mixed} value
8791 * The data value of the underlying field (defaults to "")
8795 * @cfg {String} alignment
8796 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8800 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8801 * for bottom-right shadow (defaults to "frame")
8805 * @cfg {Boolean} constrain True to constrain the editor to the viewport
8809 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8811 completeOnEnter : false,
8813 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8815 cancelOnEsc : false,
8817 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8822 onRender : function(ct, position){
8823 this.el = new Roo.Layer({
8824 shadow: this.shadow,
8830 constrain: this.constrain
8832 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8833 if(this.field.msgTarget != 'title'){
8834 this.field.msgTarget = 'qtip';
8836 this.field.render(this.el);
8838 this.field.el.dom.setAttribute('autocomplete', 'off');
8840 this.field.on("specialkey", this.onSpecialKey, this);
8841 if(this.swallowKeys){
8842 this.field.el.swallowEvent(['keydown','keypress']);
8845 this.field.on("blur", this.onBlur, this);
8846 if(this.field.grow){
8847 this.field.on("autosize", this.el.sync, this.el, {delay:1});
8851 onSpecialKey : function(field, e)
8853 //Roo.log('editor onSpecialKey');
8854 if(this.completeOnEnter && e.getKey() == e.ENTER){
8856 this.completeEdit();
8859 // do not fire special key otherwise it might hide close the editor...
8860 if(e.getKey() == e.ENTER){
8863 if(this.cancelOnEsc && e.getKey() == e.ESC){
8867 this.fireEvent('specialkey', field, e);
8872 * Starts the editing process and shows the editor.
8873 * @param {String/HTMLElement/Element} el The element to edit
8874 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8875 * to the innerHTML of el.
8877 startEdit : function(el, value){
8879 this.completeEdit();
8881 this.boundEl = Roo.get(el);
8882 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8884 this.render(this.parentEl || document.body);
8886 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8889 this.startValue = v;
8890 this.field.setValue(v);
8892 var sz = this.boundEl.getSize();
8893 switch(this.autoSize){
8895 this.setSize(sz.width, "");
8898 this.setSize("", sz.height);
8901 this.setSize(sz.width, sz.height);
8904 this.el.alignTo(this.boundEl, this.alignment);
8905 this.editing = true;
8907 Roo.QuickTips.disable();
8913 * Sets the height and width of this editor.
8914 * @param {Number} width The new width
8915 * @param {Number} height The new height
8917 setSize : function(w, h){
8918 this.field.setSize(w, h);
8925 * Realigns the editor to the bound field based on the current alignment config value.
8927 realign : function(){
8928 this.el.alignTo(this.boundEl, this.alignment);
8932 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8933 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8935 completeEdit : function(remainVisible){
8939 var v = this.getValue();
8940 if(this.revertInvalid !== false && !this.field.isValid()){
8941 v = this.startValue;
8942 this.cancelEdit(true);
8944 if(String(v) === String(this.startValue) && this.ignoreNoChange){
8945 this.editing = false;
8949 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8950 this.editing = false;
8951 if(this.updateEl && this.boundEl){
8952 this.boundEl.update(v);
8954 if(remainVisible !== true){
8957 this.fireEvent("complete", this, v, this.startValue);
8962 onShow : function(){
8964 if(this.hideEl !== false){
8965 this.boundEl.hide();
8968 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8969 this.fixIEFocus = true;
8970 this.deferredFocus.defer(50, this);
8974 this.fireEvent("startedit", this.boundEl, this.startValue);
8977 deferredFocus : function(){
8984 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
8985 * reverted to the original starting value.
8986 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8987 * cancel (defaults to false)
8989 cancelEdit : function(remainVisible){
8991 this.setValue(this.startValue);
8992 if(remainVisible !== true){
8999 onBlur : function(){
9000 if(this.allowBlur !== true && this.editing){
9001 this.completeEdit();
9006 onHide : function(){
9008 this.completeEdit();
9012 if(this.field.collapse){
9013 this.field.collapse();
9016 if(this.hideEl !== false){
9017 this.boundEl.show();
9020 Roo.QuickTips.enable();
9025 * Sets the data value of the editor
9026 * @param {Mixed} value Any valid value supported by the underlying field
9028 setValue : function(v){
9029 this.field.setValue(v);
9033 * Gets the data value of the editor
9034 * @return {Mixed} The data value
9036 getValue : function(){
9037 return this.field.getValue();
9041 * Ext JS Library 1.1.1
9042 * Copyright(c) 2006-2007, Ext JS, LLC.
9044 * Originally Released Under LGPL - original licence link has changed is not relivant.
9047 * <script type="text/javascript">
9051 * @class Roo.BasicDialog
9052 * @extends Roo.util.Observable
9053 * @parent none builder
9054 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9056 var dlg = new Roo.BasicDialog("my-dlg", {
9065 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9066 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9067 dlg.addButton('Cancel', dlg.hide, dlg);
9070 <b>A Dialog should always be a direct child of the body element.</b>
9071 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9072 * @cfg {String} title Default text to display in the title bar (defaults to null)
9073 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9074 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9075 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9076 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9077 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9078 * (defaults to null with no animation)
9079 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9080 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9081 * property for valid values (defaults to 'all')
9082 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9083 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9084 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9085 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9086 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9087 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9088 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9089 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9090 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9091 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9092 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9093 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9094 * draggable = true (defaults to false)
9095 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9096 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9097 * shadow (defaults to false)
9098 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9099 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9100 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9101 * @cfg {Array} buttons Array of buttons
9102 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9104 * Create a new BasicDialog.
9105 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9106 * @param {Object} config Configuration options
9108 Roo.BasicDialog = function(el, config){
9109 this.el = Roo.get(el);
9110 var dh = Roo.DomHelper;
9111 if(!this.el && config && config.autoCreate){
9112 if(typeof config.autoCreate == "object"){
9113 if(!config.autoCreate.id){
9114 config.autoCreate.id = el;
9116 this.el = dh.append(document.body,
9117 config.autoCreate, true);
9119 this.el = dh.append(document.body,
9120 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9124 el.setDisplayed(true);
9125 el.hide = this.hideAction;
9127 el.addClass("x-dlg");
9129 Roo.apply(this, config);
9131 this.proxy = el.createProxy("x-dlg-proxy");
9132 this.proxy.hide = this.hideAction;
9133 this.proxy.setOpacity(.5);
9137 el.setWidth(config.width);
9140 el.setHeight(config.height);
9142 this.size = el.getSize();
9143 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9144 this.xy = [config.x,config.y];
9146 this.xy = el.getCenterXY(true);
9148 /** The header element @type Roo.Element */
9149 this.header = el.child("> .x-dlg-hd");
9150 /** The body element @type Roo.Element */
9151 this.body = el.child("> .x-dlg-bd");
9152 /** The footer element @type Roo.Element */
9153 this.footer = el.child("> .x-dlg-ft");
9156 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9159 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9162 this.header.unselectable();
9164 this.header.update(this.title);
9166 // this element allows the dialog to be focused for keyboard event
9167 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9168 this.focusEl.swallowEvent("click", true);
9170 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9172 // wrap the body and footer for special rendering
9173 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9175 this.bwrap.dom.appendChild(this.footer.dom);
9178 this.bg = this.el.createChild({
9179 tag: "div", cls:"x-dlg-bg",
9180 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9182 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9185 if(this.autoScroll !== false && !this.autoTabs){
9186 this.body.setStyle("overflow", "auto");
9189 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9191 if(this.closable !== false){
9192 this.el.addClass("x-dlg-closable");
9193 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9194 this.close.on("click", this.closeClick, this);
9195 this.close.addClassOnOver("x-dlg-close-over");
9197 if(this.collapsible !== false){
9198 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9199 this.collapseBtn.on("click", this.collapseClick, this);
9200 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9201 this.header.on("dblclick", this.collapseClick, this);
9203 if(this.resizable !== false){
9204 this.el.addClass("x-dlg-resizable");
9205 this.resizer = new Roo.Resizable(el, {
9206 minWidth: this.minWidth || 80,
9207 minHeight:this.minHeight || 80,
9208 handles: this.resizeHandles || "all",
9211 this.resizer.on("beforeresize", this.beforeResize, this);
9212 this.resizer.on("resize", this.onResize, this);
9214 if(this.draggable !== false){
9215 el.addClass("x-dlg-draggable");
9216 if (!this.proxyDrag) {
9217 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9220 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9222 dd.setHandleElId(this.header.id);
9223 dd.endDrag = this.endMove.createDelegate(this);
9224 dd.startDrag = this.startMove.createDelegate(this);
9225 dd.onDrag = this.onDrag.createDelegate(this);
9230 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9231 this.mask.enableDisplayMode("block");
9233 this.el.addClass("x-dlg-modal");
9236 this.shadow = new Roo.Shadow({
9237 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9238 offset : this.shadowOffset
9241 this.shadowOffset = 0;
9243 if(Roo.useShims && this.shim !== false){
9244 this.shim = this.el.createShim();
9245 this.shim.hide = this.hideAction;
9254 var bts= this.buttons;
9256 Roo.each(bts, function(b) {
9265 * Fires when a key is pressed
9266 * @param {Roo.BasicDialog} this
9267 * @param {Roo.EventObject} e
9272 * Fires when this dialog is moved by the user.
9273 * @param {Roo.BasicDialog} this
9274 * @param {Number} x The new page X
9275 * @param {Number} y The new page Y
9280 * Fires when this dialog is resized by the user.
9281 * @param {Roo.BasicDialog} this
9282 * @param {Number} width The new width
9283 * @param {Number} height The new height
9288 * Fires before this dialog is hidden.
9289 * @param {Roo.BasicDialog} this
9291 "beforehide" : true,
9294 * Fires when this dialog is hidden.
9295 * @param {Roo.BasicDialog} this
9300 * Fires before this dialog is shown.
9301 * @param {Roo.BasicDialog} this
9303 "beforeshow" : true,
9306 * Fires when this dialog is shown.
9307 * @param {Roo.BasicDialog} this
9311 el.on("keydown", this.onKeyDown, this);
9312 el.on("mousedown", this.toFront, this);
9313 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9315 Roo.DialogManager.register(this);
9316 Roo.BasicDialog.superclass.constructor.call(this);
9319 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9320 shadowOffset: Roo.isIE ? 6 : 5,
9324 defaultButton: null,
9325 buttonAlign: "right",
9330 * Sets the dialog title text
9331 * @param {String} text The title text to display
9332 * @return {Roo.BasicDialog} this
9334 setTitle : function(text){
9335 this.header.update(text);
9340 closeClick : function(){
9345 collapseClick : function(){
9346 this[this.collapsed ? "expand" : "collapse"]();
9350 * Collapses the dialog to its minimized state (only the title bar is visible).
9351 * Equivalent to the user clicking the collapse dialog button.
9353 collapse : function(){
9354 if(!this.collapsed){
9355 this.collapsed = true;
9356 this.el.addClass("x-dlg-collapsed");
9357 this.restoreHeight = this.el.getHeight();
9358 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9363 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9364 * clicking the expand dialog button.
9366 expand : function(){
9368 this.collapsed = false;
9369 this.el.removeClass("x-dlg-collapsed");
9370 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9375 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9376 * @return {Roo.TabPanel} The tabs component
9378 initTabs : function(){
9379 var tabs = this.getTabs();
9380 while(tabs.getTab(0)){
9383 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9385 tabs.addTab(Roo.id(dom), dom.title);
9393 beforeResize : function(){
9394 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9398 onResize : function(){
9400 this.syncBodyHeight();
9401 this.adjustAssets();
9403 this.fireEvent("resize", this, this.size.width, this.size.height);
9407 onKeyDown : function(e){
9408 if(this.isVisible()){
9409 this.fireEvent("keydown", this, e);
9414 * Resizes the dialog.
9415 * @param {Number} width
9416 * @param {Number} height
9417 * @return {Roo.BasicDialog} this
9419 resizeTo : function(width, height){
9420 this.el.setSize(width, height);
9421 this.size = {width: width, height: height};
9422 this.syncBodyHeight();
9423 if(this.fixedcenter){
9426 if(this.isVisible()){
9428 this.adjustAssets();
9430 this.fireEvent("resize", this, width, height);
9436 * Resizes the dialog to fit the specified content size.
9437 * @param {Number} width
9438 * @param {Number} height
9439 * @return {Roo.BasicDialog} this
9441 setContentSize : function(w, h){
9442 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9443 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9444 //if(!this.el.isBorderBox()){
9445 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9446 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9449 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9450 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9452 this.resizeTo(w, h);
9457 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9458 * executed in response to a particular key being pressed while the dialog is active.
9459 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9460 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9461 * @param {Function} fn The function to call
9462 * @param {Object} scope (optional) The scope of the function
9463 * @return {Roo.BasicDialog} this
9465 addKeyListener : function(key, fn, scope){
9466 var keyCode, shift, ctrl, alt;
9467 if(typeof key == "object" && !(key instanceof Array)){
9468 keyCode = key["key"];
9469 shift = key["shift"];
9475 var handler = function(dlg, e){
9476 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9478 if(keyCode instanceof Array){
9479 for(var i = 0, len = keyCode.length; i < len; i++){
9480 if(keyCode[i] == k){
9481 fn.call(scope || window, dlg, k, e);
9487 fn.call(scope || window, dlg, k, e);
9492 this.on("keydown", handler);
9497 * Returns the TabPanel component (creates it if it doesn't exist).
9498 * Note: If you wish to simply check for the existence of tabs without creating them,
9499 * check for a null 'tabs' property.
9500 * @return {Roo.TabPanel} The tabs component
9502 getTabs : function(){
9504 this.el.addClass("x-dlg-auto-tabs");
9505 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9506 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9512 * Adds a button to the footer section of the dialog.
9513 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9514 * object or a valid Roo.DomHelper element config
9515 * @param {Function} handler The function called when the button is clicked
9516 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9517 * @return {Roo.Button} The new button
9519 addButton : function(config, handler, scope){
9520 var dh = Roo.DomHelper;
9522 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9524 if(!this.btnContainer){
9525 var tb = this.footer.createChild({
9527 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9528 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9530 this.btnContainer = tb.firstChild.firstChild.firstChild;
9535 minWidth: this.minButtonWidth,
9538 if(typeof config == "string"){
9539 bconfig.text = config;
9542 bconfig.dhconfig = config;
9544 Roo.apply(bconfig, config);
9548 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9549 bconfig.position = Math.max(0, bconfig.position);
9550 fc = this.btnContainer.childNodes[bconfig.position];
9553 var btn = new Roo.Button(
9555 this.btnContainer.insertBefore(document.createElement("td"),fc)
9556 : this.btnContainer.appendChild(document.createElement("td")),
9557 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9560 this.syncBodyHeight();
9563 * Array of all the buttons that have been added to this dialog via addButton
9568 this.buttons.push(btn);
9573 * Sets the default button to be focused when the dialog is displayed.
9574 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9575 * @return {Roo.BasicDialog} this
9577 setDefaultButton : function(btn){
9578 this.defaultButton = btn;
9583 getHeaderFooterHeight : function(safe){
9586 height += this.header.getHeight();
9589 var fm = this.footer.getMargins();
9590 height += (this.footer.getHeight()+fm.top+fm.bottom);
9592 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9593 height += this.centerBg.getPadding("tb");
9598 syncBodyHeight : function()
9600 var bd = this.body, // the text
9601 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9603 var height = this.size.height - this.getHeaderFooterHeight(false);
9604 bd.setHeight(height-bd.getMargins("tb"));
9605 var hh = this.header.getHeight();
9606 var h = this.size.height-hh;
9609 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9610 bw.setHeight(h-cb.getPadding("tb"));
9612 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9613 bd.setWidth(bw.getWidth(true));
9615 this.tabs.syncHeight();
9617 this.tabs.el.repaint();
9623 * Restores the previous state of the dialog if Roo.state is configured.
9624 * @return {Roo.BasicDialog} this
9626 restoreState : function(){
9627 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9628 if(box && box.width){
9629 this.xy = [box.x, box.y];
9630 this.resizeTo(box.width, box.height);
9636 beforeShow : function(){
9638 if(this.fixedcenter){
9639 this.xy = this.el.getCenterXY(true);
9642 Roo.get(document.body).addClass("x-body-masked");
9643 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9650 animShow : function(){
9651 var b = Roo.get(this.animateTarget).getBox();
9652 this.proxy.setSize(b.width, b.height);
9653 this.proxy.setLocation(b.x, b.y);
9655 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9656 true, .35, this.showEl.createDelegate(this));
9661 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9662 * @return {Roo.BasicDialog} this
9664 show : function(animateTarget){
9665 if (this.fireEvent("beforeshow", this) === false){
9668 if(this.syncHeightBeforeShow){
9669 this.syncBodyHeight();
9670 }else if(this.firstShow){
9671 this.firstShow = false;
9672 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9674 this.animateTarget = animateTarget || this.animateTarget;
9675 if(!this.el.isVisible()){
9677 if(this.animateTarget && Roo.get(this.animateTarget)){
9687 showEl : function(){
9689 this.el.setXY(this.xy);
9691 this.adjustAssets(true);
9694 // IE peekaboo bug - fix found by Dave Fenwick
9698 this.fireEvent("show", this);
9702 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
9703 * dialog itself will receive focus.
9706 if(this.defaultButton){
9707 this.defaultButton.focus();
9709 this.focusEl.focus();
9714 constrainXY : function(){
9715 if(this.constraintoviewport !== false){
9718 var s = this.container.getSize();
9719 this.viewSize = [s.width, s.height];
9721 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9724 var s = Roo.get(this.container||document).getScroll();
9726 var x = this.xy[0], y = this.xy[1];
9727 var w = this.size.width, h = this.size.height;
9728 var vw = this.viewSize[0], vh = this.viewSize[1];
9729 // only move it if it needs it
9731 // first validate right/bottom
9732 if(x + w > vw+s.left){
9736 if(y + h > vh+s.top){
9740 // then make sure top/left isn't negative
9752 if(this.isVisible()){
9753 this.el.setLocation(x, y);
9754 this.adjustAssets();
9761 onDrag : function(){
9762 if(!this.proxyDrag){
9763 this.xy = this.el.getXY();
9764 this.adjustAssets();
9769 adjustAssets : function(doShow){
9770 var x = this.xy[0], y = this.xy[1];
9771 var w = this.size.width, h = this.size.height;
9772 if(doShow === true){
9774 this.shadow.show(this.el);
9780 if(this.shadow && this.shadow.isVisible()){
9781 this.shadow.show(this.el);
9783 if(this.shim && this.shim.isVisible()){
9784 this.shim.setBounds(x, y, w, h);
9789 adjustViewport : function(w, h){
9791 w = Roo.lib.Dom.getViewWidth();
9792 h = Roo.lib.Dom.getViewHeight();
9795 this.viewSize = [w, h];
9796 if(this.modal && this.mask.isVisible()){
9797 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9798 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9800 if(this.isVisible()){
9806 * Destroys this dialog and all its supporting elements (including any tabs, shim,
9807 * shadow, proxy, mask, etc.) Also removes all event listeners.
9808 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9810 destroy : function(removeEl){
9811 if(this.isVisible()){
9812 this.animateTarget = null;
9815 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9817 this.tabs.destroy(removeEl);
9830 for(var i = 0, len = this.buttons.length; i < len; i++){
9831 this.buttons[i].destroy();
9834 this.el.removeAllListeners();
9835 if(removeEl === true){
9839 Roo.DialogManager.unregister(this);
9843 startMove : function(){
9847 if(this.constraintoviewport !== false){
9848 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9853 endMove : function(){
9854 if(!this.proxyDrag){
9855 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9857 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9861 this.adjustAssets();
9863 this.fireEvent("move", this, this.xy[0], this.xy[1]);
9867 * Brings this dialog to the front of any other visible dialogs
9868 * @return {Roo.BasicDialog} this
9870 toFront : function(){
9871 Roo.DialogManager.bringToFront(this);
9876 * Sends this dialog to the back (under) of any other visible dialogs
9877 * @return {Roo.BasicDialog} this
9879 toBack : function(){
9880 Roo.DialogManager.sendToBack(this);
9885 * Centers this dialog in the viewport
9886 * @return {Roo.BasicDialog} this
9888 center : function(){
9889 var xy = this.el.getCenterXY(true);
9890 this.moveTo(xy[0], xy[1]);
9895 * Moves the dialog's top-left corner to the specified point
9898 * @return {Roo.BasicDialog} this
9900 moveTo : function(x, y){
9902 if(this.isVisible()){
9903 this.el.setXY(this.xy);
9904 this.adjustAssets();
9910 * Aligns the dialog to the specified element
9911 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9912 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9913 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9914 * @return {Roo.BasicDialog} this
9916 alignTo : function(element, position, offsets){
9917 this.xy = this.el.getAlignToXY(element, position, offsets);
9918 if(this.isVisible()){
9919 this.el.setXY(this.xy);
9920 this.adjustAssets();
9926 * Anchors an element to another element and realigns it when the window is resized.
9927 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9928 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9929 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9930 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9931 * is a number, it is used as the buffer delay (defaults to 50ms).
9932 * @return {Roo.BasicDialog} this
9934 anchorTo : function(el, alignment, offsets, monitorScroll){
9935 var action = function(){
9936 this.alignTo(el, alignment, offsets);
9938 Roo.EventManager.onWindowResize(action, this);
9939 var tm = typeof monitorScroll;
9940 if(tm != 'undefined'){
9941 Roo.EventManager.on(window, 'scroll', action, this,
9942 {buffer: tm == 'number' ? monitorScroll : 50});
9949 * Returns true if the dialog is visible
9952 isVisible : function(){
9953 return this.el.isVisible();
9957 animHide : function(callback){
9958 var b = Roo.get(this.animateTarget).getBox();
9960 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9962 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9963 this.hideEl.createDelegate(this, [callback]));
9968 * @param {Function} callback (optional) Function to call when the dialog is hidden
9969 * @return {Roo.BasicDialog} this
9971 hide : function(callback){
9972 if (this.fireEvent("beforehide", this) === false){
9981 // sometimes animateTarget seems to get set.. causing problems...
9982 // this just double checks..
9983 if(this.animateTarget && Roo.get(this.animateTarget)) {
9984 this.animHide(callback);
9987 this.hideEl(callback);
9993 hideEl : function(callback){
9997 Roo.get(document.body).removeClass("x-body-masked");
9999 this.fireEvent("hide", this);
10000 if(typeof callback == "function"){
10006 hideAction : function(){
10007 this.setLeft("-10000px");
10008 this.setTop("-10000px");
10009 this.setStyle("visibility", "hidden");
10013 refreshSize : function(){
10014 this.size = this.el.getSize();
10015 this.xy = this.el.getXY();
10016 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10020 // z-index is managed by the DialogManager and may be overwritten at any time
10021 setZIndex : function(index){
10023 this.mask.setStyle("z-index", index);
10026 this.shim.setStyle("z-index", ++index);
10029 this.shadow.setZIndex(++index);
10031 this.el.setStyle("z-index", ++index);
10033 this.proxy.setStyle("z-index", ++index);
10036 this.resizer.proxy.setStyle("z-index", ++index);
10039 this.lastZIndex = index;
10043 * Returns the element for this dialog
10044 * @return {Roo.Element} The underlying dialog Element
10046 getEl : function(){
10052 * @class Roo.DialogManager
10053 * Provides global access to BasicDialogs that have been created and
10054 * support for z-indexing (layering) multiple open dialogs.
10056 Roo.DialogManager = function(){
10058 var accessList = [];
10062 var sortDialogs = function(d1, d2){
10063 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10067 var orderDialogs = function(){
10068 accessList.sort(sortDialogs);
10069 var seed = Roo.DialogManager.zseed;
10070 for(var i = 0, len = accessList.length; i < len; i++){
10071 var dlg = accessList[i];
10073 dlg.setZIndex(seed + (i*10));
10080 * The starting z-index for BasicDialogs (defaults to 9000)
10081 * @type Number The z-index value
10086 register : function(dlg){
10087 list[dlg.id] = dlg;
10088 accessList.push(dlg);
10092 unregister : function(dlg){
10093 delete list[dlg.id];
10096 if(!accessList.indexOf){
10097 for( i = 0, len = accessList.length; i < len; i++){
10098 if(accessList[i] == dlg){
10099 accessList.splice(i, 1);
10104 i = accessList.indexOf(dlg);
10106 accessList.splice(i, 1);
10112 * Gets a registered dialog by id
10113 * @param {String/Object} id The id of the dialog or a dialog
10114 * @return {Roo.BasicDialog} this
10116 get : function(id){
10117 return typeof id == "object" ? id : list[id];
10121 * Brings the specified dialog to the front
10122 * @param {String/Object} dlg The id of the dialog or a dialog
10123 * @return {Roo.BasicDialog} this
10125 bringToFront : function(dlg){
10126 dlg = this.get(dlg);
10129 dlg._lastAccess = new Date().getTime();
10136 * Sends the specified dialog to the back
10137 * @param {String/Object} dlg The id of the dialog or a dialog
10138 * @return {Roo.BasicDialog} this
10140 sendToBack : function(dlg){
10141 dlg = this.get(dlg);
10142 dlg._lastAccess = -(new Date().getTime());
10148 * Hides all dialogs
10150 hideAll : function(){
10151 for(var id in list){
10152 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10161 * @class Roo.LayoutDialog
10162 * @extends Roo.BasicDialog
10163 * @children Roo.ContentPanel
10164 * @parent builder none
10165 * Dialog which provides adjustments for working with a layout in a Dialog.
10166 * Add your necessary layout config options to the dialog's config.<br>
10167 * Example usage (including a nested layout):
10170 dialog = new Roo.LayoutDialog("download-dlg", {
10179 // layout config merges with the dialog config
10181 tabPosition: "top",
10182 alwaysShowTabs: true
10185 dialog.addKeyListener(27, dialog.hide, dialog);
10186 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10187 dialog.addButton("Build It!", this.getDownload, this);
10189 // we can even add nested layouts
10190 var innerLayout = new Roo.BorderLayout("dl-inner", {
10200 innerLayout.beginUpdate();
10201 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10202 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10203 innerLayout.endUpdate(true);
10205 var layout = dialog.getLayout();
10206 layout.beginUpdate();
10207 layout.add("center", new Roo.ContentPanel("standard-panel",
10208 {title: "Download the Source", fitToFrame:true}));
10209 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10210 {title: "Build your own roo.js"}));
10211 layout.getRegion("center").showPanel(sp);
10212 layout.endUpdate();
10216 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10217 * @param {Object} config configuration options
10219 Roo.LayoutDialog = function(el, cfg){
10222 if (typeof(cfg) == 'undefined') {
10223 config = Roo.apply({}, el);
10224 // not sure why we use documentElement here.. - it should always be body.
10225 // IE7 borks horribly if we use documentElement.
10226 // webkit also does not like documentElement - it creates a body element...
10227 el = Roo.get( document.body || document.documentElement ).createChild();
10228 //config.autoCreate = true;
10232 config.autoTabs = false;
10233 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10234 this.body.setStyle({overflow:"hidden", position:"relative"});
10235 this.layout = new Roo.BorderLayout(this.body.dom, config);
10236 this.layout.monitorWindowResize = false;
10237 this.el.addClass("x-dlg-auto-layout");
10238 // fix case when center region overwrites center function
10239 this.center = Roo.BasicDialog.prototype.center;
10240 this.on("show", this.layout.layout, this.layout, true);
10241 if (config.items) {
10242 var xitems = config.items;
10243 delete config.items;
10244 Roo.each(xitems, this.addxtype, this);
10249 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10253 * @cfg {Roo.LayoutRegion} east
10256 * @cfg {Roo.LayoutRegion} west
10259 * @cfg {Roo.LayoutRegion} south
10262 * @cfg {Roo.LayoutRegion} north
10265 * @cfg {Roo.LayoutRegion} center
10268 * @cfg {Roo.Button} buttons[] Bottom buttons..
10273 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10276 endUpdate : function(){
10277 this.layout.endUpdate();
10281 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10284 beginUpdate : function(){
10285 this.layout.beginUpdate();
10289 * Get the BorderLayout for this dialog
10290 * @return {Roo.BorderLayout}
10292 getLayout : function(){
10293 return this.layout;
10296 showEl : function(){
10297 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10299 this.layout.layout();
10304 // Use the syncHeightBeforeShow config option to control this automatically
10305 syncBodyHeight : function(){
10306 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10307 if(this.layout){this.layout.layout();}
10311 * Add an xtype element (actually adds to the layout.)
10312 * @return {Object} xdata xtype object data.
10315 addxtype : function(c) {
10316 return this.layout.addxtype(c);
10320 * Ext JS Library 1.1.1
10321 * Copyright(c) 2006-2007, Ext JS, LLC.
10323 * Originally Released Under LGPL - original licence link has changed is not relivant.
10326 * <script type="text/javascript">
10330 * @class Roo.MessageBox
10332 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10336 Roo.Msg.alert('Status', 'Changes saved successfully.');
10338 // Prompt for user data:
10339 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10341 // process text value...
10345 // Show a dialog using config options:
10347 title:'Save Changes?',
10348 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10349 buttons: Roo.Msg.YESNOCANCEL,
10356 Roo.MessageBox = function(){
10357 var dlg, opt, mask, waitTimer;
10358 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10359 var buttons, activeTextEl, bwidth;
10362 var handleButton = function(button){
10364 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10368 var handleHide = function(){
10369 if(opt && opt.cls){
10370 dlg.el.removeClass(opt.cls);
10373 Roo.TaskMgr.stop(waitTimer);
10379 var updateButtons = function(b){
10382 buttons["ok"].hide();
10383 buttons["cancel"].hide();
10384 buttons["yes"].hide();
10385 buttons["no"].hide();
10386 dlg.footer.dom.style.display = 'none';
10389 dlg.footer.dom.style.display = '';
10390 for(var k in buttons){
10391 if(typeof buttons[k] != "function"){
10394 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10395 width += buttons[k].el.getWidth()+15;
10405 var handleEsc = function(d, k, e){
10406 if(opt && opt.closable !== false){
10416 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10417 * @return {Roo.BasicDialog} The BasicDialog element
10419 getDialog : function(){
10421 dlg = new Roo.BasicDialog("x-msg-box", {
10426 constraintoviewport:false,
10428 collapsible : false,
10431 width:400, height:100,
10432 buttonAlign:"center",
10433 closeClick : function(){
10434 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10435 handleButton("no");
10437 handleButton("cancel");
10442 dlg.on("hide", handleHide);
10444 dlg.addKeyListener(27, handleEsc);
10446 var bt = this.buttonText;
10447 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10448 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10449 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10450 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10451 bodyEl = dlg.body.createChild({
10453 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>'
10455 msgEl = bodyEl.dom.firstChild;
10456 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10457 textboxEl.enableDisplayMode();
10458 textboxEl.addKeyListener([10,13], function(){
10459 if(dlg.isVisible() && opt && opt.buttons){
10460 if(opt.buttons.ok){
10461 handleButton("ok");
10462 }else if(opt.buttons.yes){
10463 handleButton("yes");
10467 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10468 textareaEl.enableDisplayMode();
10469 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10470 progressEl.enableDisplayMode();
10471 var pf = progressEl.dom.firstChild;
10473 pp = Roo.get(pf.firstChild);
10474 pp.setHeight(pf.offsetHeight);
10482 * Updates the message box body text
10483 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10484 * the XHTML-compliant non-breaking space character '&#160;')
10485 * @return {Roo.MessageBox} This message box
10487 updateText : function(text){
10488 if(!dlg.isVisible() && !opt.width){
10489 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10491 msgEl.innerHTML = text || ' ';
10493 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10494 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10496 Math.min(opt.width || cw , this.maxWidth),
10497 Math.max(opt.minWidth || this.minWidth, bwidth)
10500 activeTextEl.setWidth(w);
10502 if(dlg.isVisible()){
10503 dlg.fixedcenter = false;
10505 // to big, make it scroll. = But as usual stupid IE does not support
10508 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10509 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10510 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10512 bodyEl.dom.style.height = '';
10513 bodyEl.dom.style.overflowY = '';
10516 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10518 bodyEl.dom.style.overflowX = '';
10521 dlg.setContentSize(w, bodyEl.getHeight());
10522 if(dlg.isVisible()){
10523 dlg.fixedcenter = true;
10529 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10530 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10531 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10532 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10533 * @return {Roo.MessageBox} This message box
10535 updateProgress : function(value, text){
10537 this.updateText(text);
10539 if (pp) { // weird bug on my firefox - for some reason this is not defined
10540 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10546 * Returns true if the message box is currently displayed
10547 * @return {Boolean} True if the message box is visible, else false
10549 isVisible : function(){
10550 return dlg && dlg.isVisible();
10554 * Hides the message box if it is displayed
10557 if(this.isVisible()){
10563 * Displays a new message box, or reinitializes an existing message box, based on the config options
10564 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10565 * The following config object properties are supported:
10567 Property Type Description
10568 ---------- --------------- ------------------------------------------------------------------------------------
10569 animEl String/Element An id or Element from which the message box should animate as it opens and
10570 closes (defaults to undefined)
10571 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10572 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10573 closable Boolean False to hide the top-right close button (defaults to true). Note that
10574 progress and wait dialogs will ignore this property and always hide the
10575 close button as they can only be closed programmatically.
10576 cls String A custom CSS class to apply to the message box element
10577 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10578 displayed (defaults to 75)
10579 fn Function A callback function to execute after closing the dialog. The arguments to the
10580 function will be btn (the name of the button that was clicked, if applicable,
10581 e.g. "ok"), and text (the value of the active text field, if applicable).
10582 Progress and wait dialogs will ignore this option since they do not respond to
10583 user actions and can only be closed programmatically, so any required function
10584 should be called by the same code after it closes the dialog.
10585 icon String A CSS class that provides a background image to be used as an icon for
10586 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10587 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10588 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10589 modal Boolean False to allow user interaction with the page while the message box is
10590 displayed (defaults to true)
10591 msg String A string that will replace the existing message box body text (defaults
10592 to the XHTML-compliant non-breaking space character ' ')
10593 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10594 progress Boolean True to display a progress bar (defaults to false)
10595 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10596 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10597 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10598 title String The title text
10599 value String The string value to set into the active textbox element if displayed
10600 wait Boolean True to display a progress bar (defaults to false)
10601 width Number The width of the dialog in pixels
10608 msg: 'Please enter your address:',
10610 buttons: Roo.MessageBox.OKCANCEL,
10613 animEl: 'addAddressBtn'
10616 * @param {Object} config Configuration options
10617 * @return {Roo.MessageBox} This message box
10619 show : function(options)
10622 // this causes nightmares if you show one dialog after another
10623 // especially on callbacks..
10625 if(this.isVisible()){
10628 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10629 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10630 Roo.log("New Dialog Message:" + options.msg )
10631 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10632 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10635 var d = this.getDialog();
10637 d.setTitle(opt.title || " ");
10638 d.close.setDisplayed(opt.closable !== false);
10639 activeTextEl = textboxEl;
10640 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10645 textareaEl.setHeight(typeof opt.multiline == "number" ?
10646 opt.multiline : this.defaultTextHeight);
10647 activeTextEl = textareaEl;
10656 progressEl.setDisplayed(opt.progress === true);
10657 this.updateProgress(0);
10658 activeTextEl.dom.value = opt.value || "";
10660 dlg.setDefaultButton(activeTextEl);
10662 var bs = opt.buttons;
10665 db = buttons["ok"];
10666 }else if(bs && bs.yes){
10667 db = buttons["yes"];
10669 dlg.setDefaultButton(db);
10671 bwidth = updateButtons(opt.buttons);
10672 this.updateText(opt.msg);
10674 d.el.addClass(opt.cls);
10676 d.proxyDrag = opt.proxyDrag === true;
10677 d.modal = opt.modal !== false;
10678 d.mask = opt.modal !== false ? mask : false;
10679 if(!d.isVisible()){
10680 // force it to the end of the z-index stack so it gets a cursor in FF
10681 document.body.appendChild(dlg.el.dom);
10682 d.animateTarget = null;
10683 d.show(options.animEl);
10690 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
10691 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10692 * and closing the message box when the process is complete.
10693 * @param {String} title The title bar text
10694 * @param {String} msg The message box body text
10695 * @return {Roo.MessageBox} This message box
10697 progress : function(title, msg){
10704 minWidth: this.minProgressWidth,
10711 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10712 * If a callback function is passed it will be called after the user clicks the button, and the
10713 * id of the button that was clicked will be passed as the only parameter to the callback
10714 * (could also be the top-right close button).
10715 * @param {String} title The title bar text
10716 * @param {String} msg The message box body text
10717 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10718 * @param {Object} scope (optional) The scope of the callback function
10719 * @return {Roo.MessageBox} This message box
10721 alert : function(title, msg, fn, scope){
10734 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
10735 * interaction while waiting for a long-running process to complete that does not have defined intervals.
10736 * You are responsible for closing the message box when the process is complete.
10737 * @param {String} msg The message box body text
10738 * @param {String} title (optional) The title bar text
10739 * @return {Roo.MessageBox} This message box
10741 wait : function(msg, title){
10752 waitTimer = Roo.TaskMgr.start({
10754 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10762 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10763 * If a callback function is passed it will be called after the user clicks either button, and the id of the
10764 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10765 * @param {String} title The title bar text
10766 * @param {String} msg The message box body text
10767 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10768 * @param {Object} scope (optional) The scope of the callback function
10769 * @return {Roo.MessageBox} This message box
10771 confirm : function(title, msg, fn, scope){
10775 buttons: this.YESNO,
10784 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10785 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
10786 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10787 * (could also be the top-right close button) and the text that was entered will be passed as the two
10788 * parameters to the callback.
10789 * @param {String} title The title bar text
10790 * @param {String} msg The message box body text
10791 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10792 * @param {Object} scope (optional) The scope of the callback function
10793 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10794 * property, or the height in pixels to create the textbox (defaults to false / single-line)
10795 * @return {Roo.MessageBox} This message box
10797 prompt : function(title, msg, fn, scope, multiline){
10801 buttons: this.OKCANCEL,
10806 multiline: multiline,
10813 * Button config that displays a single OK button
10818 * Button config that displays Yes and No buttons
10821 YESNO : {yes:true, no:true},
10823 * Button config that displays OK and Cancel buttons
10826 OKCANCEL : {ok:true, cancel:true},
10828 * Button config that displays Yes, No and Cancel buttons
10831 YESNOCANCEL : {yes:true, no:true, cancel:true},
10834 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10837 defaultTextHeight : 75,
10839 * The maximum width in pixels of the message box (defaults to 600)
10844 * The minimum width in pixels of the message box (defaults to 100)
10849 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
10850 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10853 minProgressWidth : 250,
10855 * An object containing the default button text strings that can be overriden for localized language support.
10856 * Supported properties are: ok, cancel, yes and no.
10857 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10870 * Shorthand for {@link Roo.MessageBox}
10872 Roo.Msg = Roo.MessageBox;/*
10874 * Ext JS Library 1.1.1
10875 * Copyright(c) 2006-2007, Ext JS, LLC.
10877 * Originally Released Under LGPL - original licence link has changed is not relivant.
10880 * <script type="text/javascript">
10883 * @class Roo.QuickTips
10884 * Provides attractive and customizable tooltips for any element.
10887 Roo.QuickTips = function(){
10888 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10889 var ce, bd, xy, dd;
10890 var visible = false, disabled = true, inited = false;
10891 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10893 var onOver = function(e){
10897 var t = e.getTarget();
10898 if(!t || t.nodeType !== 1 || t == document || t == document.body){
10901 if(ce && t == ce.el){
10902 clearTimeout(hideProc);
10905 if(t && tagEls[t.id]){
10906 tagEls[t.id].el = t;
10907 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10910 var ttp, et = Roo.fly(t);
10911 var ns = cfg.namespace;
10912 if(tm.interceptTitles && t.title){
10915 t.removeAttribute("title");
10916 e.preventDefault();
10918 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10921 showProc = show.defer(tm.showDelay, tm, [{
10923 text: ttp.replace(/\\n/g,'<br/>'),
10924 width: et.getAttributeNS(ns, cfg.width),
10925 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10926 title: et.getAttributeNS(ns, cfg.title),
10927 cls: et.getAttributeNS(ns, cfg.cls)
10932 var onOut = function(e){
10933 clearTimeout(showProc);
10934 var t = e.getTarget();
10935 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10936 hideProc = setTimeout(hide, tm.hideDelay);
10940 var onMove = function(e){
10946 if(tm.trackMouse && ce){
10951 var onDown = function(e){
10952 clearTimeout(showProc);
10953 clearTimeout(hideProc);
10955 if(tm.hideOnClick){
10958 tm.enable.defer(100, tm);
10963 var getPad = function(){
10964 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10967 var show = function(o){
10971 clearTimeout(dismissProc);
10973 if(removeCls){ // in case manually hidden
10974 el.removeClass(removeCls);
10978 el.addClass(ce.cls);
10979 removeCls = ce.cls;
10982 tipTitle.update(ce.title);
10985 tipTitle.update('');
10988 el.dom.style.width = tm.maxWidth+'px';
10989 //tipBody.dom.style.width = '';
10990 tipBodyText.update(o.text);
10991 var p = getPad(), w = ce.width;
10993 var td = tipBodyText.dom;
10994 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10995 if(aw > tm.maxWidth){
10997 }else if(aw < tm.minWidth){
11003 //tipBody.setWidth(w);
11004 el.setWidth(parseInt(w, 10) + p);
11005 if(ce.autoHide === false){
11006 close.setDisplayed(true);
11011 close.setDisplayed(false);
11017 el.avoidY = xy[1]-18;
11022 el.setStyle("visibility", "visible");
11023 el.fadeIn({callback: afterShow});
11029 var afterShow = function(){
11033 if(tm.autoDismiss && ce.autoHide !== false){
11034 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11039 var hide = function(noanim){
11040 clearTimeout(dismissProc);
11041 clearTimeout(hideProc);
11043 if(el.isVisible()){
11045 if(noanim !== true && tm.animate){
11046 el.fadeOut({callback: afterHide});
11053 var afterHide = function(){
11056 el.removeClass(removeCls);
11063 * @cfg {Number} minWidth
11064 * The minimum width of the quick tip (defaults to 40)
11068 * @cfg {Number} maxWidth
11069 * The maximum width of the quick tip (defaults to 300)
11073 * @cfg {Boolean} interceptTitles
11074 * True to automatically use the element's DOM title value if available (defaults to false)
11076 interceptTitles : false,
11078 * @cfg {Boolean} trackMouse
11079 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11081 trackMouse : false,
11083 * @cfg {Boolean} hideOnClick
11084 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11086 hideOnClick : true,
11088 * @cfg {Number} showDelay
11089 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11093 * @cfg {Number} hideDelay
11094 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11098 * @cfg {Boolean} autoHide
11099 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11100 * Used in conjunction with hideDelay.
11105 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11106 * (defaults to true). Used in conjunction with autoDismissDelay.
11108 autoDismiss : true,
11111 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11113 autoDismissDelay : 5000,
11115 * @cfg {Boolean} animate
11116 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11121 * @cfg {String} title
11122 * Title text to display (defaults to ''). This can be any valid HTML markup.
11126 * @cfg {String} text
11127 * Body text to display (defaults to ''). This can be any valid HTML markup.
11131 * @cfg {String} cls
11132 * A CSS class to apply to the base quick tip element (defaults to '').
11136 * @cfg {Number} width
11137 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11138 * minWidth or maxWidth.
11143 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11144 * or display QuickTips in a page.
11147 tm = Roo.QuickTips;
11148 cfg = tm.tagConfig;
11150 if(!Roo.isReady){ // allow calling of init() before onReady
11151 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11154 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11155 el.fxDefaults = {stopFx: true};
11156 // maximum custom styling
11157 //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>');
11158 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>');
11159 tipTitle = el.child('h3');
11160 tipTitle.enableDisplayMode("block");
11161 tipBody = el.child('div.x-tip-bd');
11162 tipBodyText = el.child('div.x-tip-bd-inner');
11163 //bdLeft = el.child('div.x-tip-bd-left');
11164 //bdRight = el.child('div.x-tip-bd-right');
11165 close = el.child('div.x-tip-close');
11166 close.enableDisplayMode("block");
11167 close.on("click", hide);
11168 var d = Roo.get(document);
11169 d.on("mousedown", onDown);
11170 d.on("mouseover", onOver);
11171 d.on("mouseout", onOut);
11172 d.on("mousemove", onMove);
11173 esc = d.addKeyListener(27, hide);
11176 dd = el.initDD("default", null, {
11177 onDrag : function(){
11181 dd.setHandleElId(tipTitle.id);
11190 * Configures a new quick tip instance and assigns it to a target element. The following config options
11193 Property Type Description
11194 ---------- --------------------- ------------------------------------------------------------------------
11195 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11197 * @param {Object} config The config object
11199 register : function(config){
11200 var cs = config instanceof Array ? config : arguments;
11201 for(var i = 0, len = cs.length; i < len; i++) {
11203 var target = c.target;
11205 if(target instanceof Array){
11206 for(var j = 0, jlen = target.length; j < jlen; j++){
11207 tagEls[target[j]] = c;
11210 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11217 * Removes this quick tip from its element and destroys it.
11218 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11220 unregister : function(el){
11221 delete tagEls[Roo.id(el)];
11225 * Enable this quick tip.
11227 enable : function(){
11228 if(inited && disabled){
11230 if(locks.length < 1){
11237 * Disable this quick tip.
11239 disable : function(){
11241 clearTimeout(showProc);
11242 clearTimeout(hideProc);
11243 clearTimeout(dismissProc);
11251 * Returns true if the quick tip is enabled, else false.
11253 isEnabled : function(){
11259 namespace : "roo", // was ext?? this may break..
11260 alt_namespace : "ext",
11261 attribute : "qtip",
11271 // backwards compat
11272 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11274 * Ext JS Library 1.1.1
11275 * Copyright(c) 2006-2007, Ext JS, LLC.
11277 * Originally Released Under LGPL - original licence link has changed is not relivant.
11280 * <script type="text/javascript">
11285 * @class Roo.tree.TreePanel
11286 * @extends Roo.data.Tree
11287 * @cfg {Roo.tree.TreeNode} root The root node
11288 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11289 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11290 * @cfg {Boolean} enableDD true to enable drag and drop
11291 * @cfg {Boolean} enableDrag true to enable just drag
11292 * @cfg {Boolean} enableDrop true to enable just drop
11293 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11294 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11295 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11296 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11297 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11298 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11299 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11300 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11301 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11302 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11303 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11304 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11305 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11306 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11307 * @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>
11308 * @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>
11311 * @param {String/HTMLElement/Element} el The container element
11312 * @param {Object} config
11314 Roo.tree.TreePanel = function(el, config){
11316 var loader = false;
11318 root = config.root;
11319 delete config.root;
11321 if (config.loader) {
11322 loader = config.loader;
11323 delete config.loader;
11326 Roo.apply(this, config);
11327 Roo.tree.TreePanel.superclass.constructor.call(this);
11328 this.el = Roo.get(el);
11329 this.el.addClass('x-tree');
11330 //console.log(root);
11332 this.setRootNode( Roo.factory(root, Roo.tree));
11335 this.loader = Roo.factory(loader, Roo.tree);
11338 * Read-only. The id of the container element becomes this TreePanel's id.
11340 this.id = this.el.id;
11343 * @event beforeload
11344 * Fires before a node is loaded, return false to cancel
11345 * @param {Node} node The node being loaded
11347 "beforeload" : true,
11350 * Fires when a node is loaded
11351 * @param {Node} node The node that was loaded
11355 * @event textchange
11356 * Fires when the text for a node is changed
11357 * @param {Node} node The node
11358 * @param {String} text The new text
11359 * @param {String} oldText The old text
11361 "textchange" : true,
11363 * @event beforeexpand
11364 * Fires before a node is expanded, return false to cancel.
11365 * @param {Node} node The node
11366 * @param {Boolean} deep
11367 * @param {Boolean} anim
11369 "beforeexpand" : true,
11371 * @event beforecollapse
11372 * Fires before a node is collapsed, return false to cancel.
11373 * @param {Node} node The node
11374 * @param {Boolean} deep
11375 * @param {Boolean} anim
11377 "beforecollapse" : true,
11380 * Fires when a node is expanded
11381 * @param {Node} node The node
11385 * @event disabledchange
11386 * Fires when the disabled status of a node changes
11387 * @param {Node} node The node
11388 * @param {Boolean} disabled
11390 "disabledchange" : true,
11393 * Fires when a node is collapsed
11394 * @param {Node} node The node
11398 * @event beforeclick
11399 * Fires before click processing on a node. Return false to cancel the default action.
11400 * @param {Node} node The node
11401 * @param {Roo.EventObject} e The event object
11403 "beforeclick":true,
11405 * @event checkchange
11406 * Fires when a node with a checkbox's checked property changes
11407 * @param {Node} this This node
11408 * @param {Boolean} checked
11410 "checkchange":true,
11413 * Fires when a node is clicked
11414 * @param {Node} node The node
11415 * @param {Roo.EventObject} e The event object
11420 * Fires when a node is double clicked
11421 * @param {Node} node The node
11422 * @param {Roo.EventObject} e The event object
11426 * @event contextmenu
11427 * Fires when a node is right clicked
11428 * @param {Node} node The node
11429 * @param {Roo.EventObject} e The event object
11431 "contextmenu":true,
11433 * @event beforechildrenrendered
11434 * Fires right before the child nodes for a node are rendered
11435 * @param {Node} node The node
11437 "beforechildrenrendered":true,
11440 * Fires when a node starts being dragged
11441 * @param {Roo.tree.TreePanel} this
11442 * @param {Roo.tree.TreeNode} node
11443 * @param {event} e The raw browser event
11445 "startdrag" : true,
11448 * Fires when a drag operation is complete
11449 * @param {Roo.tree.TreePanel} this
11450 * @param {Roo.tree.TreeNode} node
11451 * @param {event} e The raw browser event
11456 * Fires when a dragged node is dropped on a valid DD target
11457 * @param {Roo.tree.TreePanel} this
11458 * @param {Roo.tree.TreeNode} node
11459 * @param {DD} dd The dd it was dropped on
11460 * @param {event} e The raw browser event
11464 * @event beforenodedrop
11465 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11466 * passed to handlers has the following properties:<br />
11467 * <ul style="padding:5px;padding-left:16px;">
11468 * <li>tree - The TreePanel</li>
11469 * <li>target - The node being targeted for the drop</li>
11470 * <li>data - The drag data from the drag source</li>
11471 * <li>point - The point of the drop - append, above or below</li>
11472 * <li>source - The drag source</li>
11473 * <li>rawEvent - Raw mouse event</li>
11474 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11475 * to be inserted by setting them on this object.</li>
11476 * <li>cancel - Set this to true to cancel the drop.</li>
11478 * @param {Object} dropEvent
11480 "beforenodedrop" : true,
11483 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11484 * passed to handlers has the following properties:<br />
11485 * <ul style="padding:5px;padding-left:16px;">
11486 * <li>tree - The TreePanel</li>
11487 * <li>target - The node being targeted for the drop</li>
11488 * <li>data - The drag data from the drag source</li>
11489 * <li>point - The point of the drop - append, above or below</li>
11490 * <li>source - The drag source</li>
11491 * <li>rawEvent - Raw mouse event</li>
11492 * <li>dropNode - Dropped node(s).</li>
11494 * @param {Object} dropEvent
11498 * @event nodedragover
11499 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11500 * passed to handlers has the following properties:<br />
11501 * <ul style="padding:5px;padding-left:16px;">
11502 * <li>tree - The TreePanel</li>
11503 * <li>target - The node being targeted for the drop</li>
11504 * <li>data - The drag data from the drag source</li>
11505 * <li>point - The point of the drop - append, above or below</li>
11506 * <li>source - The drag source</li>
11507 * <li>rawEvent - Raw mouse event</li>
11508 * <li>dropNode - Drop node(s) provided by the source.</li>
11509 * <li>cancel - Set this to true to signal drop not allowed.</li>
11511 * @param {Object} dragOverEvent
11513 "nodedragover" : true,
11515 * @event appendnode
11516 * Fires when append node to the tree
11517 * @param {Roo.tree.TreePanel} this
11518 * @param {Roo.tree.TreeNode} node
11519 * @param {Number} index The index of the newly appended node
11521 "appendnode" : true
11524 if(this.singleExpand){
11525 this.on("beforeexpand", this.restrictExpand, this);
11528 this.editor.tree = this;
11529 this.editor = Roo.factory(this.editor, Roo.tree);
11532 if (this.selModel) {
11533 this.selModel = Roo.factory(this.selModel, Roo.tree);
11537 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11538 rootVisible : true,
11539 animate: Roo.enableFx,
11542 hlDrop : Roo.enableFx,
11546 rendererTip: false,
11548 restrictExpand : function(node){
11549 var p = node.parentNode;
11551 if(p.expandedChild && p.expandedChild.parentNode == p){
11552 p.expandedChild.collapse();
11554 p.expandedChild = node;
11558 // private override
11559 setRootNode : function(node){
11560 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11561 if(!this.rootVisible){
11562 node.ui = new Roo.tree.RootTreeNodeUI(node);
11568 * Returns the container element for this TreePanel
11570 getEl : function(){
11575 * Returns the default TreeLoader for this TreePanel
11577 getLoader : function(){
11578 return this.loader;
11584 expandAll : function(){
11585 this.root.expand(true);
11589 * Collapse all nodes
11591 collapseAll : function(){
11592 this.root.collapse(true);
11596 * Returns the selection model used by this TreePanel
11598 getSelectionModel : function(){
11599 if(!this.selModel){
11600 this.selModel = new Roo.tree.DefaultSelectionModel();
11602 return this.selModel;
11606 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11607 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11608 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11611 getChecked : function(a, startNode){
11612 startNode = startNode || this.root;
11614 var f = function(){
11615 if(this.attributes.checked){
11616 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11619 startNode.cascade(f);
11624 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11625 * @param {String} path
11626 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11627 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11628 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11630 expandPath : function(path, attr, callback){
11631 attr = attr || "id";
11632 var keys = path.split(this.pathSeparator);
11633 var curNode = this.root;
11634 if(curNode.attributes[attr] != keys[1]){ // invalid root
11636 callback(false, null);
11641 var f = function(){
11642 if(++index == keys.length){
11644 callback(true, curNode);
11648 var c = curNode.findChild(attr, keys[index]);
11651 callback(false, curNode);
11656 c.expand(false, false, f);
11658 curNode.expand(false, false, f);
11662 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11663 * @param {String} path
11664 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11665 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11666 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11668 selectPath : function(path, attr, callback){
11669 attr = attr || "id";
11670 var keys = path.split(this.pathSeparator);
11671 var v = keys.pop();
11672 if(keys.length > 0){
11673 var f = function(success, node){
11674 if(success && node){
11675 var n = node.findChild(attr, v);
11681 }else if(callback){
11682 callback(false, n);
11686 callback(false, n);
11690 this.expandPath(keys.join(this.pathSeparator), attr, f);
11692 this.root.select();
11694 callback(true, this.root);
11699 getTreeEl : function(){
11704 * Trigger rendering of this TreePanel
11706 render : function(){
11707 if (this.innerCt) {
11708 return this; // stop it rendering more than once!!
11711 this.innerCt = this.el.createChild({tag:"ul",
11712 cls:"x-tree-root-ct " +
11713 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11715 if(this.containerScroll){
11716 Roo.dd.ScrollManager.register(this.el);
11718 if((this.enableDD || this.enableDrop) && !this.dropZone){
11720 * The dropZone used by this tree if drop is enabled
11721 * @type Roo.tree.TreeDropZone
11723 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11724 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11727 if((this.enableDD || this.enableDrag) && !this.dragZone){
11729 * The dragZone used by this tree if drag is enabled
11730 * @type Roo.tree.TreeDragZone
11732 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11733 ddGroup: this.ddGroup || "TreeDD",
11734 scroll: this.ddScroll
11737 this.getSelectionModel().init(this);
11739 Roo.log("ROOT not set in tree");
11742 this.root.render();
11743 if(!this.rootVisible){
11744 this.root.renderChildren();
11750 * Ext JS Library 1.1.1
11751 * Copyright(c) 2006-2007, Ext JS, LLC.
11753 * Originally Released Under LGPL - original licence link has changed is not relivant.
11756 * <script type="text/javascript">
11761 * @class Roo.tree.DefaultSelectionModel
11762 * @extends Roo.util.Observable
11763 * The default single selection for a TreePanel.
11764 * @param {Object} cfg Configuration
11766 Roo.tree.DefaultSelectionModel = function(cfg){
11767 this.selNode = null;
11773 * @event selectionchange
11774 * Fires when the selected node changes
11775 * @param {DefaultSelectionModel} this
11776 * @param {TreeNode} node the new selection
11778 "selectionchange" : true,
11781 * @event beforeselect
11782 * Fires before the selected node changes, return false to cancel the change
11783 * @param {DefaultSelectionModel} this
11784 * @param {TreeNode} node the new selection
11785 * @param {TreeNode} node the old selection
11787 "beforeselect" : true
11790 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11793 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11794 init : function(tree){
11796 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11797 tree.on("click", this.onNodeClick, this);
11800 onNodeClick : function(node, e){
11801 if (e.ctrlKey && this.selNode == node) {
11802 this.unselect(node);
11810 * @param {TreeNode} node The node to select
11811 * @return {TreeNode} The selected node
11813 select : function(node){
11814 var last = this.selNode;
11815 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11817 last.ui.onSelectedChange(false);
11819 this.selNode = node;
11820 node.ui.onSelectedChange(true);
11821 this.fireEvent("selectionchange", this, node, last);
11828 * @param {TreeNode} node The node to unselect
11830 unselect : function(node){
11831 if(this.selNode == node){
11832 this.clearSelections();
11837 * Clear all selections
11839 clearSelections : function(){
11840 var n = this.selNode;
11842 n.ui.onSelectedChange(false);
11843 this.selNode = null;
11844 this.fireEvent("selectionchange", this, null);
11850 * Get the selected node
11851 * @return {TreeNode} The selected node
11853 getSelectedNode : function(){
11854 return this.selNode;
11858 * Returns true if the node is selected
11859 * @param {TreeNode} node The node to check
11860 * @return {Boolean}
11862 isSelected : function(node){
11863 return this.selNode == node;
11867 * Selects the node above the selected node in the tree, intelligently walking the nodes
11868 * @return TreeNode The new selection
11870 selectPrevious : function(){
11871 var s = this.selNode || this.lastSelNode;
11875 var ps = s.previousSibling;
11877 if(!ps.isExpanded() || ps.childNodes.length < 1){
11878 return this.select(ps);
11880 var lc = ps.lastChild;
11881 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11884 return this.select(lc);
11886 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11887 return this.select(s.parentNode);
11893 * Selects the node above the selected node in the tree, intelligently walking the nodes
11894 * @return TreeNode The new selection
11896 selectNext : function(){
11897 var s = this.selNode || this.lastSelNode;
11901 if(s.firstChild && s.isExpanded()){
11902 return this.select(s.firstChild);
11903 }else if(s.nextSibling){
11904 return this.select(s.nextSibling);
11905 }else if(s.parentNode){
11907 s.parentNode.bubble(function(){
11908 if(this.nextSibling){
11909 newS = this.getOwnerTree().selModel.select(this.nextSibling);
11918 onKeyDown : function(e){
11919 var s = this.selNode || this.lastSelNode;
11920 // undesirable, but required
11925 var k = e.getKey();
11933 this.selectPrevious();
11936 e.preventDefault();
11937 if(s.hasChildNodes()){
11938 if(!s.isExpanded()){
11940 }else if(s.firstChild){
11941 this.select(s.firstChild, e);
11946 e.preventDefault();
11947 if(s.hasChildNodes() && s.isExpanded()){
11949 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11950 this.select(s.parentNode, e);
11958 * @class Roo.tree.MultiSelectionModel
11959 * @extends Roo.util.Observable
11960 * Multi selection for a TreePanel.
11961 * @param {Object} cfg Configuration
11963 Roo.tree.MultiSelectionModel = function(){
11964 this.selNodes = [];
11968 * @event selectionchange
11969 * Fires when the selected nodes change
11970 * @param {MultiSelectionModel} this
11971 * @param {Array} nodes Array of the selected nodes
11973 "selectionchange" : true
11975 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11979 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11980 init : function(tree){
11982 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11983 tree.on("click", this.onNodeClick, this);
11986 onNodeClick : function(node, e){
11987 this.select(node, e, e.ctrlKey);
11992 * @param {TreeNode} node The node to select
11993 * @param {EventObject} e (optional) An event associated with the selection
11994 * @param {Boolean} keepExisting True to retain existing selections
11995 * @return {TreeNode} The selected node
11997 select : function(node, e, keepExisting){
11998 if(keepExisting !== true){
11999 this.clearSelections(true);
12001 if(this.isSelected(node)){
12002 this.lastSelNode = node;
12005 this.selNodes.push(node);
12006 this.selMap[node.id] = node;
12007 this.lastSelNode = node;
12008 node.ui.onSelectedChange(true);
12009 this.fireEvent("selectionchange", this, this.selNodes);
12015 * @param {TreeNode} node The node to unselect
12017 unselect : function(node){
12018 if(this.selMap[node.id]){
12019 node.ui.onSelectedChange(false);
12020 var sn = this.selNodes;
12023 index = sn.indexOf(node);
12025 for(var i = 0, len = sn.length; i < len; i++){
12033 this.selNodes.splice(index, 1);
12035 delete this.selMap[node.id];
12036 this.fireEvent("selectionchange", this, this.selNodes);
12041 * Clear all selections
12043 clearSelections : function(suppressEvent){
12044 var sn = this.selNodes;
12046 for(var i = 0, len = sn.length; i < len; i++){
12047 sn[i].ui.onSelectedChange(false);
12049 this.selNodes = [];
12051 if(suppressEvent !== true){
12052 this.fireEvent("selectionchange", this, this.selNodes);
12058 * Returns true if the node is selected
12059 * @param {TreeNode} node The node to check
12060 * @return {Boolean}
12062 isSelected : function(node){
12063 return this.selMap[node.id] ? true : false;
12067 * Returns an array of the selected nodes
12070 getSelectedNodes : function(){
12071 return this.selNodes;
12074 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12076 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12078 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12081 * Ext JS Library 1.1.1
12082 * Copyright(c) 2006-2007, Ext JS, LLC.
12084 * Originally Released Under LGPL - original licence link has changed is not relivant.
12087 * <script type="text/javascript">
12091 * @class Roo.tree.TreeNode
12092 * @extends Roo.data.Node
12093 * @cfg {String} text The text for this node
12094 * @cfg {Boolean} expanded true to start the node expanded
12095 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12096 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12097 * @cfg {Boolean} disabled true to start the node disabled
12098 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12099 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12100 * @cfg {String} cls A css class to be added to the node
12101 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12102 * @cfg {String} href URL of the link used for the node (defaults to #)
12103 * @cfg {String} hrefTarget target frame for the link
12104 * @cfg {String} qtip An Ext QuickTip for the node
12105 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12106 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12107 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12108 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12109 * (defaults to undefined with no checkbox rendered)
12111 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12113 Roo.tree.TreeNode = function(attributes){
12114 attributes = attributes || {};
12115 if(typeof attributes == "string"){
12116 attributes = {text: attributes};
12118 this.childrenRendered = false;
12119 this.rendered = false;
12120 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12121 this.expanded = attributes.expanded === true;
12122 this.isTarget = attributes.isTarget !== false;
12123 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12124 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12127 * Read-only. The text for this node. To change it use setText().
12130 this.text = attributes.text;
12132 * True if this node is disabled.
12135 this.disabled = attributes.disabled === true;
12139 * @event textchange
12140 * Fires when the text for this node is changed
12141 * @param {Node} this This node
12142 * @param {String} text The new text
12143 * @param {String} oldText The old text
12145 "textchange" : true,
12147 * @event beforeexpand
12148 * Fires before this node is expanded, return false to cancel.
12149 * @param {Node} this This node
12150 * @param {Boolean} deep
12151 * @param {Boolean} anim
12153 "beforeexpand" : true,
12155 * @event beforecollapse
12156 * Fires before this node is collapsed, return false to cancel.
12157 * @param {Node} this This node
12158 * @param {Boolean} deep
12159 * @param {Boolean} anim
12161 "beforecollapse" : true,
12164 * Fires when this node is expanded
12165 * @param {Node} this This node
12169 * @event disabledchange
12170 * Fires when the disabled status of this node changes
12171 * @param {Node} this This node
12172 * @param {Boolean} disabled
12174 "disabledchange" : true,
12177 * Fires when this node is collapsed
12178 * @param {Node} this This node
12182 * @event beforeclick
12183 * Fires before click processing. Return false to cancel the default action.
12184 * @param {Node} this This node
12185 * @param {Roo.EventObject} e The event object
12187 "beforeclick":true,
12189 * @event checkchange
12190 * Fires when a node with a checkbox's checked property changes
12191 * @param {Node} this This node
12192 * @param {Boolean} checked
12194 "checkchange":true,
12197 * Fires when this node is clicked
12198 * @param {Node} this This node
12199 * @param {Roo.EventObject} e The event object
12204 * Fires when this node is double clicked
12205 * @param {Node} this This node
12206 * @param {Roo.EventObject} e The event object
12210 * @event contextmenu
12211 * Fires when this node is right clicked
12212 * @param {Node} this This node
12213 * @param {Roo.EventObject} e The event object
12215 "contextmenu":true,
12217 * @event beforechildrenrendered
12218 * Fires right before the child nodes for this node are rendered
12219 * @param {Node} this This node
12221 "beforechildrenrendered":true
12224 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12227 * Read-only. The UI for this node
12230 this.ui = new uiClass(this);
12232 // finally support items[]
12233 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12238 Roo.each(this.attributes.items, function(c) {
12239 this.appendChild(Roo.factory(c,Roo.Tree));
12241 delete this.attributes.items;
12246 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12247 preventHScroll: true,
12249 * Returns true if this node is expanded
12250 * @return {Boolean}
12252 isExpanded : function(){
12253 return this.expanded;
12257 * Returns the UI object for this node
12258 * @return {TreeNodeUI}
12260 getUI : function(){
12264 // private override
12265 setFirstChild : function(node){
12266 var of = this.firstChild;
12267 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12268 if(this.childrenRendered && of && node != of){
12269 of.renderIndent(true, true);
12272 this.renderIndent(true, true);
12276 // private override
12277 setLastChild : function(node){
12278 var ol = this.lastChild;
12279 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12280 if(this.childrenRendered && ol && node != ol){
12281 ol.renderIndent(true, true);
12284 this.renderIndent(true, true);
12288 // these methods are overridden to provide lazy rendering support
12289 // private override
12290 appendChild : function()
12292 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12293 if(node && this.childrenRendered){
12296 this.ui.updateExpandIcon();
12300 // private override
12301 removeChild : function(node){
12302 this.ownerTree.getSelectionModel().unselect(node);
12303 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12304 // if it's been rendered remove dom node
12305 if(this.childrenRendered){
12308 if(this.childNodes.length < 1){
12309 this.collapse(false, false);
12311 this.ui.updateExpandIcon();
12313 if(!this.firstChild) {
12314 this.childrenRendered = false;
12319 // private override
12320 insertBefore : function(node, refNode){
12321 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12322 if(newNode && refNode && this.childrenRendered){
12325 this.ui.updateExpandIcon();
12330 * Sets the text for this node
12331 * @param {String} text
12333 setText : function(text){
12334 var oldText = this.text;
12336 this.attributes.text = text;
12337 if(this.rendered){ // event without subscribing
12338 this.ui.onTextChange(this, text, oldText);
12340 this.fireEvent("textchange", this, text, oldText);
12344 * Triggers selection of this node
12346 select : function(){
12347 this.getOwnerTree().getSelectionModel().select(this);
12351 * Triggers deselection of this node
12353 unselect : function(){
12354 this.getOwnerTree().getSelectionModel().unselect(this);
12358 * Returns true if this node is selected
12359 * @return {Boolean}
12361 isSelected : function(){
12362 return this.getOwnerTree().getSelectionModel().isSelected(this);
12366 * Expand this node.
12367 * @param {Boolean} deep (optional) True to expand all children as well
12368 * @param {Boolean} anim (optional) false to cancel the default animation
12369 * @param {Function} callback (optional) A callback to be called when
12370 * expanding this node completes (does not wait for deep expand to complete).
12371 * Called with 1 parameter, this node.
12373 expand : function(deep, anim, callback){
12374 if(!this.expanded){
12375 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12378 if(!this.childrenRendered){
12379 this.renderChildren();
12381 this.expanded = true;
12383 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12384 this.ui.animExpand(function(){
12385 this.fireEvent("expand", this);
12386 if(typeof callback == "function"){
12390 this.expandChildNodes(true);
12392 }.createDelegate(this));
12396 this.fireEvent("expand", this);
12397 if(typeof callback == "function"){
12402 if(typeof callback == "function"){
12407 this.expandChildNodes(true);
12411 isHiddenRoot : function(){
12412 return this.isRoot && !this.getOwnerTree().rootVisible;
12416 * Collapse this node.
12417 * @param {Boolean} deep (optional) True to collapse all children as well
12418 * @param {Boolean} anim (optional) false to cancel the default animation
12420 collapse : function(deep, anim){
12421 if(this.expanded && !this.isHiddenRoot()){
12422 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12425 this.expanded = false;
12426 if((this.getOwnerTree().animate && anim !== false) || anim){
12427 this.ui.animCollapse(function(){
12428 this.fireEvent("collapse", this);
12430 this.collapseChildNodes(true);
12432 }.createDelegate(this));
12435 this.ui.collapse();
12436 this.fireEvent("collapse", this);
12440 var cs = this.childNodes;
12441 for(var i = 0, len = cs.length; i < len; i++) {
12442 cs[i].collapse(true, false);
12448 delayedExpand : function(delay){
12449 if(!this.expandProcId){
12450 this.expandProcId = this.expand.defer(delay, this);
12455 cancelExpand : function(){
12456 if(this.expandProcId){
12457 clearTimeout(this.expandProcId);
12459 this.expandProcId = false;
12463 * Toggles expanded/collapsed state of the node
12465 toggle : function(){
12474 * Ensures all parent nodes are expanded
12476 ensureVisible : function(callback){
12477 var tree = this.getOwnerTree();
12478 tree.expandPath(this.parentNode.getPath(), false, function(){
12479 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12480 Roo.callback(callback);
12481 }.createDelegate(this));
12485 * Expand all child nodes
12486 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12488 expandChildNodes : function(deep){
12489 var cs = this.childNodes;
12490 for(var i = 0, len = cs.length; i < len; i++) {
12491 cs[i].expand(deep);
12496 * Collapse all child nodes
12497 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12499 collapseChildNodes : function(deep){
12500 var cs = this.childNodes;
12501 for(var i = 0, len = cs.length; i < len; i++) {
12502 cs[i].collapse(deep);
12507 * Disables this node
12509 disable : function(){
12510 this.disabled = true;
12512 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12513 this.ui.onDisableChange(this, true);
12515 this.fireEvent("disabledchange", this, true);
12519 * Enables this node
12521 enable : function(){
12522 this.disabled = false;
12523 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12524 this.ui.onDisableChange(this, false);
12526 this.fireEvent("disabledchange", this, false);
12530 renderChildren : function(suppressEvent){
12531 if(suppressEvent !== false){
12532 this.fireEvent("beforechildrenrendered", this);
12534 var cs = this.childNodes;
12535 for(var i = 0, len = cs.length; i < len; i++){
12536 cs[i].render(true);
12538 this.childrenRendered = true;
12542 sort : function(fn, scope){
12543 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12544 if(this.childrenRendered){
12545 var cs = this.childNodes;
12546 for(var i = 0, len = cs.length; i < len; i++){
12547 cs[i].render(true);
12553 render : function(bulkRender){
12554 this.ui.render(bulkRender);
12555 if(!this.rendered){
12556 this.rendered = true;
12558 this.expanded = false;
12559 this.expand(false, false);
12565 renderIndent : function(deep, refresh){
12567 this.ui.childIndent = null;
12569 this.ui.renderIndent();
12570 if(deep === true && this.childrenRendered){
12571 var cs = this.childNodes;
12572 for(var i = 0, len = cs.length; i < len; i++){
12573 cs[i].renderIndent(true, refresh);
12579 * Ext JS Library 1.1.1
12580 * Copyright(c) 2006-2007, Ext JS, LLC.
12582 * Originally Released Under LGPL - original licence link has changed is not relivant.
12585 * <script type="text/javascript">
12589 * @class Roo.tree.AsyncTreeNode
12590 * @extends Roo.tree.TreeNode
12591 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12593 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12595 Roo.tree.AsyncTreeNode = function(config){
12596 this.loaded = false;
12597 this.loading = false;
12598 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12600 * @event beforeload
12601 * Fires before this node is loaded, return false to cancel
12602 * @param {Node} this This node
12604 this.addEvents({'beforeload':true, 'load': true});
12607 * Fires when this node is loaded
12608 * @param {Node} this This node
12611 * The loader used by this node (defaults to using the tree's defined loader)
12616 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12617 expand : function(deep, anim, callback){
12618 if(this.loading){ // if an async load is already running, waiting til it's done
12620 var f = function(){
12621 if(!this.loading){ // done loading
12622 clearInterval(timer);
12623 this.expand(deep, anim, callback);
12625 }.createDelegate(this);
12626 timer = setInterval(f, 200);
12630 if(this.fireEvent("beforeload", this) === false){
12633 this.loading = true;
12634 this.ui.beforeLoad(this);
12635 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12637 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12641 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12645 * Returns true if this node is currently loading
12646 * @return {Boolean}
12648 isLoading : function(){
12649 return this.loading;
12652 loadComplete : function(deep, anim, callback){
12653 this.loading = false;
12654 this.loaded = true;
12655 this.ui.afterLoad(this);
12656 this.fireEvent("load", this);
12657 this.expand(deep, anim, callback);
12661 * Returns true if this node has been loaded
12662 * @return {Boolean}
12664 isLoaded : function(){
12665 return this.loaded;
12668 hasChildNodes : function(){
12669 if(!this.isLeaf() && !this.loaded){
12672 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12677 * Trigger a reload for this node
12678 * @param {Function} callback
12680 reload : function(callback){
12681 this.collapse(false, false);
12682 while(this.firstChild){
12683 this.removeChild(this.firstChild);
12685 this.childrenRendered = false;
12686 this.loaded = false;
12687 if(this.isHiddenRoot()){
12688 this.expanded = false;
12690 this.expand(false, false, callback);
12694 * Ext JS Library 1.1.1
12695 * Copyright(c) 2006-2007, Ext JS, LLC.
12697 * Originally Released Under LGPL - original licence link has changed is not relivant.
12700 * <script type="text/javascript">
12704 * @class Roo.tree.TreeNodeUI
12706 * @param {Object} node The node to render
12707 * The TreeNode UI implementation is separate from the
12708 * tree implementation. Unless you are customizing the tree UI,
12709 * you should never have to use this directly.
12711 Roo.tree.TreeNodeUI = function(node){
12713 this.rendered = false;
12714 this.animating = false;
12715 this.emptyIcon = Roo.BLANK_IMAGE_URL;
12718 Roo.tree.TreeNodeUI.prototype = {
12719 removeChild : function(node){
12721 this.ctNode.removeChild(node.ui.getEl());
12725 beforeLoad : function(){
12726 this.addClass("x-tree-node-loading");
12729 afterLoad : function(){
12730 this.removeClass("x-tree-node-loading");
12733 onTextChange : function(node, text, oldText){
12735 this.textNode.innerHTML = text;
12739 onDisableChange : function(node, state){
12740 this.disabled = state;
12742 this.addClass("x-tree-node-disabled");
12744 this.removeClass("x-tree-node-disabled");
12748 onSelectedChange : function(state){
12751 this.addClass("x-tree-selected");
12754 this.removeClass("x-tree-selected");
12758 onMove : function(tree, node, oldParent, newParent, index, refNode){
12759 this.childIndent = null;
12761 var targetNode = newParent.ui.getContainer();
12762 if(!targetNode){//target not rendered
12763 this.holder = document.createElement("div");
12764 this.holder.appendChild(this.wrap);
12767 var insertBefore = refNode ? refNode.ui.getEl() : null;
12769 targetNode.insertBefore(this.wrap, insertBefore);
12771 targetNode.appendChild(this.wrap);
12773 this.node.renderIndent(true);
12777 addClass : function(cls){
12779 Roo.fly(this.elNode).addClass(cls);
12783 removeClass : function(cls){
12785 Roo.fly(this.elNode).removeClass(cls);
12789 remove : function(){
12791 this.holder = document.createElement("div");
12792 this.holder.appendChild(this.wrap);
12796 fireEvent : function(){
12797 return this.node.fireEvent.apply(this.node, arguments);
12800 initEvents : function(){
12801 this.node.on("move", this.onMove, this);
12802 var E = Roo.EventManager;
12803 var a = this.anchor;
12805 var el = Roo.fly(a, '_treeui');
12807 if(Roo.isOpera){ // opera render bug ignores the CSS
12808 el.setStyle("text-decoration", "none");
12811 el.on("click", this.onClick, this);
12812 el.on("dblclick", this.onDblClick, this);
12815 Roo.EventManager.on(this.checkbox,
12816 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12819 el.on("contextmenu", this.onContextMenu, this);
12821 var icon = Roo.fly(this.iconNode);
12822 icon.on("click", this.onClick, this);
12823 icon.on("dblclick", this.onDblClick, this);
12824 icon.on("contextmenu", this.onContextMenu, this);
12825 E.on(this.ecNode, "click", this.ecClick, this, true);
12827 if(this.node.disabled){
12828 this.addClass("x-tree-node-disabled");
12830 if(this.node.hidden){
12831 this.addClass("x-tree-node-disabled");
12833 var ot = this.node.getOwnerTree();
12834 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12835 if(dd && (!this.node.isRoot || ot.rootVisible)){
12836 Roo.dd.Registry.register(this.elNode, {
12838 handles: this.getDDHandles(),
12844 getDDHandles : function(){
12845 return [this.iconNode, this.textNode];
12850 this.wrap.style.display = "none";
12856 this.wrap.style.display = "";
12860 onContextMenu : function(e){
12861 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12862 e.preventDefault();
12864 this.fireEvent("contextmenu", this.node, e);
12868 onClick : function(e){
12873 if(this.fireEvent("beforeclick", this.node, e) !== false){
12874 if(!this.disabled && this.node.attributes.href){
12875 this.fireEvent("click", this.node, e);
12878 e.preventDefault();
12883 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12884 this.node.toggle();
12887 this.fireEvent("click", this.node, e);
12893 onDblClick : function(e){
12894 e.preventDefault();
12899 this.toggleCheck();
12901 if(!this.animating && this.node.hasChildNodes()){
12902 this.node.toggle();
12904 this.fireEvent("dblclick", this.node, e);
12907 onCheckChange : function(){
12908 var checked = this.checkbox.checked;
12909 this.node.attributes.checked = checked;
12910 this.fireEvent('checkchange', this.node, checked);
12913 ecClick : function(e){
12914 if(!this.animating && this.node.hasChildNodes()){
12915 this.node.toggle();
12919 startDrop : function(){
12920 this.dropping = true;
12923 // delayed drop so the click event doesn't get fired on a drop
12924 endDrop : function(){
12925 setTimeout(function(){
12926 this.dropping = false;
12927 }.createDelegate(this), 50);
12930 expand : function(){
12931 this.updateExpandIcon();
12932 this.ctNode.style.display = "";
12935 focus : function(){
12936 if(!this.node.preventHScroll){
12937 try{this.anchor.focus();
12939 }else if(!Roo.isIE){
12941 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12942 var l = noscroll.scrollLeft;
12943 this.anchor.focus();
12944 noscroll.scrollLeft = l;
12949 toggleCheck : function(value){
12950 var cb = this.checkbox;
12952 cb.checked = (value === undefined ? !cb.checked : value);
12958 this.anchor.blur();
12962 animExpand : function(callback){
12963 var ct = Roo.get(this.ctNode);
12965 if(!this.node.hasChildNodes()){
12966 this.updateExpandIcon();
12967 this.ctNode.style.display = "";
12968 Roo.callback(callback);
12971 this.animating = true;
12972 this.updateExpandIcon();
12975 callback : function(){
12976 this.animating = false;
12977 Roo.callback(callback);
12980 duration: this.node.ownerTree.duration || .25
12984 highlight : function(){
12985 var tree = this.node.getOwnerTree();
12986 Roo.fly(this.wrap).highlight(
12987 tree.hlColor || "C3DAF9",
12988 {endColor: tree.hlBaseColor}
12992 collapse : function(){
12993 this.updateExpandIcon();
12994 this.ctNode.style.display = "none";
12997 animCollapse : function(callback){
12998 var ct = Roo.get(this.ctNode);
12999 ct.enableDisplayMode('block');
13002 this.animating = true;
13003 this.updateExpandIcon();
13006 callback : function(){
13007 this.animating = false;
13008 Roo.callback(callback);
13011 duration: this.node.ownerTree.duration || .25
13015 getContainer : function(){
13016 return this.ctNode;
13019 getEl : function(){
13023 appendDDGhost : function(ghostNode){
13024 ghostNode.appendChild(this.elNode.cloneNode(true));
13027 getDDRepairXY : function(){
13028 return Roo.lib.Dom.getXY(this.iconNode);
13031 onRender : function(){
13035 render : function(bulkRender){
13036 var n = this.node, a = n.attributes;
13037 var targetNode = n.parentNode ?
13038 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13040 if(!this.rendered){
13041 this.rendered = true;
13043 this.renderElements(n, a, targetNode, bulkRender);
13046 if(this.textNode.setAttributeNS){
13047 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13049 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13052 this.textNode.setAttribute("ext:qtip", a.qtip);
13054 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13057 }else if(a.qtipCfg){
13058 a.qtipCfg.target = Roo.id(this.textNode);
13059 Roo.QuickTips.register(a.qtipCfg);
13062 if(!this.node.expanded){
13063 this.updateExpandIcon();
13066 if(bulkRender === true) {
13067 targetNode.appendChild(this.wrap);
13072 renderElements : function(n, a, targetNode, bulkRender)
13074 // add some indent caching, this helps performance when rendering a large tree
13075 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13076 var t = n.getOwnerTree();
13077 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13078 if (typeof(n.attributes.html) != 'undefined') {
13079 txt = n.attributes.html;
13081 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13082 var cb = typeof a.checked == 'boolean';
13083 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13084 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13085 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13086 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13087 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13088 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13089 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13090 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13091 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13092 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13095 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13096 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13097 n.nextSibling.ui.getEl(), buf.join(""));
13099 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13102 this.elNode = this.wrap.childNodes[0];
13103 this.ctNode = this.wrap.childNodes[1];
13104 var cs = this.elNode.childNodes;
13105 this.indentNode = cs[0];
13106 this.ecNode = cs[1];
13107 this.iconNode = cs[2];
13110 this.checkbox = cs[3];
13113 this.anchor = cs[index];
13114 this.textNode = cs[index].firstChild;
13117 getAnchor : function(){
13118 return this.anchor;
13121 getTextEl : function(){
13122 return this.textNode;
13125 getIconEl : function(){
13126 return this.iconNode;
13129 isChecked : function(){
13130 return this.checkbox ? this.checkbox.checked : false;
13133 updateExpandIcon : function(){
13135 var n = this.node, c1, c2;
13136 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13137 var hasChild = n.hasChildNodes();
13141 c1 = "x-tree-node-collapsed";
13142 c2 = "x-tree-node-expanded";
13145 c1 = "x-tree-node-expanded";
13146 c2 = "x-tree-node-collapsed";
13149 this.removeClass("x-tree-node-leaf");
13150 this.wasLeaf = false;
13152 if(this.c1 != c1 || this.c2 != c2){
13153 Roo.fly(this.elNode).replaceClass(c1, c2);
13154 this.c1 = c1; this.c2 = c2;
13157 // this changes non-leafs into leafs if they have no children.
13158 // it's not very rational behaviour..
13160 if(!this.wasLeaf && this.node.leaf){
13161 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13164 this.wasLeaf = true;
13167 var ecc = "x-tree-ec-icon "+cls;
13168 if(this.ecc != ecc){
13169 this.ecNode.className = ecc;
13175 getChildIndent : function(){
13176 if(!this.childIndent){
13180 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13182 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13184 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13189 this.childIndent = buf.join("");
13191 return this.childIndent;
13194 renderIndent : function(){
13197 var p = this.node.parentNode;
13199 indent = p.ui.getChildIndent();
13201 if(this.indentMarkup != indent){ // don't rerender if not required
13202 this.indentNode.innerHTML = indent;
13203 this.indentMarkup = indent;
13205 this.updateExpandIcon();
13210 Roo.tree.RootTreeNodeUI = function(){
13211 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13213 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13214 render : function(){
13215 if(!this.rendered){
13216 var targetNode = this.node.ownerTree.innerCt.dom;
13217 this.node.expanded = true;
13218 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13219 this.wrap = this.ctNode = targetNode.firstChild;
13222 collapse : function(){
13224 expand : function(){
13228 * Ext JS Library 1.1.1
13229 * Copyright(c) 2006-2007, Ext JS, LLC.
13231 * Originally Released Under LGPL - original licence link has changed is not relivant.
13234 * <script type="text/javascript">
13237 * @class Roo.tree.TreeLoader
13238 * @extends Roo.util.Observable
13239 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13240 * nodes from a specified URL. The response must be a javascript Array definition
13241 * who's elements are node definition objects. eg:
13246 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13247 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13254 * The old style respose with just an array is still supported, but not recommended.
13257 * A server request is sent, and child nodes are loaded only when a node is expanded.
13258 * The loading node's id is passed to the server under the parameter name "node" to
13259 * enable the server to produce the correct child nodes.
13261 * To pass extra parameters, an event handler may be attached to the "beforeload"
13262 * event, and the parameters specified in the TreeLoader's baseParams property:
13264 myTreeLoader.on("beforeload", function(treeLoader, node) {
13265 this.baseParams.category = node.attributes.category;
13270 * This would pass an HTTP parameter called "category" to the server containing
13271 * the value of the Node's "category" attribute.
13273 * Creates a new Treeloader.
13274 * @param {Object} config A config object containing config properties.
13276 Roo.tree.TreeLoader = function(config){
13277 this.baseParams = {};
13278 this.requestMethod = "POST";
13279 Roo.apply(this, config);
13284 * @event beforeload
13285 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13286 * @param {Object} This TreeLoader object.
13287 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13288 * @param {Object} callback The callback function specified in the {@link #load} call.
13293 * Fires when the node has been successfuly loaded.
13294 * @param {Object} This TreeLoader object.
13295 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13296 * @param {Object} response The response object containing the data from the server.
13300 * @event loadexception
13301 * Fires if the network request failed.
13302 * @param {Object} This TreeLoader object.
13303 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13304 * @param {Object} response The response object containing the data from the server.
13306 loadexception : true,
13309 * Fires before a node is created, enabling you to return custom Node types
13310 * @param {Object} This TreeLoader object.
13311 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13316 Roo.tree.TreeLoader.superclass.constructor.call(this);
13319 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13321 * @cfg {String} dataUrl The URL from which to request a Json string which
13322 * specifies an array of node definition object representing the child nodes
13326 * @cfg {String} requestMethod either GET or POST
13327 * defaults to POST (due to BC)
13331 * @cfg {Object} baseParams (optional) An object containing properties which
13332 * specify HTTP parameters to be passed to each request for child nodes.
13335 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13336 * created by this loader. If the attributes sent by the server have an attribute in this object,
13337 * they take priority.
13340 * @cfg {Object} uiProviders (optional) An object containing properties which
13342 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13343 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13344 * <i>uiProvider</i> attribute of a returned child node is a string rather
13345 * than a reference to a TreeNodeUI implementation, this that string value
13346 * is used as a property name in the uiProviders object. You can define the provider named
13347 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13352 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13353 * child nodes before loading.
13355 clearOnLoad : true,
13358 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13359 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13360 * Grid query { data : [ .....] }
13365 * @cfg {String} queryParam (optional)
13366 * Name of the query as it will be passed on the querystring (defaults to 'node')
13367 * eg. the request will be ?node=[id]
13374 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13375 * This is called automatically when a node is expanded, but may be used to reload
13376 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13377 * @param {Roo.tree.TreeNode} node
13378 * @param {Function} callback
13380 load : function(node, callback){
13381 if(this.clearOnLoad){
13382 while(node.firstChild){
13383 node.removeChild(node.firstChild);
13386 if(node.attributes.children){ // preloaded json children
13387 var cs = node.attributes.children;
13388 for(var i = 0, len = cs.length; i < len; i++){
13389 node.appendChild(this.createNode(cs[i]));
13391 if(typeof callback == "function"){
13394 }else if(this.dataUrl){
13395 this.requestData(node, callback);
13399 getParams: function(node){
13400 var buf = [], bp = this.baseParams;
13401 for(var key in bp){
13402 if(typeof bp[key] != "function"){
13403 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13406 var n = this.queryParam === false ? 'node' : this.queryParam;
13407 buf.push(n + "=", encodeURIComponent(node.id));
13408 return buf.join("");
13411 requestData : function(node, callback){
13412 if(this.fireEvent("beforeload", this, node, callback) !== false){
13413 this.transId = Roo.Ajax.request({
13414 method:this.requestMethod,
13415 url: this.dataUrl||this.url,
13416 success: this.handleResponse,
13417 failure: this.handleFailure,
13419 argument: {callback: callback, node: node},
13420 params: this.getParams(node)
13423 // if the load is cancelled, make sure we notify
13424 // the node that we are done
13425 if(typeof callback == "function"){
13431 isLoading : function(){
13432 return this.transId ? true : false;
13435 abort : function(){
13436 if(this.isLoading()){
13437 Roo.Ajax.abort(this.transId);
13442 createNode : function(attr)
13444 // apply baseAttrs, nice idea Corey!
13445 if(this.baseAttrs){
13446 Roo.applyIf(attr, this.baseAttrs);
13448 if(this.applyLoader !== false){
13449 attr.loader = this;
13451 // uiProvider = depreciated..
13453 if(typeof(attr.uiProvider) == 'string'){
13454 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13455 /** eval:var:attr */ eval(attr.uiProvider);
13457 if(typeof(this.uiProviders['default']) != 'undefined') {
13458 attr.uiProvider = this.uiProviders['default'];
13461 this.fireEvent('create', this, attr);
13463 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13465 new Roo.tree.TreeNode(attr) :
13466 new Roo.tree.AsyncTreeNode(attr));
13469 processResponse : function(response, node, callback)
13471 var json = response.responseText;
13474 var o = Roo.decode(json);
13476 if (this.root === false && typeof(o.success) != undefined) {
13477 this.root = 'data'; // the default behaviour for list like data..
13480 if (this.root !== false && !o.success) {
13481 // it's a failure condition.
13482 var a = response.argument;
13483 this.fireEvent("loadexception", this, a.node, response);
13484 Roo.log("Load failed - should have a handler really");
13490 if (this.root !== false) {
13494 for(var i = 0, len = o.length; i < len; i++){
13495 var n = this.createNode(o[i]);
13497 node.appendChild(n);
13500 if(typeof callback == "function"){
13501 callback(this, node);
13504 this.handleFailure(response);
13508 handleResponse : function(response){
13509 this.transId = false;
13510 var a = response.argument;
13511 this.processResponse(response, a.node, a.callback);
13512 this.fireEvent("load", this, a.node, response);
13515 handleFailure : function(response)
13517 // should handle failure better..
13518 this.transId = false;
13519 var a = response.argument;
13520 this.fireEvent("loadexception", this, a.node, response);
13521 if(typeof a.callback == "function"){
13522 a.callback(this, a.node);
13527 * Ext JS Library 1.1.1
13528 * Copyright(c) 2006-2007, Ext JS, LLC.
13530 * Originally Released Under LGPL - original licence link has changed is not relivant.
13533 * <script type="text/javascript">
13537 * @class Roo.tree.TreeFilter
13538 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13539 * @param {TreePanel} tree
13540 * @param {Object} config (optional)
13542 Roo.tree.TreeFilter = function(tree, config){
13544 this.filtered = {};
13545 Roo.apply(this, config);
13548 Roo.tree.TreeFilter.prototype = {
13555 * Filter the data by a specific attribute.
13556 * @param {String/RegExp} value Either string that the attribute value
13557 * should start with or a RegExp to test against the attribute
13558 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13559 * @param {TreeNode} startNode (optional) The node to start the filter at.
13561 filter : function(value, attr, startNode){
13562 attr = attr || "text";
13564 if(typeof value == "string"){
13565 var vlen = value.length;
13566 // auto clear empty filter
13567 if(vlen == 0 && this.clearBlank){
13571 value = value.toLowerCase();
13573 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13575 }else if(value.exec){ // regex?
13577 return value.test(n.attributes[attr]);
13580 throw 'Illegal filter type, must be string or regex';
13582 this.filterBy(f, null, startNode);
13586 * Filter by a function. The passed function will be called with each
13587 * node in the tree (or from the startNode). If the function returns true, the node is kept
13588 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13589 * @param {Function} fn The filter function
13590 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13592 filterBy : function(fn, scope, startNode){
13593 startNode = startNode || this.tree.root;
13594 if(this.autoClear){
13597 var af = this.filtered, rv = this.reverse;
13598 var f = function(n){
13599 if(n == startNode){
13605 var m = fn.call(scope || n, n);
13613 startNode.cascade(f);
13616 if(typeof id != "function"){
13618 if(n && n.parentNode){
13619 n.parentNode.removeChild(n);
13627 * Clears the current filter. Note: with the "remove" option
13628 * set a filter cannot be cleared.
13630 clear : function(){
13632 var af = this.filtered;
13634 if(typeof id != "function"){
13641 this.filtered = {};
13646 * Ext JS Library 1.1.1
13647 * Copyright(c) 2006-2007, Ext JS, LLC.
13649 * Originally Released Under LGPL - original licence link has changed is not relivant.
13652 * <script type="text/javascript">
13657 * @class Roo.tree.TreeSorter
13658 * Provides sorting of nodes in a TreePanel
13660 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13661 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13662 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13663 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13664 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13665 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13667 * @param {TreePanel} tree
13668 * @param {Object} config
13670 Roo.tree.TreeSorter = function(tree, config){
13671 Roo.apply(this, config);
13672 tree.on("beforechildrenrendered", this.doSort, this);
13673 tree.on("append", this.updateSort, this);
13674 tree.on("insert", this.updateSort, this);
13676 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13677 var p = this.property || "text";
13678 var sortType = this.sortType;
13679 var fs = this.folderSort;
13680 var cs = this.caseSensitive === true;
13681 var leafAttr = this.leafAttr || 'leaf';
13683 this.sortFn = function(n1, n2){
13685 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13688 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13692 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13693 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13695 return dsc ? +1 : -1;
13697 return dsc ? -1 : +1;
13704 Roo.tree.TreeSorter.prototype = {
13705 doSort : function(node){
13706 node.sort(this.sortFn);
13709 compareNodes : function(n1, n2){
13710 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13713 updateSort : function(tree, node){
13714 if(node.childrenRendered){
13715 this.doSort.defer(1, this, [node]);
13720 * Ext JS Library 1.1.1
13721 * Copyright(c) 2006-2007, Ext JS, LLC.
13723 * Originally Released Under LGPL - original licence link has changed is not relivant.
13726 * <script type="text/javascript">
13729 if(Roo.dd.DropZone){
13731 Roo.tree.TreeDropZone = function(tree, config){
13732 this.allowParentInsert = false;
13733 this.allowContainerDrop = false;
13734 this.appendOnly = false;
13735 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13737 this.lastInsertClass = "x-tree-no-status";
13738 this.dragOverData = {};
13741 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13742 ddGroup : "TreeDD",
13745 expandDelay : 1000,
13747 expandNode : function(node){
13748 if(node.hasChildNodes() && !node.isExpanded()){
13749 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13753 queueExpand : function(node){
13754 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13757 cancelExpand : function(){
13758 if(this.expandProcId){
13759 clearTimeout(this.expandProcId);
13760 this.expandProcId = false;
13764 isValidDropPoint : function(n, pt, dd, e, data){
13765 if(!n || !data){ return false; }
13766 var targetNode = n.node;
13767 var dropNode = data.node;
13768 // default drop rules
13769 if(!(targetNode && targetNode.isTarget && pt)){
13772 if(pt == "append" && targetNode.allowChildren === false){
13775 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13778 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13781 // reuse the object
13782 var overEvent = this.dragOverData;
13783 overEvent.tree = this.tree;
13784 overEvent.target = targetNode;
13785 overEvent.data = data;
13786 overEvent.point = pt;
13787 overEvent.source = dd;
13788 overEvent.rawEvent = e;
13789 overEvent.dropNode = dropNode;
13790 overEvent.cancel = false;
13791 var result = this.tree.fireEvent("nodedragover", overEvent);
13792 return overEvent.cancel === false && result !== false;
13795 getDropPoint : function(e, n, dd)
13799 return tn.allowChildren !== false ? "append" : false; // always append for root
13801 var dragEl = n.ddel;
13802 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13803 var y = Roo.lib.Event.getPageY(e);
13804 //var noAppend = tn.allowChildren === false || tn.isLeaf();
13806 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13807 var noAppend = tn.allowChildren === false;
13808 if(this.appendOnly || tn.parentNode.allowChildren === false){
13809 return noAppend ? false : "append";
13811 var noBelow = false;
13812 if(!this.allowParentInsert){
13813 noBelow = tn.hasChildNodes() && tn.isExpanded();
13815 var q = (b - t) / (noAppend ? 2 : 3);
13816 if(y >= t && y < (t + q)){
13818 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13825 onNodeEnter : function(n, dd, e, data)
13827 this.cancelExpand();
13830 onNodeOver : function(n, dd, e, data)
13833 var pt = this.getDropPoint(e, n, dd);
13836 // auto node expand check
13837 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13838 this.queueExpand(node);
13839 }else if(pt != "append"){
13840 this.cancelExpand();
13843 // set the insert point style on the target node
13844 var returnCls = this.dropNotAllowed;
13845 if(this.isValidDropPoint(n, pt, dd, e, data)){
13850 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13851 cls = "x-tree-drag-insert-above";
13852 }else if(pt == "below"){
13853 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13854 cls = "x-tree-drag-insert-below";
13856 returnCls = "x-tree-drop-ok-append";
13857 cls = "x-tree-drag-append";
13859 if(this.lastInsertClass != cls){
13860 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13861 this.lastInsertClass = cls;
13868 onNodeOut : function(n, dd, e, data){
13870 this.cancelExpand();
13871 this.removeDropIndicators(n);
13874 onNodeDrop : function(n, dd, e, data){
13875 var point = this.getDropPoint(e, n, dd);
13876 var targetNode = n.node;
13877 targetNode.ui.startDrop();
13878 if(!this.isValidDropPoint(n, point, dd, e, data)){
13879 targetNode.ui.endDrop();
13882 // first try to find the drop node
13883 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13886 target: targetNode,
13891 dropNode: dropNode,
13894 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13895 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13896 targetNode.ui.endDrop();
13899 // allow target changing
13900 targetNode = dropEvent.target;
13901 if(point == "append" && !targetNode.isExpanded()){
13902 targetNode.expand(false, null, function(){
13903 this.completeDrop(dropEvent);
13904 }.createDelegate(this));
13906 this.completeDrop(dropEvent);
13911 completeDrop : function(de){
13912 var ns = de.dropNode, p = de.point, t = de.target;
13913 if(!(ns instanceof Array)){
13917 for(var i = 0, len = ns.length; i < len; i++){
13920 t.parentNode.insertBefore(n, t);
13921 }else if(p == "below"){
13922 t.parentNode.insertBefore(n, t.nextSibling);
13928 if(this.tree.hlDrop){
13932 this.tree.fireEvent("nodedrop", de);
13935 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13936 if(this.tree.hlDrop){
13937 dropNode.ui.focus();
13938 dropNode.ui.highlight();
13940 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13943 getTree : function(){
13947 removeDropIndicators : function(n){
13950 Roo.fly(el).removeClass([
13951 "x-tree-drag-insert-above",
13952 "x-tree-drag-insert-below",
13953 "x-tree-drag-append"]);
13954 this.lastInsertClass = "_noclass";
13958 beforeDragDrop : function(target, e, id){
13959 this.cancelExpand();
13963 afterRepair : function(data){
13964 if(data && Roo.enableFx){
13965 data.node.ui.highlight();
13975 * Ext JS Library 1.1.1
13976 * Copyright(c) 2006-2007, Ext JS, LLC.
13978 * Originally Released Under LGPL - original licence link has changed is not relivant.
13981 * <script type="text/javascript">
13985 if(Roo.dd.DragZone){
13986 Roo.tree.TreeDragZone = function(tree, config){
13987 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13991 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13992 ddGroup : "TreeDD",
13994 onBeforeDrag : function(data, e){
13996 return n && n.draggable && !n.disabled;
14000 onInitDrag : function(e){
14001 var data = this.dragData;
14002 this.tree.getSelectionModel().select(data.node);
14003 this.proxy.update("");
14004 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14005 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14008 getRepairXY : function(e, data){
14009 return data.node.ui.getDDRepairXY();
14012 onEndDrag : function(data, e){
14013 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14018 onValidDrop : function(dd, e, id){
14019 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14023 beforeInvalidDrop : function(e, id){
14024 // this scrolls the original position back into view
14025 var sm = this.tree.getSelectionModel();
14026 sm.clearSelections();
14027 sm.select(this.dragData.node);
14032 * Ext JS Library 1.1.1
14033 * Copyright(c) 2006-2007, Ext JS, LLC.
14035 * Originally Released Under LGPL - original licence link has changed is not relivant.
14038 * <script type="text/javascript">
14041 * @class Roo.tree.TreeEditor
14042 * @extends Roo.Editor
14043 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14044 * as the editor field.
14046 * @param {Object} config (used to be the tree panel.)
14047 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14049 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14050 * @cfg {Roo.form.TextField} field [required] The field configuration
14054 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14057 if (oldconfig) { // old style..
14058 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14061 tree = config.tree;
14062 config.field = config.field || {};
14063 config.field.xtype = 'TextField';
14064 field = Roo.factory(config.field, Roo.form);
14066 config = config || {};
14071 * @event beforenodeedit
14072 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14073 * false from the handler of this event.
14074 * @param {Editor} this
14075 * @param {Roo.tree.Node} node
14077 "beforenodeedit" : true
14081 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14085 tree.on('beforeclick', this.beforeNodeClick, this);
14086 tree.getTreeEl().on('mousedown', this.hide, this);
14087 this.on('complete', this.updateNode, this);
14088 this.on('beforestartedit', this.fitToTree, this);
14089 this.on('startedit', this.bindScroll, this, {delay:10});
14090 this.on('specialkey', this.onSpecialKey, this);
14093 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14095 * @cfg {String} alignment
14096 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14102 * @cfg {Boolean} hideEl
14103 * True to hide the bound element while the editor is displayed (defaults to false)
14107 * @cfg {String} cls
14108 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14110 cls: "x-small-editor x-tree-editor",
14112 * @cfg {Boolean} shim
14113 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14119 * @cfg {Number} maxWidth
14120 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14121 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14122 * scroll and client offsets into account prior to each edit.
14129 fitToTree : function(ed, el){
14130 var td = this.tree.getTreeEl().dom, nd = el.dom;
14131 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14132 td.scrollLeft = nd.offsetLeft;
14136 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14137 this.setSize(w, '');
14139 return this.fireEvent('beforenodeedit', this, this.editNode);
14144 triggerEdit : function(node){
14145 this.completeEdit();
14146 this.editNode = node;
14147 this.startEdit(node.ui.textNode, node.text);
14151 bindScroll : function(){
14152 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14156 beforeNodeClick : function(node, e){
14157 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14158 this.lastClick = new Date();
14159 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14161 this.triggerEdit(node);
14168 updateNode : function(ed, value){
14169 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14170 this.editNode.setText(value);
14174 onHide : function(){
14175 Roo.tree.TreeEditor.superclass.onHide.call(this);
14177 this.editNode.ui.focus();
14182 onSpecialKey : function(field, e){
14183 var k = e.getKey();
14187 }else if(k == e.ENTER && !e.hasModifier()){
14189 this.completeEdit();
14192 });//<Script type="text/javascript">
14195 * Ext JS Library 1.1.1
14196 * Copyright(c) 2006-2007, Ext JS, LLC.
14198 * Originally Released Under LGPL - original licence link has changed is not relivant.
14201 * <script type="text/javascript">
14205 * Not documented??? - probably should be...
14208 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14209 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14211 renderElements : function(n, a, targetNode, bulkRender){
14212 //consel.log("renderElements?");
14213 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14215 var t = n.getOwnerTree();
14216 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14218 var cols = t.columns;
14219 var bw = t.borderWidth;
14221 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14222 var cb = typeof a.checked == "boolean";
14223 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14224 var colcls = 'x-t-' + tid + '-c0';
14226 '<li class="x-tree-node">',
14229 '<div class="x-tree-node-el ', a.cls,'">',
14231 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14234 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14235 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14236 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14237 (a.icon ? ' x-tree-node-inline-icon' : ''),
14238 (a.iconCls ? ' '+a.iconCls : ''),
14239 '" unselectable="on" />',
14240 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14241 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14243 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14244 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14245 '<span unselectable="on" qtip="' + tx + '">',
14249 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14250 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14252 for(var i = 1, len = cols.length; i < len; i++){
14254 colcls = 'x-t-' + tid + '-c' +i;
14255 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14256 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14257 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14263 '<div class="x-clear"></div></div>',
14264 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14267 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14268 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14269 n.nextSibling.ui.getEl(), buf.join(""));
14271 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14273 var el = this.wrap.firstChild;
14275 this.elNode = el.firstChild;
14276 this.ranchor = el.childNodes[1];
14277 this.ctNode = this.wrap.childNodes[1];
14278 var cs = el.firstChild.childNodes;
14279 this.indentNode = cs[0];
14280 this.ecNode = cs[1];
14281 this.iconNode = cs[2];
14284 this.checkbox = cs[3];
14287 this.anchor = cs[index];
14289 this.textNode = cs[index].firstChild;
14291 //el.on("click", this.onClick, this);
14292 //el.on("dblclick", this.onDblClick, this);
14295 // console.log(this);
14297 initEvents : function(){
14298 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14301 var a = this.ranchor;
14303 var el = Roo.get(a);
14305 if(Roo.isOpera){ // opera render bug ignores the CSS
14306 el.setStyle("text-decoration", "none");
14309 el.on("click", this.onClick, this);
14310 el.on("dblclick", this.onDblClick, this);
14311 el.on("contextmenu", this.onContextMenu, this);
14315 /*onSelectedChange : function(state){
14318 this.addClass("x-tree-selected");
14321 this.removeClass("x-tree-selected");
14324 addClass : function(cls){
14326 Roo.fly(this.elRow).addClass(cls);
14332 removeClass : function(cls){
14334 Roo.fly(this.elRow).removeClass(cls);
14340 });//<Script type="text/javascript">
14344 * Ext JS Library 1.1.1
14345 * Copyright(c) 2006-2007, Ext JS, LLC.
14347 * Originally Released Under LGPL - original licence link has changed is not relivant.
14350 * <script type="text/javascript">
14355 * @class Roo.tree.ColumnTree
14356 * @extends Roo.tree.TreePanel
14357 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14358 * @cfg {int} borderWidth compined right/left border allowance
14360 * @param {String/HTMLElement/Element} el The container element
14361 * @param {Object} config
14363 Roo.tree.ColumnTree = function(el, config)
14365 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14369 * Fire this event on a container when it resizes
14370 * @param {int} w Width
14371 * @param {int} h Height
14375 this.on('resize', this.onResize, this);
14378 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14382 borderWidth: Roo.isBorderBox ? 0 : 2,
14385 render : function(){
14386 // add the header.....
14388 Roo.tree.ColumnTree.superclass.render.apply(this);
14390 this.el.addClass('x-column-tree');
14392 this.headers = this.el.createChild(
14393 {cls:'x-tree-headers'},this.innerCt.dom);
14395 var cols = this.columns, c;
14396 var totalWidth = 0;
14398 var len = cols.length;
14399 for(var i = 0; i < len; i++){
14401 totalWidth += c.width;
14402 this.headEls.push(this.headers.createChild({
14403 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14405 cls:'x-tree-hd-text',
14408 style:'width:'+(c.width-this.borderWidth)+'px;'
14411 this.headers.createChild({cls:'x-clear'});
14412 // prevent floats from wrapping when clipped
14413 this.headers.setWidth(totalWidth);
14414 //this.innerCt.setWidth(totalWidth);
14415 this.innerCt.setStyle({ overflow: 'auto' });
14416 this.onResize(this.width, this.height);
14420 onResize : function(w,h)
14425 this.innerCt.setWidth(this.width);
14426 this.innerCt.setHeight(this.height-20);
14429 var cols = this.columns, c;
14430 var totalWidth = 0;
14432 var len = cols.length;
14433 for(var i = 0; i < len; i++){
14435 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14436 // it's the expander..
14437 expEl = this.headEls[i];
14440 totalWidth += c.width;
14444 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14446 this.headers.setWidth(w-20);
14455 * Ext JS Library 1.1.1
14456 * Copyright(c) 2006-2007, Ext JS, LLC.
14458 * Originally Released Under LGPL - original licence link has changed is not relivant.
14461 * <script type="text/javascript">
14465 * @class Roo.menu.Menu
14466 * @extends Roo.util.Observable
14467 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14468 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14469 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14471 * Creates a new Menu
14472 * @param {Object} config Configuration options
14474 Roo.menu.Menu = function(config){
14476 Roo.menu.Menu.superclass.constructor.call(this, config);
14478 this.id = this.id || Roo.id();
14481 * @event beforeshow
14482 * Fires before this menu is displayed
14483 * @param {Roo.menu.Menu} this
14487 * @event beforehide
14488 * Fires before this menu is hidden
14489 * @param {Roo.menu.Menu} this
14494 * Fires after this menu is displayed
14495 * @param {Roo.menu.Menu} this
14500 * Fires after this menu is hidden
14501 * @param {Roo.menu.Menu} this
14506 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14507 * @param {Roo.menu.Menu} this
14508 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14509 * @param {Roo.EventObject} e
14514 * Fires when the mouse is hovering over this menu
14515 * @param {Roo.menu.Menu} this
14516 * @param {Roo.EventObject} e
14517 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14522 * Fires when the mouse exits this menu
14523 * @param {Roo.menu.Menu} this
14524 * @param {Roo.EventObject} e
14525 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14530 * Fires when a menu item contained in this menu is clicked
14531 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14532 * @param {Roo.EventObject} e
14536 if (this.registerMenu) {
14537 Roo.menu.MenuMgr.register(this);
14540 var mis = this.items;
14541 this.items = new Roo.util.MixedCollection();
14543 this.add.apply(this, mis);
14547 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14549 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14553 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14554 * for bottom-right shadow (defaults to "sides")
14558 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14559 * this menu (defaults to "tl-tr?")
14561 subMenuAlign : "tl-tr?",
14563 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14564 * relative to its element of origin (defaults to "tl-bl?")
14566 defaultAlign : "tl-bl?",
14568 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14570 allowOtherMenus : false,
14572 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14574 registerMenu : true,
14579 render : function(){
14583 var el = this.el = new Roo.Layer({
14585 shadow:this.shadow,
14587 parentEl: this.parentEl || document.body,
14591 this.keyNav = new Roo.menu.MenuNav(this);
14594 el.addClass("x-menu-plain");
14597 el.addClass(this.cls);
14599 // generic focus element
14600 this.focusEl = el.createChild({
14601 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14603 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14604 //disabling touch- as it's causing issues ..
14605 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14606 ul.on('click' , this.onClick, this);
14609 ul.on("mouseover", this.onMouseOver, this);
14610 ul.on("mouseout", this.onMouseOut, this);
14611 this.items.each(function(item){
14616 var li = document.createElement("li");
14617 li.className = "x-menu-list-item";
14618 ul.dom.appendChild(li);
14619 item.render(li, this);
14626 autoWidth : function(){
14627 var el = this.el, ul = this.ul;
14631 var w = this.width;
14634 }else if(Roo.isIE){
14635 el.setWidth(this.minWidth);
14636 var t = el.dom.offsetWidth; // force recalc
14637 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14642 delayAutoWidth : function(){
14645 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14647 this.awTask.delay(20);
14652 findTargetItem : function(e){
14653 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14654 if(t && t.menuItemId){
14655 return this.items.get(t.menuItemId);
14660 onClick : function(e){
14661 Roo.log("menu.onClick");
14662 var t = this.findTargetItem(e);
14667 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14668 if(t == this.activeItem && t.shouldDeactivate(e)){
14669 this.activeItem.deactivate();
14670 delete this.activeItem;
14674 this.setActiveItem(t, true);
14682 this.fireEvent("click", this, t, e);
14686 setActiveItem : function(item, autoExpand){
14687 if(item != this.activeItem){
14688 if(this.activeItem){
14689 this.activeItem.deactivate();
14691 this.activeItem = item;
14692 item.activate(autoExpand);
14693 }else if(autoExpand){
14699 tryActivate : function(start, step){
14700 var items = this.items;
14701 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14702 var item = items.get(i);
14703 if(!item.disabled && item.canActivate){
14704 this.setActiveItem(item, false);
14712 onMouseOver : function(e){
14714 if(t = this.findTargetItem(e)){
14715 if(t.canActivate && !t.disabled){
14716 this.setActiveItem(t, true);
14719 this.fireEvent("mouseover", this, e, t);
14723 onMouseOut : function(e){
14725 if(t = this.findTargetItem(e)){
14726 if(t == this.activeItem && t.shouldDeactivate(e)){
14727 this.activeItem.deactivate();
14728 delete this.activeItem;
14731 this.fireEvent("mouseout", this, e, t);
14735 * Read-only. Returns true if the menu is currently displayed, else false.
14738 isVisible : function(){
14739 return this.el && !this.hidden;
14743 * Displays this menu relative to another element
14744 * @param {String/HTMLElement/Roo.Element} element The element to align to
14745 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14746 * the element (defaults to this.defaultAlign)
14747 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14749 show : function(el, pos, parentMenu){
14750 this.parentMenu = parentMenu;
14754 this.fireEvent("beforeshow", this);
14755 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14759 * Displays this menu at a specific xy position
14760 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14761 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14763 showAt : function(xy, parentMenu, /* private: */_e){
14764 this.parentMenu = parentMenu;
14769 this.fireEvent("beforeshow", this);
14770 xy = this.el.adjustForConstraints(xy);
14774 this.hidden = false;
14776 this.fireEvent("show", this);
14779 focus : function(){
14781 this.doFocus.defer(50, this);
14785 doFocus : function(){
14787 this.focusEl.focus();
14792 * Hides this menu and optionally all parent menus
14793 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14795 hide : function(deep){
14796 if(this.el && this.isVisible()){
14797 this.fireEvent("beforehide", this);
14798 if(this.activeItem){
14799 this.activeItem.deactivate();
14800 this.activeItem = null;
14803 this.hidden = true;
14804 this.fireEvent("hide", this);
14806 if(deep === true && this.parentMenu){
14807 this.parentMenu.hide(true);
14812 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14813 * Any of the following are valid:
14815 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14816 * <li>An HTMLElement object which will be converted to a menu item</li>
14817 * <li>A menu item config object that will be created as a new menu item</li>
14818 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14819 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14824 var menu = new Roo.menu.Menu();
14826 // Create a menu item to add by reference
14827 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14829 // Add a bunch of items at once using different methods.
14830 // Only the last item added will be returned.
14831 var item = menu.add(
14832 menuItem, // add existing item by ref
14833 'Dynamic Item', // new TextItem
14834 '-', // new separator
14835 { text: 'Config Item' } // new item by config
14838 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14839 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14842 var a = arguments, l = a.length, item;
14843 for(var i = 0; i < l; i++){
14845 if ((typeof(el) == "object") && el.xtype && el.xns) {
14846 el = Roo.factory(el, Roo.menu);
14849 if(el.render){ // some kind of Item
14850 item = this.addItem(el);
14851 }else if(typeof el == "string"){ // string
14852 if(el == "separator" || el == "-"){
14853 item = this.addSeparator();
14855 item = this.addText(el);
14857 }else if(el.tagName || el.el){ // element
14858 item = this.addElement(el);
14859 }else if(typeof el == "object"){ // must be menu item config?
14860 item = this.addMenuItem(el);
14867 * Returns this menu's underlying {@link Roo.Element} object
14868 * @return {Roo.Element} The element
14870 getEl : function(){
14878 * Adds a separator bar to the menu
14879 * @return {Roo.menu.Item} The menu item that was added
14881 addSeparator : function(){
14882 return this.addItem(new Roo.menu.Separator());
14886 * Adds an {@link Roo.Element} object to the menu
14887 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14888 * @return {Roo.menu.Item} The menu item that was added
14890 addElement : function(el){
14891 return this.addItem(new Roo.menu.BaseItem(el));
14895 * Adds an existing object based on {@link Roo.menu.Item} to the menu
14896 * @param {Roo.menu.Item} item The menu item to add
14897 * @return {Roo.menu.Item} The menu item that was added
14899 addItem : function(item){
14900 this.items.add(item);
14902 var li = document.createElement("li");
14903 li.className = "x-menu-list-item";
14904 this.ul.dom.appendChild(li);
14905 item.render(li, this);
14906 this.delayAutoWidth();
14912 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14913 * @param {Object} config A MenuItem config object
14914 * @return {Roo.menu.Item} The menu item that was added
14916 addMenuItem : function(config){
14917 if(!(config instanceof Roo.menu.Item)){
14918 if(typeof config.checked == "boolean"){ // must be check menu item config?
14919 config = new Roo.menu.CheckItem(config);
14921 config = new Roo.menu.Item(config);
14924 return this.addItem(config);
14928 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14929 * @param {String} text The text to display in the menu item
14930 * @return {Roo.menu.Item} The menu item that was added
14932 addText : function(text){
14933 return this.addItem(new Roo.menu.TextItem({ text : text }));
14937 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14938 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14939 * @param {Roo.menu.Item} item The menu item to add
14940 * @return {Roo.menu.Item} The menu item that was added
14942 insert : function(index, item){
14943 this.items.insert(index, item);
14945 var li = document.createElement("li");
14946 li.className = "x-menu-list-item";
14947 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14948 item.render(li, this);
14949 this.delayAutoWidth();
14955 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14956 * @param {Roo.menu.Item} item The menu item to remove
14958 remove : function(item){
14959 this.items.removeKey(item.id);
14964 * Removes and destroys all items in the menu
14966 removeAll : function(){
14968 while(f = this.items.first()){
14974 // MenuNav is a private utility class used internally by the Menu
14975 Roo.menu.MenuNav = function(menu){
14976 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14977 this.scope = this.menu = menu;
14980 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14981 doRelay : function(e, h){
14982 var k = e.getKey();
14983 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14984 this.menu.tryActivate(0, 1);
14987 return h.call(this.scope || this, e, this.menu);
14990 up : function(e, m){
14991 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14992 m.tryActivate(m.items.length-1, -1);
14996 down : function(e, m){
14997 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14998 m.tryActivate(0, 1);
15002 right : function(e, m){
15004 m.activeItem.expandMenu(true);
15008 left : function(e, m){
15010 if(m.parentMenu && m.parentMenu.activeItem){
15011 m.parentMenu.activeItem.activate();
15015 enter : function(e, m){
15017 e.stopPropagation();
15018 m.activeItem.onClick(e);
15019 m.fireEvent("click", this, m.activeItem);
15025 * Ext JS Library 1.1.1
15026 * Copyright(c) 2006-2007, Ext JS, LLC.
15028 * Originally Released Under LGPL - original licence link has changed is not relivant.
15031 * <script type="text/javascript">
15035 * @class Roo.menu.MenuMgr
15036 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15039 Roo.menu.MenuMgr = function(){
15040 var menus, active, groups = {}, attached = false, lastShow = new Date();
15042 // private - called when first menu is created
15045 active = new Roo.util.MixedCollection();
15046 Roo.get(document).addKeyListener(27, function(){
15047 if(active.length > 0){
15054 function hideAll(){
15055 if(active && active.length > 0){
15056 var c = active.clone();
15057 c.each(function(m){
15064 function onHide(m){
15066 if(active.length < 1){
15067 Roo.get(document).un("mousedown", onMouseDown);
15073 function onShow(m){
15074 var last = active.last();
15075 lastShow = new Date();
15078 Roo.get(document).on("mousedown", onMouseDown);
15082 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15083 m.parentMenu.activeChild = m;
15084 }else if(last && last.isVisible()){
15085 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15090 function onBeforeHide(m){
15092 m.activeChild.hide();
15094 if(m.autoHideTimer){
15095 clearTimeout(m.autoHideTimer);
15096 delete m.autoHideTimer;
15101 function onBeforeShow(m){
15102 var pm = m.parentMenu;
15103 if(!pm && !m.allowOtherMenus){
15105 }else if(pm && pm.activeChild && active != m){
15106 pm.activeChild.hide();
15111 function onMouseDown(e){
15112 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15118 function onBeforeCheck(mi, state){
15120 var g = groups[mi.group];
15121 for(var i = 0, l = g.length; i < l; i++){
15123 g[i].setChecked(false);
15132 * Hides all menus that are currently visible
15134 hideAll : function(){
15139 register : function(menu){
15143 menus[menu.id] = menu;
15144 menu.on("beforehide", onBeforeHide);
15145 menu.on("hide", onHide);
15146 menu.on("beforeshow", onBeforeShow);
15147 menu.on("show", onShow);
15148 var g = menu.group;
15149 if(g && menu.events["checkchange"]){
15153 groups[g].push(menu);
15154 menu.on("checkchange", onCheck);
15159 * Returns a {@link Roo.menu.Menu} object
15160 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15161 * be used to generate and return a new Menu instance.
15163 get : function(menu){
15164 if(typeof menu == "string"){ // menu id
15165 return menus[menu];
15166 }else if(menu.events){ // menu instance
15168 }else if(typeof menu.length == 'number'){ // array of menu items?
15169 return new Roo.menu.Menu({items:menu});
15170 }else{ // otherwise, must be a config
15171 return new Roo.menu.Menu(menu);
15176 unregister : function(menu){
15177 delete menus[menu.id];
15178 menu.un("beforehide", onBeforeHide);
15179 menu.un("hide", onHide);
15180 menu.un("beforeshow", onBeforeShow);
15181 menu.un("show", onShow);
15182 var g = menu.group;
15183 if(g && menu.events["checkchange"]){
15184 groups[g].remove(menu);
15185 menu.un("checkchange", onCheck);
15190 registerCheckable : function(menuItem){
15191 var g = menuItem.group;
15196 groups[g].push(menuItem);
15197 menuItem.on("beforecheckchange", onBeforeCheck);
15202 unregisterCheckable : function(menuItem){
15203 var g = menuItem.group;
15205 groups[g].remove(menuItem);
15206 menuItem.un("beforecheckchange", onBeforeCheck);
15212 * Ext JS Library 1.1.1
15213 * Copyright(c) 2006-2007, Ext JS, LLC.
15215 * Originally Released Under LGPL - original licence link has changed is not relivant.
15218 * <script type="text/javascript">
15223 * @class Roo.menu.BaseItem
15224 * @extends Roo.Component
15226 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15227 * management and base configuration options shared by all menu components.
15229 * Creates a new BaseItem
15230 * @param {Object} config Configuration options
15232 Roo.menu.BaseItem = function(config){
15233 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15238 * Fires when this item is clicked
15239 * @param {Roo.menu.BaseItem} this
15240 * @param {Roo.EventObject} e
15245 * Fires when this item is activated
15246 * @param {Roo.menu.BaseItem} this
15250 * @event deactivate
15251 * Fires when this item is deactivated
15252 * @param {Roo.menu.BaseItem} this
15258 this.on("click", this.handler, this.scope, true);
15262 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15264 * @cfg {Function} handler
15265 * A function that will handle the click event of this menu item (defaults to undefined)
15268 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15270 canActivate : false,
15273 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15278 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15280 activeClass : "x-menu-item-active",
15282 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15284 hideOnClick : true,
15286 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15291 ctype: "Roo.menu.BaseItem",
15294 actionMode : "container",
15297 render : function(container, parentMenu){
15298 this.parentMenu = parentMenu;
15299 Roo.menu.BaseItem.superclass.render.call(this, container);
15300 this.container.menuItemId = this.id;
15304 onRender : function(container, position){
15305 this.el = Roo.get(this.el);
15306 container.dom.appendChild(this.el.dom);
15310 onClick : function(e){
15311 if(!this.disabled && this.fireEvent("click", this, e) !== false
15312 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15313 this.handleClick(e);
15320 activate : function(){
15324 var li = this.container;
15325 li.addClass(this.activeClass);
15326 this.region = li.getRegion().adjust(2, 2, -2, -2);
15327 this.fireEvent("activate", this);
15332 deactivate : function(){
15333 this.container.removeClass(this.activeClass);
15334 this.fireEvent("deactivate", this);
15338 shouldDeactivate : function(e){
15339 return !this.region || !this.region.contains(e.getPoint());
15343 handleClick : function(e){
15344 if(this.hideOnClick){
15345 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15350 expandMenu : function(autoActivate){
15355 hideMenu : function(){
15360 * Ext JS Library 1.1.1
15361 * Copyright(c) 2006-2007, Ext JS, LLC.
15363 * Originally Released Under LGPL - original licence link has changed is not relivant.
15366 * <script type="text/javascript">
15370 * @class Roo.menu.Adapter
15371 * @extends Roo.menu.BaseItem
15373 * 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.
15374 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15376 * Creates a new Adapter
15377 * @param {Object} config Configuration options
15379 Roo.menu.Adapter = function(component, config){
15380 Roo.menu.Adapter.superclass.constructor.call(this, config);
15381 this.component = component;
15383 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15385 canActivate : true,
15388 onRender : function(container, position){
15389 this.component.render(container);
15390 this.el = this.component.getEl();
15394 activate : function(){
15398 this.component.focus();
15399 this.fireEvent("activate", this);
15404 deactivate : function(){
15405 this.fireEvent("deactivate", this);
15409 disable : function(){
15410 this.component.disable();
15411 Roo.menu.Adapter.superclass.disable.call(this);
15415 enable : function(){
15416 this.component.enable();
15417 Roo.menu.Adapter.superclass.enable.call(this);
15421 * Ext JS Library 1.1.1
15422 * Copyright(c) 2006-2007, Ext JS, LLC.
15424 * Originally Released Under LGPL - original licence link has changed is not relivant.
15427 * <script type="text/javascript">
15431 * @class Roo.menu.TextItem
15432 * @extends Roo.menu.BaseItem
15433 * Adds a static text string to a menu, usually used as either a heading or group separator.
15434 * Note: old style constructor with text is still supported.
15437 * Creates a new TextItem
15438 * @param {Object} cfg Configuration
15440 Roo.menu.TextItem = function(cfg){
15441 if (typeof(cfg) == 'string') {
15444 Roo.apply(this,cfg);
15447 Roo.menu.TextItem.superclass.constructor.call(this);
15450 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15452 * @cfg {String} text Text to show on item.
15457 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15459 hideOnClick : false,
15461 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15463 itemCls : "x-menu-text",
15466 onRender : function(){
15467 var s = document.createElement("span");
15468 s.className = this.itemCls;
15469 s.innerHTML = this.text;
15471 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15475 * Ext JS Library 1.1.1
15476 * Copyright(c) 2006-2007, Ext JS, LLC.
15478 * Originally Released Under LGPL - original licence link has changed is not relivant.
15481 * <script type="text/javascript">
15485 * @class Roo.menu.Separator
15486 * @extends Roo.menu.BaseItem
15487 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15488 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15490 * @param {Object} config Configuration options
15492 Roo.menu.Separator = function(config){
15493 Roo.menu.Separator.superclass.constructor.call(this, config);
15496 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15498 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15500 itemCls : "x-menu-sep",
15502 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15504 hideOnClick : false,
15507 onRender : function(li){
15508 var s = document.createElement("span");
15509 s.className = this.itemCls;
15510 s.innerHTML = " ";
15512 li.addClass("x-menu-sep-li");
15513 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15517 * Ext JS Library 1.1.1
15518 * Copyright(c) 2006-2007, Ext JS, LLC.
15520 * Originally Released Under LGPL - original licence link has changed is not relivant.
15523 * <script type="text/javascript">
15526 * @class Roo.menu.Item
15527 * @extends Roo.menu.BaseItem
15528 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15529 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15530 * activation and click handling.
15532 * Creates a new Item
15533 * @param {Object} config Configuration options
15535 Roo.menu.Item = function(config){
15536 Roo.menu.Item.superclass.constructor.call(this, config);
15538 this.menu = Roo.menu.MenuMgr.get(this.menu);
15541 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15543 * @cfg {Roo.menu.Menu} menu
15547 * @cfg {String} text
15548 * The text to show on the menu item.
15552 * @cfg {String} html to render in menu
15553 * The text to show on the menu item (HTML version).
15557 * @cfg {String} icon
15558 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15562 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15564 itemCls : "x-menu-item",
15566 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15568 canActivate : true,
15570 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15573 // doc'd in BaseItem
15577 ctype: "Roo.menu.Item",
15580 onRender : function(container, position){
15581 var el = document.createElement("a");
15582 el.hideFocus = true;
15583 el.unselectable = "on";
15584 el.href = this.href || "#";
15585 if(this.hrefTarget){
15586 el.target = this.hrefTarget;
15588 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15590 var html = this.html.length ? this.html : String.format('{0}',this.text);
15592 el.innerHTML = String.format(
15593 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15594 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15596 Roo.menu.Item.superclass.onRender.call(this, container, position);
15600 * Sets the text to display in this menu item
15601 * @param {String} text The text to display
15602 * @param {Boolean} isHTML true to indicate text is pure html.
15604 setText : function(text, isHTML){
15612 var html = this.html.length ? this.html : String.format('{0}',this.text);
15614 this.el.update(String.format(
15615 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15616 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15617 this.parentMenu.autoWidth();
15622 handleClick : function(e){
15623 if(!this.href){ // if no link defined, stop the event automatically
15626 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15630 activate : function(autoExpand){
15631 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15641 shouldDeactivate : function(e){
15642 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15643 if(this.menu && this.menu.isVisible()){
15644 return !this.menu.getEl().getRegion().contains(e.getPoint());
15652 deactivate : function(){
15653 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15658 expandMenu : function(autoActivate){
15659 if(!this.disabled && this.menu){
15660 clearTimeout(this.hideTimer);
15661 delete this.hideTimer;
15662 if(!this.menu.isVisible() && !this.showTimer){
15663 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15664 }else if (this.menu.isVisible() && autoActivate){
15665 this.menu.tryActivate(0, 1);
15671 deferExpand : function(autoActivate){
15672 delete this.showTimer;
15673 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15675 this.menu.tryActivate(0, 1);
15680 hideMenu : function(){
15681 clearTimeout(this.showTimer);
15682 delete this.showTimer;
15683 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15684 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15689 deferHide : function(){
15690 delete this.hideTimer;
15695 * Ext JS Library 1.1.1
15696 * Copyright(c) 2006-2007, Ext JS, LLC.
15698 * Originally Released Under LGPL - original licence link has changed is not relivant.
15701 * <script type="text/javascript">
15705 * @class Roo.menu.CheckItem
15706 * @extends Roo.menu.Item
15707 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15709 * Creates a new CheckItem
15710 * @param {Object} config Configuration options
15712 Roo.menu.CheckItem = function(config){
15713 Roo.menu.CheckItem.superclass.constructor.call(this, config);
15716 * @event beforecheckchange
15717 * Fires before the checked value is set, providing an opportunity to cancel if needed
15718 * @param {Roo.menu.CheckItem} this
15719 * @param {Boolean} checked The new checked value that will be set
15721 "beforecheckchange" : true,
15723 * @event checkchange
15724 * Fires after the checked value has been set
15725 * @param {Roo.menu.CheckItem} this
15726 * @param {Boolean} checked The checked value that was set
15728 "checkchange" : true
15730 if(this.checkHandler){
15731 this.on('checkchange', this.checkHandler, this.scope);
15734 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15736 * @cfg {String} group
15737 * All check items with the same group name will automatically be grouped into a single-select
15738 * radio button group (defaults to '')
15741 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15743 itemCls : "x-menu-item x-menu-check-item",
15745 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15747 groupClass : "x-menu-group-item",
15750 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
15751 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15752 * initialized with checked = true will be rendered as checked.
15757 ctype: "Roo.menu.CheckItem",
15760 onRender : function(c){
15761 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15763 this.el.addClass(this.groupClass);
15765 Roo.menu.MenuMgr.registerCheckable(this);
15767 this.checked = false;
15768 this.setChecked(true, true);
15773 destroy : function(){
15775 Roo.menu.MenuMgr.unregisterCheckable(this);
15777 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15781 * Set the checked state of this item
15782 * @param {Boolean} checked The new checked value
15783 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15785 setChecked : function(state, suppressEvent){
15786 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15787 if(this.container){
15788 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15790 this.checked = state;
15791 if(suppressEvent !== true){
15792 this.fireEvent("checkchange", this, state);
15798 handleClick : function(e){
15799 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15800 this.setChecked(!this.checked);
15802 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15806 * Ext JS Library 1.1.1
15807 * Copyright(c) 2006-2007, Ext JS, LLC.
15809 * Originally Released Under LGPL - original licence link has changed is not relivant.
15812 * <script type="text/javascript">
15816 * @class Roo.menu.DateItem
15817 * @extends Roo.menu.Adapter
15818 * A menu item that wraps the {@link Roo.DatPicker} component.
15820 * Creates a new DateItem
15821 * @param {Object} config Configuration options
15823 Roo.menu.DateItem = function(config){
15824 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15825 /** The Roo.DatePicker object @type Roo.DatePicker */
15826 this.picker = this.component;
15827 this.addEvents({select: true});
15829 this.picker.on("render", function(picker){
15830 picker.getEl().swallowEvent("click");
15831 picker.container.addClass("x-menu-date-item");
15834 this.picker.on("select", this.onSelect, this);
15837 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15839 onSelect : function(picker, date){
15840 this.fireEvent("select", this, date, picker);
15841 Roo.menu.DateItem.superclass.handleClick.call(this);
15845 * Ext JS Library 1.1.1
15846 * Copyright(c) 2006-2007, Ext JS, LLC.
15848 * Originally Released Under LGPL - original licence link has changed is not relivant.
15851 * <script type="text/javascript">
15855 * @class Roo.menu.ColorItem
15856 * @extends Roo.menu.Adapter
15857 * A menu item that wraps the {@link Roo.ColorPalette} component.
15859 * Creates a new ColorItem
15860 * @param {Object} config Configuration options
15862 Roo.menu.ColorItem = function(config){
15863 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15864 /** The Roo.ColorPalette object @type Roo.ColorPalette */
15865 this.palette = this.component;
15866 this.relayEvents(this.palette, ["select"]);
15867 if(this.selectHandler){
15868 this.on('select', this.selectHandler, this.scope);
15871 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15873 * Ext JS Library 1.1.1
15874 * Copyright(c) 2006-2007, Ext JS, LLC.
15876 * Originally Released Under LGPL - original licence link has changed is not relivant.
15879 * <script type="text/javascript">
15884 * @class Roo.menu.DateMenu
15885 * @extends Roo.menu.Menu
15886 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15888 * Creates a new DateMenu
15889 * @param {Object} config Configuration options
15891 Roo.menu.DateMenu = function(config){
15892 Roo.menu.DateMenu.superclass.constructor.call(this, config);
15894 var di = new Roo.menu.DateItem(config);
15897 * The {@link Roo.DatePicker} instance for this DateMenu
15900 this.picker = di.picker;
15903 * @param {DatePicker} picker
15904 * @param {Date} date
15906 this.relayEvents(di, ["select"]);
15907 this.on('beforeshow', function(){
15909 this.picker.hideMonthPicker(false);
15913 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15917 * Ext JS Library 1.1.1
15918 * Copyright(c) 2006-2007, Ext JS, LLC.
15920 * Originally Released Under LGPL - original licence link has changed is not relivant.
15923 * <script type="text/javascript">
15928 * @class Roo.menu.ColorMenu
15929 * @extends Roo.menu.Menu
15930 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15932 * Creates a new ColorMenu
15933 * @param {Object} config Configuration options
15935 Roo.menu.ColorMenu = function(config){
15936 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15938 var ci = new Roo.menu.ColorItem(config);
15941 * The {@link Roo.ColorPalette} instance for this ColorMenu
15942 * @type ColorPalette
15944 this.palette = ci.palette;
15947 * @param {ColorPalette} palette
15948 * @param {String} color
15950 this.relayEvents(ci, ["select"]);
15952 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15954 * Ext JS Library 1.1.1
15955 * Copyright(c) 2006-2007, Ext JS, LLC.
15957 * Originally Released Under LGPL - original licence link has changed is not relivant.
15960 * <script type="text/javascript">
15964 * @class Roo.form.TextItem
15965 * @extends Roo.BoxComponent
15966 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15968 * Creates a new TextItem
15969 * @param {Object} config Configuration options
15971 Roo.form.TextItem = function(config){
15972 Roo.form.TextItem.superclass.constructor.call(this, config);
15975 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
15978 * @cfg {String} tag the tag for this item (default div)
15982 * @cfg {String} html the content for this item
15986 getAutoCreate : function()
15999 onRender : function(ct, position)
16001 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16004 var cfg = this.getAutoCreate();
16006 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16008 if (!cfg.name.length) {
16011 this.el = ct.createChild(cfg, position);
16016 * @param {String} html update the Contents of the element.
16018 setHTML : function(html)
16020 this.fieldEl.dom.innerHTML = html;
16025 * Ext JS Library 1.1.1
16026 * Copyright(c) 2006-2007, Ext JS, LLC.
16028 * Originally Released Under LGPL - original licence link has changed is not relivant.
16031 * <script type="text/javascript">
16035 * @class Roo.form.Field
16036 * @extends Roo.BoxComponent
16037 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16039 * Creates a new Field
16040 * @param {Object} config Configuration options
16042 Roo.form.Field = function(config){
16043 Roo.form.Field.superclass.constructor.call(this, config);
16046 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16048 * @cfg {String} fieldLabel Label to use when rendering a form.
16051 * @cfg {String} qtip Mouse over tip
16055 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16057 invalidClass : "x-form-invalid",
16059 * @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")
16061 invalidText : "The value in this field is invalid",
16063 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16065 focusClass : "x-form-focus",
16067 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16068 automatic validation (defaults to "keyup").
16070 validationEvent : "keyup",
16072 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16074 validateOnBlur : true,
16076 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16078 validationDelay : 250,
16080 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16081 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16083 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16085 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16087 fieldClass : "x-form-field",
16089 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16092 ----------- ----------------------------------------------------------------------
16093 qtip Display a quick tip when the user hovers over the field
16094 title Display a default browser title attribute popup
16095 under Add a block div beneath the field containing the error text
16096 side Add an error icon to the right of the field with a popup on hover
16097 [element id] Add the error text directly to the innerHTML of the specified element
16100 msgTarget : 'qtip',
16102 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16107 * @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.
16112 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16117 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16119 inputType : undefined,
16122 * @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).
16124 tabIndex : undefined,
16127 isFormField : true,
16132 * @property {Roo.Element} fieldEl
16133 * Element Containing the rendered Field (with label etc.)
16136 * @cfg {Mixed} value A value to initialize this field with.
16141 * @cfg {String} name The field's HTML name attribute.
16144 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16147 loadedValue : false,
16151 initComponent : function(){
16152 Roo.form.Field.superclass.initComponent.call(this);
16156 * Fires when this field receives input focus.
16157 * @param {Roo.form.Field} this
16162 * Fires when this field loses input focus.
16163 * @param {Roo.form.Field} this
16167 * @event specialkey
16168 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16169 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16170 * @param {Roo.form.Field} this
16171 * @param {Roo.EventObject} e The event object
16176 * Fires just before the field blurs if the field value has changed.
16177 * @param {Roo.form.Field} this
16178 * @param {Mixed} newValue The new value
16179 * @param {Mixed} oldValue The original value
16184 * Fires after the field has been marked as invalid.
16185 * @param {Roo.form.Field} this
16186 * @param {String} msg The validation message
16191 * Fires after the field has been validated with no errors.
16192 * @param {Roo.form.Field} this
16197 * Fires after the key up
16198 * @param {Roo.form.Field} this
16199 * @param {Roo.EventObject} e The event Object
16206 * Returns the name attribute of the field if available
16207 * @return {String} name The field name
16209 getName: function(){
16210 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16214 onRender : function(ct, position){
16215 Roo.form.Field.superclass.onRender.call(this, ct, position);
16217 var cfg = this.getAutoCreate();
16219 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16221 if (!cfg.name.length) {
16224 if(this.inputType){
16225 cfg.type = this.inputType;
16227 this.el = ct.createChild(cfg, position);
16229 var type = this.el.dom.type;
16231 if(type == 'password'){
16234 this.el.addClass('x-form-'+type);
16237 this.el.dom.readOnly = true;
16239 if(this.tabIndex !== undefined){
16240 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16243 this.el.addClass([this.fieldClass, this.cls]);
16248 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16249 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16250 * @return {Roo.form.Field} this
16252 applyTo : function(target){
16253 this.allowDomMove = false;
16254 this.el = Roo.get(target);
16255 this.render(this.el.dom.parentNode);
16260 initValue : function(){
16261 if(this.value !== undefined){
16262 this.setValue(this.value);
16263 }else if(this.el.dom.value.length > 0){
16264 this.setValue(this.el.dom.value);
16269 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16270 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16272 isDirty : function() {
16273 if(this.disabled) {
16276 return String(this.getValue()) !== String(this.originalValue);
16280 * stores the current value in loadedValue
16282 resetHasChanged : function()
16284 this.loadedValue = String(this.getValue());
16287 * checks the current value against the 'loaded' value.
16288 * Note - will return false if 'resetHasChanged' has not been called first.
16290 hasChanged : function()
16292 if(this.disabled || this.readOnly) {
16295 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16301 afterRender : function(){
16302 Roo.form.Field.superclass.afterRender.call(this);
16307 fireKey : function(e){
16308 //Roo.log('field ' + e.getKey());
16309 if(e.isNavKeyPress()){
16310 this.fireEvent("specialkey", this, e);
16315 * Resets the current field value to the originally loaded value and clears any validation messages
16317 reset : function(){
16318 this.setValue(this.resetValue);
16319 this.originalValue = this.getValue();
16320 this.clearInvalid();
16324 initEvents : function(){
16325 // safari killled keypress - so keydown is now used..
16326 this.el.on("keydown" , this.fireKey, this);
16327 this.el.on("focus", this.onFocus, this);
16328 this.el.on("blur", this.onBlur, this);
16329 this.el.relayEvent('keyup', this);
16331 // reference to original value for reset
16332 this.originalValue = this.getValue();
16333 this.resetValue = this.getValue();
16337 onFocus : function(){
16338 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16339 this.el.addClass(this.focusClass);
16341 if(!this.hasFocus){
16342 this.hasFocus = true;
16343 this.startValue = this.getValue();
16344 this.fireEvent("focus", this);
16348 beforeBlur : Roo.emptyFn,
16351 onBlur : function(){
16353 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16354 this.el.removeClass(this.focusClass);
16356 this.hasFocus = false;
16357 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16360 var v = this.getValue();
16361 if(String(v) !== String(this.startValue)){
16362 this.fireEvent('change', this, v, this.startValue);
16364 this.fireEvent("blur", this);
16368 * Returns whether or not the field value is currently valid
16369 * @param {Boolean} preventMark True to disable marking the field invalid
16370 * @return {Boolean} True if the value is valid, else false
16372 isValid : function(preventMark){
16376 var restore = this.preventMark;
16377 this.preventMark = preventMark === true;
16378 var v = this.validateValue(this.processValue(this.getRawValue()));
16379 this.preventMark = restore;
16384 * Validates the field value
16385 * @return {Boolean} True if the value is valid, else false
16387 validate : function(){
16388 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16389 this.clearInvalid();
16395 processValue : function(value){
16400 // Subclasses should provide the validation implementation by overriding this
16401 validateValue : function(value){
16406 * Mark this field as invalid
16407 * @param {String} msg The validation message
16409 markInvalid : function(msg){
16410 if(!this.rendered || this.preventMark){ // not rendered
16414 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16416 obj.el.addClass(this.invalidClass);
16417 msg = msg || this.invalidText;
16418 switch(this.msgTarget){
16420 obj.el.dom.qtip = msg;
16421 obj.el.dom.qclass = 'x-form-invalid-tip';
16422 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16423 Roo.QuickTips.enable();
16427 this.el.dom.title = msg;
16431 var elp = this.el.findParent('.x-form-element', 5, true);
16432 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16433 this.errorEl.setWidth(elp.getWidth(true)-20);
16435 this.errorEl.update(msg);
16436 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16439 if(!this.errorIcon){
16440 var elp = this.el.findParent('.x-form-element', 5, true);
16441 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16443 this.alignErrorIcon();
16444 this.errorIcon.dom.qtip = msg;
16445 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16446 this.errorIcon.show();
16447 this.on('resize', this.alignErrorIcon, this);
16450 var t = Roo.getDom(this.msgTarget);
16452 t.style.display = this.msgDisplay;
16455 this.fireEvent('invalid', this, msg);
16459 alignErrorIcon : function(){
16460 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16464 * Clear any invalid styles/messages for this field
16466 clearInvalid : function(){
16467 if(!this.rendered || this.preventMark){ // not rendered
16470 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16472 obj.el.removeClass(this.invalidClass);
16473 switch(this.msgTarget){
16475 obj.el.dom.qtip = '';
16478 this.el.dom.title = '';
16482 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16486 if(this.errorIcon){
16487 this.errorIcon.dom.qtip = '';
16488 this.errorIcon.hide();
16489 this.un('resize', this.alignErrorIcon, this);
16493 var t = Roo.getDom(this.msgTarget);
16495 t.style.display = 'none';
16498 this.fireEvent('valid', this);
16502 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16503 * @return {Mixed} value The field value
16505 getRawValue : function(){
16506 var v = this.el.getValue();
16512 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16513 * @return {Mixed} value The field value
16515 getValue : function(){
16516 var v = this.el.getValue();
16522 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16523 * @param {Mixed} value The value to set
16525 setRawValue : function(v){
16526 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16530 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16531 * @param {Mixed} value The value to set
16533 setValue : function(v){
16536 this.el.dom.value = (v === null || v === undefined ? '' : v);
16541 adjustSize : function(w, h){
16542 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16543 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16547 adjustWidth : function(tag, w){
16548 tag = tag.toLowerCase();
16549 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16550 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16551 if(tag == 'input'){
16554 if(tag == 'textarea'){
16557 }else if(Roo.isOpera){
16558 if(tag == 'input'){
16561 if(tag == 'textarea'){
16571 // anything other than normal should be considered experimental
16572 Roo.form.Field.msgFx = {
16574 show: function(msgEl, f){
16575 msgEl.setDisplayed('block');
16578 hide : function(msgEl, f){
16579 msgEl.setDisplayed(false).update('');
16584 show: function(msgEl, f){
16585 msgEl.slideIn('t', {stopFx:true});
16588 hide : function(msgEl, f){
16589 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16594 show: function(msgEl, f){
16595 msgEl.fixDisplay();
16596 msgEl.alignTo(f.el, 'tl-tr');
16597 msgEl.slideIn('l', {stopFx:true});
16600 hide : function(msgEl, f){
16601 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16606 * Ext JS Library 1.1.1
16607 * Copyright(c) 2006-2007, Ext JS, LLC.
16609 * Originally Released Under LGPL - original licence link has changed is not relivant.
16612 * <script type="text/javascript">
16617 * @class Roo.form.TextField
16618 * @extends Roo.form.Field
16619 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16620 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16622 * Creates a new TextField
16623 * @param {Object} config Configuration options
16625 Roo.form.TextField = function(config){
16626 Roo.form.TextField.superclass.constructor.call(this, config);
16630 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16631 * according to the default logic, but this event provides a hook for the developer to apply additional
16632 * logic at runtime to resize the field if needed.
16633 * @param {Roo.form.Field} this This text field
16634 * @param {Number} width The new field width
16640 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16642 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16646 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16650 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16654 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16658 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16662 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16664 disableKeyFilter : false,
16666 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16670 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16674 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16676 maxLength : Number.MAX_VALUE,
16678 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16680 minLengthText : "The minimum length for this field is {0}",
16682 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16684 maxLengthText : "The maximum length for this field is {0}",
16686 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16688 selectOnFocus : false,
16690 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
16692 allowLeadingSpace : false,
16694 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16696 blankText : "This field is required",
16698 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16699 * If available, this function will be called only after the basic validators all return true, and will be passed the
16700 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16704 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16705 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16706 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
16710 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16714 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16720 initEvents : function()
16722 if (this.emptyText) {
16723 this.el.attr('placeholder', this.emptyText);
16726 Roo.form.TextField.superclass.initEvents.call(this);
16727 if(this.validationEvent == 'keyup'){
16728 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16729 this.el.on('keyup', this.filterValidation, this);
16731 else if(this.validationEvent !== false){
16732 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16735 if(this.selectOnFocus){
16736 this.on("focus", this.preFocus, this);
16738 if (!this.allowLeadingSpace) {
16739 this.on('blur', this.cleanLeadingSpace, this);
16742 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16743 this.el.on("keypress", this.filterKeys, this);
16746 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
16747 this.el.on("click", this.autoSize, this);
16749 if(this.el.is('input[type=password]') && Roo.isSafari){
16750 this.el.on('keydown', this.SafariOnKeyDown, this);
16754 processValue : function(value){
16755 if(this.stripCharsRe){
16756 var newValue = value.replace(this.stripCharsRe, '');
16757 if(newValue !== value){
16758 this.setRawValue(newValue);
16765 filterValidation : function(e){
16766 if(!e.isNavKeyPress()){
16767 this.validationTask.delay(this.validationDelay);
16772 onKeyUp : function(e){
16773 if(!e.isNavKeyPress()){
16777 // private - clean the leading white space
16778 cleanLeadingSpace : function(e)
16780 if ( this.inputType == 'file') {
16784 this.setValue((this.getValue() + '').replace(/^\s+/,''));
16787 * Resets the current field value to the originally-loaded value and clears any validation messages.
16790 reset : function(){
16791 Roo.form.TextField.superclass.reset.call(this);
16795 preFocus : function(){
16797 if(this.selectOnFocus){
16798 this.el.dom.select();
16804 filterKeys : function(e){
16805 var k = e.getKey();
16806 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16809 var c = e.getCharCode(), cc = String.fromCharCode(c);
16810 if(Roo.isIE && (e.isSpecialKey() || !cc)){
16813 if(!this.maskRe.test(cc)){
16818 setValue : function(v){
16820 Roo.form.TextField.superclass.setValue.apply(this, arguments);
16826 * Validates a value according to the field's validation rules and marks the field as invalid
16827 * if the validation fails
16828 * @param {Mixed} value The value to validate
16829 * @return {Boolean} True if the value is valid, else false
16831 validateValue : function(value){
16832 if(value.length < 1) { // if it's blank
16833 if(this.allowBlank){
16834 this.clearInvalid();
16837 this.markInvalid(this.blankText);
16841 if(value.length < this.minLength){
16842 this.markInvalid(String.format(this.minLengthText, this.minLength));
16845 if(value.length > this.maxLength){
16846 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16850 var vt = Roo.form.VTypes;
16851 if(!vt[this.vtype](value, this)){
16852 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16856 if(typeof this.validator == "function"){
16857 var msg = this.validator(value);
16859 this.markInvalid(msg);
16863 if(this.regex && !this.regex.test(value)){
16864 this.markInvalid(this.regexText);
16871 * Selects text in this field
16872 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16873 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16875 selectText : function(start, end){
16876 var v = this.getRawValue();
16878 start = start === undefined ? 0 : start;
16879 end = end === undefined ? v.length : end;
16880 var d = this.el.dom;
16881 if(d.setSelectionRange){
16882 d.setSelectionRange(start, end);
16883 }else if(d.createTextRange){
16884 var range = d.createTextRange();
16885 range.moveStart("character", start);
16886 range.moveEnd("character", v.length-end);
16893 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16894 * This only takes effect if grow = true, and fires the autosize event.
16896 autoSize : function(){
16897 if(!this.grow || !this.rendered){
16901 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16904 var v = el.dom.value;
16905 var d = document.createElement('div');
16906 d.appendChild(document.createTextNode(v));
16910 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16911 this.el.setWidth(w);
16912 this.fireEvent("autosize", this, w);
16916 SafariOnKeyDown : function(event)
16918 // this is a workaround for a password hang bug on chrome/ webkit.
16920 var isSelectAll = false;
16922 if(this.el.dom.selectionEnd > 0){
16923 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16925 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16926 event.preventDefault();
16931 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16933 event.preventDefault();
16934 // this is very hacky as keydown always get's upper case.
16936 var cc = String.fromCharCode(event.getCharCode());
16939 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
16947 * Ext JS Library 1.1.1
16948 * Copyright(c) 2006-2007, Ext JS, LLC.
16950 * Originally Released Under LGPL - original licence link has changed is not relivant.
16953 * <script type="text/javascript">
16957 * @class Roo.form.Hidden
16958 * @extends Roo.form.TextField
16959 * Simple Hidden element used on forms
16961 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16964 * Creates a new Hidden form element.
16965 * @param {Object} config Configuration options
16970 // easy hidden field...
16971 Roo.form.Hidden = function(config){
16972 Roo.form.Hidden.superclass.constructor.call(this, config);
16975 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16977 inputType: 'hidden',
16980 labelSeparator: '',
16982 itemCls : 'x-form-item-display-none'
16990 * Ext JS Library 1.1.1
16991 * Copyright(c) 2006-2007, Ext JS, LLC.
16993 * Originally Released Under LGPL - original licence link has changed is not relivant.
16996 * <script type="text/javascript">
17000 * @class Roo.form.TriggerField
17001 * @extends Roo.form.TextField
17002 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17003 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17004 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17005 * for which you can provide a custom implementation. For example:
17007 var trigger = new Roo.form.TriggerField();
17008 trigger.onTriggerClick = myTriggerFn;
17009 trigger.applyTo('my-field');
17012 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17013 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17014 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17015 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17017 * Create a new TriggerField.
17018 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17019 * to the base TextField)
17021 Roo.form.TriggerField = function(config){
17022 this.mimicing = false;
17023 Roo.form.TriggerField.superclass.constructor.call(this, config);
17026 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17028 * @cfg {String} triggerClass A CSS class to apply to the trigger
17031 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17032 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17034 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17036 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17040 /** @cfg {Boolean} grow @hide */
17041 /** @cfg {Number} growMin @hide */
17042 /** @cfg {Number} growMax @hide */
17048 autoSize: Roo.emptyFn,
17052 deferHeight : true,
17055 actionMode : 'wrap',
17057 onResize : function(w, h){
17058 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17059 if(typeof w == 'number'){
17060 var x = w - this.trigger.getWidth();
17061 this.el.setWidth(this.adjustWidth('input', x));
17062 this.trigger.setStyle('left', x+'px');
17067 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17070 getResizeEl : function(){
17075 getPositionEl : function(){
17080 alignErrorIcon : function(){
17081 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17085 onRender : function(ct, position){
17086 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17087 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17088 this.trigger = this.wrap.createChild(this.triggerConfig ||
17089 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17090 if(this.hideTrigger){
17091 this.trigger.setDisplayed(false);
17093 this.initTrigger();
17095 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17100 initTrigger : function(){
17101 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17102 this.trigger.addClassOnOver('x-form-trigger-over');
17103 this.trigger.addClassOnClick('x-form-trigger-click');
17107 onDestroy : function(){
17109 this.trigger.removeAllListeners();
17110 this.trigger.remove();
17113 this.wrap.remove();
17115 Roo.form.TriggerField.superclass.onDestroy.call(this);
17119 onFocus : function(){
17120 Roo.form.TriggerField.superclass.onFocus.call(this);
17121 if(!this.mimicing){
17122 this.wrap.addClass('x-trigger-wrap-focus');
17123 this.mimicing = true;
17124 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17125 if(this.monitorTab){
17126 this.el.on("keydown", this.checkTab, this);
17132 checkTab : function(e){
17133 if(e.getKey() == e.TAB){
17134 this.triggerBlur();
17139 onBlur : function(){
17144 mimicBlur : function(e, t){
17145 if(!this.wrap.contains(t) && this.validateBlur()){
17146 this.triggerBlur();
17151 triggerBlur : function(){
17152 this.mimicing = false;
17153 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17154 if(this.monitorTab){
17155 this.el.un("keydown", this.checkTab, this);
17157 this.wrap.removeClass('x-trigger-wrap-focus');
17158 Roo.form.TriggerField.superclass.onBlur.call(this);
17162 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17163 validateBlur : function(e, t){
17168 onDisable : function(){
17169 Roo.form.TriggerField.superclass.onDisable.call(this);
17171 this.wrap.addClass('x-item-disabled');
17176 onEnable : function(){
17177 Roo.form.TriggerField.superclass.onEnable.call(this);
17179 this.wrap.removeClass('x-item-disabled');
17184 onShow : function(){
17185 var ae = this.getActionEl();
17188 ae.dom.style.display = '';
17189 ae.dom.style.visibility = 'visible';
17195 onHide : function(){
17196 var ae = this.getActionEl();
17197 ae.dom.style.display = 'none';
17201 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17202 * by an implementing function.
17204 * @param {EventObject} e
17206 onTriggerClick : Roo.emptyFn
17209 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17210 // to be extended by an implementing class. For an example of implementing this class, see the custom
17211 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17212 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17213 initComponent : function(){
17214 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17216 this.triggerConfig = {
17217 tag:'span', cls:'x-form-twin-triggers', cn:[
17218 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17219 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17223 getTrigger : function(index){
17224 return this.triggers[index];
17227 initTrigger : function(){
17228 var ts = this.trigger.select('.x-form-trigger', true);
17229 this.wrap.setStyle('overflow', 'hidden');
17230 var triggerField = this;
17231 ts.each(function(t, all, index){
17232 t.hide = function(){
17233 var w = triggerField.wrap.getWidth();
17234 this.dom.style.display = 'none';
17235 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17237 t.show = function(){
17238 var w = triggerField.wrap.getWidth();
17239 this.dom.style.display = '';
17240 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17242 var triggerIndex = 'Trigger'+(index+1);
17244 if(this['hide'+triggerIndex]){
17245 t.dom.style.display = 'none';
17247 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17248 t.addClassOnOver('x-form-trigger-over');
17249 t.addClassOnClick('x-form-trigger-click');
17251 this.triggers = ts.elements;
17254 onTrigger1Click : Roo.emptyFn,
17255 onTrigger2Click : Roo.emptyFn
17258 * Ext JS Library 1.1.1
17259 * Copyright(c) 2006-2007, Ext JS, LLC.
17261 * Originally Released Under LGPL - original licence link has changed is not relivant.
17264 * <script type="text/javascript">
17268 * @class Roo.form.TextArea
17269 * @extends Roo.form.TextField
17270 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17271 * support for auto-sizing.
17273 * Creates a new TextArea
17274 * @param {Object} config Configuration options
17276 Roo.form.TextArea = function(config){
17277 Roo.form.TextArea.superclass.constructor.call(this, config);
17278 // these are provided exchanges for backwards compat
17279 // minHeight/maxHeight were replaced by growMin/growMax to be
17280 // compatible with TextField growing config values
17281 if(this.minHeight !== undefined){
17282 this.growMin = this.minHeight;
17284 if(this.maxHeight !== undefined){
17285 this.growMax = this.maxHeight;
17289 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17291 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17295 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17299 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17300 * in the field (equivalent to setting overflow: hidden, defaults to false)
17302 preventScrollbars: false,
17304 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17305 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17309 onRender : function(ct, position){
17311 this.defaultAutoCreate = {
17313 style:"width:300px;height:60px;",
17314 autocomplete: "new-password"
17317 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17319 this.textSizeEl = Roo.DomHelper.append(document.body, {
17320 tag: "pre", cls: "x-form-grow-sizer"
17322 if(this.preventScrollbars){
17323 this.el.setStyle("overflow", "hidden");
17325 this.el.setHeight(this.growMin);
17329 onDestroy : function(){
17330 if(this.textSizeEl){
17331 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17333 Roo.form.TextArea.superclass.onDestroy.call(this);
17337 onKeyUp : function(e){
17338 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17344 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17345 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17347 autoSize : function(){
17348 if(!this.grow || !this.textSizeEl){
17352 var v = el.dom.value;
17353 var ts = this.textSizeEl;
17356 ts.appendChild(document.createTextNode(v));
17359 Roo.fly(ts).setWidth(this.el.getWidth());
17361 v = "  ";
17364 v = v.replace(/\n/g, '<p> </p>');
17366 v += " \n ";
17369 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17370 if(h != this.lastHeight){
17371 this.lastHeight = h;
17372 this.el.setHeight(h);
17373 this.fireEvent("autosize", this, h);
17378 * Ext JS Library 1.1.1
17379 * Copyright(c) 2006-2007, Ext JS, LLC.
17381 * Originally Released Under LGPL - original licence link has changed is not relivant.
17384 * <script type="text/javascript">
17389 * @class Roo.form.NumberField
17390 * @extends Roo.form.TextField
17391 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17393 * Creates a new NumberField
17394 * @param {Object} config Configuration options
17396 Roo.form.NumberField = function(config){
17397 Roo.form.NumberField.superclass.constructor.call(this, config);
17400 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17402 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17404 fieldClass: "x-form-field x-form-num-field",
17406 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17408 allowDecimals : true,
17410 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17412 decimalSeparator : ".",
17414 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17416 decimalPrecision : 2,
17418 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17420 allowNegative : true,
17422 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17424 minValue : Number.NEGATIVE_INFINITY,
17426 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17428 maxValue : Number.MAX_VALUE,
17430 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17432 minText : "The minimum value for this field is {0}",
17434 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17436 maxText : "The maximum value for this field is {0}",
17438 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17439 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17441 nanText : "{0} is not a valid number",
17444 initEvents : function(){
17445 Roo.form.NumberField.superclass.initEvents.call(this);
17446 var allowed = "0123456789";
17447 if(this.allowDecimals){
17448 allowed += this.decimalSeparator;
17450 if(this.allowNegative){
17453 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17454 var keyPress = function(e){
17455 var k = e.getKey();
17456 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17459 var c = e.getCharCode();
17460 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17464 this.el.on("keypress", keyPress, this);
17468 validateValue : function(value){
17469 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17472 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17475 var num = this.parseValue(value);
17477 this.markInvalid(String.format(this.nanText, value));
17480 if(num < this.minValue){
17481 this.markInvalid(String.format(this.minText, this.minValue));
17484 if(num > this.maxValue){
17485 this.markInvalid(String.format(this.maxText, this.maxValue));
17491 getValue : function(){
17492 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17496 parseValue : function(value){
17497 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17498 return isNaN(value) ? '' : value;
17502 fixPrecision : function(value){
17503 var nan = isNaN(value);
17504 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17505 return nan ? '' : value;
17507 return parseFloat(value).toFixed(this.decimalPrecision);
17510 setValue : function(v){
17511 v = this.fixPrecision(v);
17512 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17516 decimalPrecisionFcn : function(v){
17517 return Math.floor(v);
17520 beforeBlur : function(){
17521 var v = this.parseValue(this.getRawValue());
17528 * Ext JS Library 1.1.1
17529 * Copyright(c) 2006-2007, Ext JS, LLC.
17531 * Originally Released Under LGPL - original licence link has changed is not relivant.
17534 * <script type="text/javascript">
17538 * @class Roo.form.DateField
17539 * @extends Roo.form.TriggerField
17540 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17542 * Create a new DateField
17543 * @param {Object} config
17545 Roo.form.DateField = function(config)
17547 Roo.form.DateField.superclass.constructor.call(this, config);
17553 * Fires when a date is selected
17554 * @param {Roo.form.DateField} combo This combo box
17555 * @param {Date} date The date selected
17562 if(typeof this.minValue == "string") {
17563 this.minValue = this.parseDate(this.minValue);
17565 if(typeof this.maxValue == "string") {
17566 this.maxValue = this.parseDate(this.maxValue);
17568 this.ddMatch = null;
17569 if(this.disabledDates){
17570 var dd = this.disabledDates;
17572 for(var i = 0; i < dd.length; i++){
17574 if(i != dd.length-1) {
17578 this.ddMatch = new RegExp(re + ")");
17582 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17584 * @cfg {String} format
17585 * The default date format string which can be overriden for localization support. The format must be
17586 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17590 * @cfg {String} altFormats
17591 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17592 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17594 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17596 * @cfg {Array} disabledDays
17597 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17599 disabledDays : null,
17601 * @cfg {String} disabledDaysText
17602 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17604 disabledDaysText : "Disabled",
17606 * @cfg {Array} disabledDates
17607 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17608 * expression so they are very powerful. Some examples:
17610 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17611 * <li>["03/08", "09/16"] would disable those days for every year</li>
17612 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17613 * <li>["03/../2006"] would disable every day in March 2006</li>
17614 * <li>["^03"] would disable every day in every March</li>
17616 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17617 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17619 disabledDates : null,
17621 * @cfg {String} disabledDatesText
17622 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17624 disabledDatesText : "Disabled",
17628 * @cfg {Date/String} zeroValue
17629 * if the date is less that this number, then the field is rendered as empty
17632 zeroValue : '1800-01-01',
17636 * @cfg {Date/String} minValue
17637 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17638 * valid format (defaults to null).
17642 * @cfg {Date/String} maxValue
17643 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17644 * valid format (defaults to null).
17648 * @cfg {String} minText
17649 * The error text to display when the date in the cell is before minValue (defaults to
17650 * 'The date in this field must be after {minValue}').
17652 minText : "The date in this field must be equal to or after {0}",
17654 * @cfg {String} maxText
17655 * The error text to display when the date in the cell is after maxValue (defaults to
17656 * 'The date in this field must be before {maxValue}').
17658 maxText : "The date in this field must be equal to or before {0}",
17660 * @cfg {String} invalidText
17661 * The error text to display when the date in the field is invalid (defaults to
17662 * '{value} is not a valid date - it must be in the format {format}').
17664 invalidText : "{0} is not a valid date - it must be in the format {1}",
17666 * @cfg {String} triggerClass
17667 * An additional CSS class used to style the trigger button. The trigger will always get the
17668 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17669 * which displays a calendar icon).
17671 triggerClass : 'x-form-date-trigger',
17675 * @cfg {Boolean} useIso
17676 * if enabled, then the date field will use a hidden field to store the
17677 * real value as iso formated date. default (false)
17681 * @cfg {String/Object} autoCreate
17682 * A DomHelper element spec, or true for a default element spec (defaults to
17683 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17686 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17689 hiddenField: false,
17691 onRender : function(ct, position)
17693 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17695 //this.el.dom.removeAttribute('name');
17696 Roo.log("Changing name?");
17697 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17698 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17700 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17701 // prevent input submission
17702 this.hiddenName = this.name;
17709 validateValue : function(value)
17711 value = this.formatDate(value);
17712 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17713 Roo.log('super failed');
17716 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17719 var svalue = value;
17720 value = this.parseDate(value);
17722 Roo.log('parse date failed' + svalue);
17723 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17726 var time = value.getTime();
17727 if(this.minValue && time < this.minValue.getTime()){
17728 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17731 if(this.maxValue && time > this.maxValue.getTime()){
17732 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17735 if(this.disabledDays){
17736 var day = value.getDay();
17737 for(var i = 0; i < this.disabledDays.length; i++) {
17738 if(day === this.disabledDays[i]){
17739 this.markInvalid(this.disabledDaysText);
17744 var fvalue = this.formatDate(value);
17745 if(this.ddMatch && this.ddMatch.test(fvalue)){
17746 this.markInvalid(String.format(this.disabledDatesText, fvalue));
17753 // Provides logic to override the default TriggerField.validateBlur which just returns true
17754 validateBlur : function(){
17755 return !this.menu || !this.menu.isVisible();
17758 getName: function()
17760 // returns hidden if it's set..
17761 if (!this.rendered) {return ''};
17762 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
17767 * Returns the current date value of the date field.
17768 * @return {Date} The date value
17770 getValue : function(){
17772 return this.hiddenField ?
17773 this.hiddenField.value :
17774 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17778 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
17779 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17780 * (the default format used is "m/d/y").
17783 //All of these calls set the same date value (May 4, 2006)
17785 //Pass a date object:
17786 var dt = new Date('5/4/06');
17787 dateField.setValue(dt);
17789 //Pass a date string (default format):
17790 dateField.setValue('5/4/06');
17792 //Pass a date string (custom format):
17793 dateField.format = 'Y-m-d';
17794 dateField.setValue('2006-5-4');
17796 * @param {String/Date} date The date or valid date string
17798 setValue : function(date){
17799 if (this.hiddenField) {
17800 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17802 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17803 // make sure the value field is always stored as a date..
17804 this.value = this.parseDate(date);
17810 parseDate : function(value){
17812 if (value instanceof Date) {
17813 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17820 if(!value || value instanceof Date){
17823 var v = Date.parseDate(value, this.format);
17824 if (!v && this.useIso) {
17825 v = Date.parseDate(value, 'Y-m-d');
17827 if(!v && this.altFormats){
17828 if(!this.altFormatsArray){
17829 this.altFormatsArray = this.altFormats.split("|");
17831 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17832 v = Date.parseDate(value, this.altFormatsArray[i]);
17835 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17842 formatDate : function(date, fmt){
17843 return (!date || !(date instanceof Date)) ?
17844 date : date.dateFormat(fmt || this.format);
17849 select: function(m, d){
17852 this.fireEvent('select', this, d);
17854 show : function(){ // retain focus styling
17858 this.focus.defer(10, this);
17859 var ml = this.menuListeners;
17860 this.menu.un("select", ml.select, this);
17861 this.menu.un("show", ml.show, this);
17862 this.menu.un("hide", ml.hide, this);
17867 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17868 onTriggerClick : function(){
17872 if(this.menu == null){
17873 this.menu = new Roo.menu.DateMenu();
17875 Roo.apply(this.menu.picker, {
17876 showClear: this.allowBlank,
17877 minDate : this.minValue,
17878 maxDate : this.maxValue,
17879 disabledDatesRE : this.ddMatch,
17880 disabledDatesText : this.disabledDatesText,
17881 disabledDays : this.disabledDays,
17882 disabledDaysText : this.disabledDaysText,
17883 format : this.useIso ? 'Y-m-d' : this.format,
17884 minText : String.format(this.minText, this.formatDate(this.minValue)),
17885 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17887 this.menu.on(Roo.apply({}, this.menuListeners, {
17890 this.menu.picker.setValue(this.getValue() || new Date());
17891 this.menu.show(this.el, "tl-bl?");
17894 beforeBlur : function(){
17895 var v = this.parseDate(this.getRawValue());
17905 isDirty : function() {
17906 if(this.disabled) {
17910 if(typeof(this.startValue) === 'undefined'){
17914 return String(this.getValue()) !== String(this.startValue);
17918 cleanLeadingSpace : function(e)
17925 * Ext JS Library 1.1.1
17926 * Copyright(c) 2006-2007, Ext JS, LLC.
17928 * Originally Released Under LGPL - original licence link has changed is not relivant.
17931 * <script type="text/javascript">
17935 * @class Roo.form.MonthField
17936 * @extends Roo.form.TriggerField
17937 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17939 * Create a new MonthField
17940 * @param {Object} config
17942 Roo.form.MonthField = function(config){
17944 Roo.form.MonthField.superclass.constructor.call(this, config);
17950 * Fires when a date is selected
17951 * @param {Roo.form.MonthFieeld} combo This combo box
17952 * @param {Date} date The date selected
17959 if(typeof this.minValue == "string") {
17960 this.minValue = this.parseDate(this.minValue);
17962 if(typeof this.maxValue == "string") {
17963 this.maxValue = this.parseDate(this.maxValue);
17965 this.ddMatch = null;
17966 if(this.disabledDates){
17967 var dd = this.disabledDates;
17969 for(var i = 0; i < dd.length; i++){
17971 if(i != dd.length-1) {
17975 this.ddMatch = new RegExp(re + ")");
17979 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
17981 * @cfg {String} format
17982 * The default date format string which can be overriden for localization support. The format must be
17983 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17987 * @cfg {String} altFormats
17988 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17989 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17991 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17993 * @cfg {Array} disabledDays
17994 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17996 disabledDays : [0,1,2,3,4,5,6],
17998 * @cfg {String} disabledDaysText
17999 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18001 disabledDaysText : "Disabled",
18003 * @cfg {Array} disabledDates
18004 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18005 * expression so they are very powerful. Some examples:
18007 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18008 * <li>["03/08", "09/16"] would disable those days for every year</li>
18009 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18010 * <li>["03/../2006"] would disable every day in March 2006</li>
18011 * <li>["^03"] would disable every day in every March</li>
18013 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18014 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18016 disabledDates : null,
18018 * @cfg {String} disabledDatesText
18019 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18021 disabledDatesText : "Disabled",
18023 * @cfg {Date/String} minValue
18024 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18025 * valid format (defaults to null).
18029 * @cfg {Date/String} maxValue
18030 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18031 * valid format (defaults to null).
18035 * @cfg {String} minText
18036 * The error text to display when the date in the cell is before minValue (defaults to
18037 * 'The date in this field must be after {minValue}').
18039 minText : "The date in this field must be equal to or after {0}",
18041 * @cfg {String} maxTextf
18042 * The error text to display when the date in the cell is after maxValue (defaults to
18043 * 'The date in this field must be before {maxValue}').
18045 maxText : "The date in this field must be equal to or before {0}",
18047 * @cfg {String} invalidText
18048 * The error text to display when the date in the field is invalid (defaults to
18049 * '{value} is not a valid date - it must be in the format {format}').
18051 invalidText : "{0} is not a valid date - it must be in the format {1}",
18053 * @cfg {String} triggerClass
18054 * An additional CSS class used to style the trigger button. The trigger will always get the
18055 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18056 * which displays a calendar icon).
18058 triggerClass : 'x-form-date-trigger',
18062 * @cfg {Boolean} useIso
18063 * if enabled, then the date field will use a hidden field to store the
18064 * real value as iso formated date. default (true)
18068 * @cfg {String/Object} autoCreate
18069 * A DomHelper element spec, or true for a default element spec (defaults to
18070 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18073 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18076 hiddenField: false,
18078 hideMonthPicker : false,
18080 onRender : function(ct, position)
18082 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18084 this.el.dom.removeAttribute('name');
18085 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18087 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18088 // prevent input submission
18089 this.hiddenName = this.name;
18096 validateValue : function(value)
18098 value = this.formatDate(value);
18099 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18102 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18105 var svalue = value;
18106 value = this.parseDate(value);
18108 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18111 var time = value.getTime();
18112 if(this.minValue && time < this.minValue.getTime()){
18113 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18116 if(this.maxValue && time > this.maxValue.getTime()){
18117 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18120 /*if(this.disabledDays){
18121 var day = value.getDay();
18122 for(var i = 0; i < this.disabledDays.length; i++) {
18123 if(day === this.disabledDays[i]){
18124 this.markInvalid(this.disabledDaysText);
18130 var fvalue = this.formatDate(value);
18131 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18132 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18140 // Provides logic to override the default TriggerField.validateBlur which just returns true
18141 validateBlur : function(){
18142 return !this.menu || !this.menu.isVisible();
18146 * Returns the current date value of the date field.
18147 * @return {Date} The date value
18149 getValue : function(){
18153 return this.hiddenField ?
18154 this.hiddenField.value :
18155 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18159 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18160 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18161 * (the default format used is "m/d/y").
18164 //All of these calls set the same date value (May 4, 2006)
18166 //Pass a date object:
18167 var dt = new Date('5/4/06');
18168 monthField.setValue(dt);
18170 //Pass a date string (default format):
18171 monthField.setValue('5/4/06');
18173 //Pass a date string (custom format):
18174 monthField.format = 'Y-m-d';
18175 monthField.setValue('2006-5-4');
18177 * @param {String/Date} date The date or valid date string
18179 setValue : function(date){
18180 Roo.log('month setValue' + date);
18181 // can only be first of month..
18183 var val = this.parseDate(date);
18185 if (this.hiddenField) {
18186 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18188 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18189 this.value = this.parseDate(date);
18193 parseDate : function(value){
18194 if(!value || value instanceof Date){
18195 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18198 var v = Date.parseDate(value, this.format);
18199 if (!v && this.useIso) {
18200 v = Date.parseDate(value, 'Y-m-d');
18204 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18208 if(!v && this.altFormats){
18209 if(!this.altFormatsArray){
18210 this.altFormatsArray = this.altFormats.split("|");
18212 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18213 v = Date.parseDate(value, this.altFormatsArray[i]);
18220 formatDate : function(date, fmt){
18221 return (!date || !(date instanceof Date)) ?
18222 date : date.dateFormat(fmt || this.format);
18227 select: function(m, d){
18229 this.fireEvent('select', this, d);
18231 show : function(){ // retain focus styling
18235 this.focus.defer(10, this);
18236 var ml = this.menuListeners;
18237 this.menu.un("select", ml.select, this);
18238 this.menu.un("show", ml.show, this);
18239 this.menu.un("hide", ml.hide, this);
18243 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18244 onTriggerClick : function(){
18248 if(this.menu == null){
18249 this.menu = new Roo.menu.DateMenu();
18253 Roo.apply(this.menu.picker, {
18255 showClear: this.allowBlank,
18256 minDate : this.minValue,
18257 maxDate : this.maxValue,
18258 disabledDatesRE : this.ddMatch,
18259 disabledDatesText : this.disabledDatesText,
18261 format : this.useIso ? 'Y-m-d' : this.format,
18262 minText : String.format(this.minText, this.formatDate(this.minValue)),
18263 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18266 this.menu.on(Roo.apply({}, this.menuListeners, {
18274 // hide month picker get's called when we called by 'before hide';
18276 var ignorehide = true;
18277 p.hideMonthPicker = function(disableAnim){
18281 if(this.monthPicker){
18282 Roo.log("hideMonthPicker called");
18283 if(disableAnim === true){
18284 this.monthPicker.hide();
18286 this.monthPicker.slideOut('t', {duration:.2});
18287 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18288 p.fireEvent("select", this, this.value);
18294 Roo.log('picker set value');
18295 Roo.log(this.getValue());
18296 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18297 m.show(this.el, 'tl-bl?');
18298 ignorehide = false;
18299 // this will trigger hideMonthPicker..
18302 // hidden the day picker
18303 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18309 p.showMonthPicker.defer(100, p);
18315 beforeBlur : function(){
18316 var v = this.parseDate(this.getRawValue());
18322 /** @cfg {Boolean} grow @hide */
18323 /** @cfg {Number} growMin @hide */
18324 /** @cfg {Number} growMax @hide */
18331 * Ext JS Library 1.1.1
18332 * Copyright(c) 2006-2007, Ext JS, LLC.
18334 * Originally Released Under LGPL - original licence link has changed is not relivant.
18337 * <script type="text/javascript">
18342 * @class Roo.form.ComboBox
18343 * @extends Roo.form.TriggerField
18344 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18346 * Create a new ComboBox.
18347 * @param {Object} config Configuration options
18349 Roo.form.ComboBox = function(config){
18350 Roo.form.ComboBox.superclass.constructor.call(this, config);
18354 * Fires when the dropdown list is expanded
18355 * @param {Roo.form.ComboBox} combo This combo box
18360 * Fires when the dropdown list is collapsed
18361 * @param {Roo.form.ComboBox} combo This combo box
18365 * @event beforeselect
18366 * Fires before a list item is selected. Return false to cancel the selection.
18367 * @param {Roo.form.ComboBox} combo This combo box
18368 * @param {Roo.data.Record} record The data record returned from the underlying store
18369 * @param {Number} index The index of the selected item in the dropdown list
18371 'beforeselect' : true,
18374 * Fires when a list item is selected
18375 * @param {Roo.form.ComboBox} combo This combo box
18376 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18377 * @param {Number} index The index of the selected item in the dropdown list
18381 * @event beforequery
18382 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18383 * The event object passed has these properties:
18384 * @param {Roo.form.ComboBox} combo This combo box
18385 * @param {String} query The query
18386 * @param {Boolean} forceAll true to force "all" query
18387 * @param {Boolean} cancel true to cancel the query
18388 * @param {Object} e The query event object
18390 'beforequery': true,
18393 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18394 * @param {Roo.form.ComboBox} combo This combo box
18399 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18400 * @param {Roo.form.ComboBox} combo This combo box
18401 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18407 if(this.transform){
18408 this.allowDomMove = false;
18409 var s = Roo.getDom(this.transform);
18410 if(!this.hiddenName){
18411 this.hiddenName = s.name;
18414 this.mode = 'local';
18415 var d = [], opts = s.options;
18416 for(var i = 0, len = opts.length;i < len; i++){
18418 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18420 this.value = value;
18422 d.push([value, o.text]);
18424 this.store = new Roo.data.SimpleStore({
18426 fields: ['value', 'text'],
18429 this.valueField = 'value';
18430 this.displayField = 'text';
18432 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18433 if(!this.lazyRender){
18434 this.target = true;
18435 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18436 s.parentNode.removeChild(s); // remove it
18437 this.render(this.el.parentNode);
18439 s.parentNode.removeChild(s); // remove it
18444 this.store = Roo.factory(this.store, Roo.data);
18447 this.selectedIndex = -1;
18448 if(this.mode == 'local'){
18449 if(config.queryDelay === undefined){
18450 this.queryDelay = 10;
18452 if(config.minChars === undefined){
18458 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18460 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18463 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18464 * rendering into an Roo.Editor, defaults to false)
18467 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18468 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18471 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18474 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18475 * the dropdown list (defaults to undefined, with no header element)
18479 * @cfg {String/Roo.Template} tpl The template to use to render the output
18483 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18485 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18487 listWidth: undefined,
18489 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18490 * mode = 'remote' or 'text' if mode = 'local')
18492 displayField: undefined,
18494 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18495 * mode = 'remote' or 'value' if mode = 'local').
18496 * Note: use of a valueField requires the user make a selection
18497 * in order for a value to be mapped.
18499 valueField: undefined,
18503 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18504 * field's data value (defaults to the underlying DOM element's name)
18506 hiddenName: undefined,
18508 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18512 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18514 selectedClass: 'x-combo-selected',
18516 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18517 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18518 * which displays a downward arrow icon).
18520 triggerClass : 'x-form-arrow-trigger',
18522 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18526 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18527 * anchor positions (defaults to 'tl-bl')
18529 listAlign: 'tl-bl?',
18531 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18535 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18536 * query specified by the allQuery config option (defaults to 'query')
18538 triggerAction: 'query',
18540 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18541 * (defaults to 4, does not apply if editable = false)
18545 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18546 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18550 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18551 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18555 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18556 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18560 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18561 * when editable = true (defaults to false)
18563 selectOnFocus:false,
18565 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18567 queryParam: 'query',
18569 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18570 * when mode = 'remote' (defaults to 'Loading...')
18572 loadingText: 'Loading...',
18574 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18578 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18582 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18583 * traditional select (defaults to true)
18587 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18591 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18595 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18596 * listWidth has a higher value)
18600 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18601 * allow the user to set arbitrary text into the field (defaults to false)
18603 forceSelection:false,
18605 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18606 * if typeAhead = true (defaults to 250)
18608 typeAheadDelay : 250,
18610 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18611 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18613 valueNotFoundText : undefined,
18615 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18617 blockFocus : false,
18620 * @cfg {Boolean} disableClear Disable showing of clear button.
18622 disableClear : false,
18624 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18626 alwaysQuery : false,
18632 // element that contains real text value.. (when hidden is used..)
18635 onRender : function(ct, position)
18637 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18639 if(this.hiddenName){
18640 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18642 this.hiddenField.value =
18643 this.hiddenValue !== undefined ? this.hiddenValue :
18644 this.value !== undefined ? this.value : '';
18646 // prevent input submission
18647 this.el.dom.removeAttribute('name');
18653 this.el.dom.setAttribute('autocomplete', 'off');
18656 var cls = 'x-combo-list';
18658 this.list = new Roo.Layer({
18659 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18662 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18663 this.list.setWidth(lw);
18664 this.list.swallowEvent('mousewheel');
18665 this.assetHeight = 0;
18668 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18669 this.assetHeight += this.header.getHeight();
18672 this.innerList = this.list.createChild({cls:cls+'-inner'});
18673 this.innerList.on('mouseover', this.onViewOver, this);
18674 this.innerList.on('mousemove', this.onViewMove, this);
18675 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18677 if(this.allowBlank && !this.pageSize && !this.disableClear){
18678 this.footer = this.list.createChild({cls:cls+'-ft'});
18679 this.pageTb = new Roo.Toolbar(this.footer);
18683 this.footer = this.list.createChild({cls:cls+'-ft'});
18684 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18685 {pageSize: this.pageSize});
18689 if (this.pageTb && this.allowBlank && !this.disableClear) {
18691 this.pageTb.add(new Roo.Toolbar.Fill(), {
18692 cls: 'x-btn-icon x-btn-clear',
18694 handler: function()
18697 _this.clearValue();
18698 _this.onSelect(false, -1);
18703 this.assetHeight += this.footer.getHeight();
18708 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18711 this.view = new Roo.View(this.innerList, this.tpl, {
18714 selectedClass: this.selectedClass
18717 this.view.on('click', this.onViewClick, this);
18719 this.store.on('beforeload', this.onBeforeLoad, this);
18720 this.store.on('load', this.onLoad, this);
18721 this.store.on('loadexception', this.onLoadException, this);
18723 if(this.resizable){
18724 this.resizer = new Roo.Resizable(this.list, {
18725 pinned:true, handles:'se'
18727 this.resizer.on('resize', function(r, w, h){
18728 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18729 this.listWidth = w;
18730 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18731 this.restrictHeight();
18733 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18735 if(!this.editable){
18736 this.editable = true;
18737 this.setEditable(false);
18741 if (typeof(this.events.add.listeners) != 'undefined') {
18743 this.addicon = this.wrap.createChild(
18744 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18746 this.addicon.on('click', function(e) {
18747 this.fireEvent('add', this);
18750 if (typeof(this.events.edit.listeners) != 'undefined') {
18752 this.editicon = this.wrap.createChild(
18753 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
18754 if (this.addicon) {
18755 this.editicon.setStyle('margin-left', '40px');
18757 this.editicon.on('click', function(e) {
18759 // we fire even if inothing is selected..
18760 this.fireEvent('edit', this, this.lastData );
18770 initEvents : function(){
18771 Roo.form.ComboBox.superclass.initEvents.call(this);
18773 this.keyNav = new Roo.KeyNav(this.el, {
18774 "up" : function(e){
18775 this.inKeyMode = true;
18779 "down" : function(e){
18780 if(!this.isExpanded()){
18781 this.onTriggerClick();
18783 this.inKeyMode = true;
18788 "enter" : function(e){
18789 this.onViewClick();
18793 "esc" : function(e){
18797 "tab" : function(e){
18798 this.onViewClick(false);
18799 this.fireEvent("specialkey", this, e);
18805 doRelay : function(foo, bar, hname){
18806 if(hname == 'down' || this.scope.isExpanded()){
18807 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18814 this.queryDelay = Math.max(this.queryDelay || 10,
18815 this.mode == 'local' ? 10 : 250);
18816 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18817 if(this.typeAhead){
18818 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18820 if(this.editable !== false){
18821 this.el.on("keyup", this.onKeyUp, this);
18823 if(this.forceSelection){
18824 this.on('blur', this.doForce, this);
18828 onDestroy : function(){
18830 this.view.setStore(null);
18831 this.view.el.removeAllListeners();
18832 this.view.el.remove();
18833 this.view.purgeListeners();
18836 this.list.destroy();
18839 this.store.un('beforeload', this.onBeforeLoad, this);
18840 this.store.un('load', this.onLoad, this);
18841 this.store.un('loadexception', this.onLoadException, this);
18843 Roo.form.ComboBox.superclass.onDestroy.call(this);
18847 fireKey : function(e){
18848 if(e.isNavKeyPress() && !this.list.isVisible()){
18849 this.fireEvent("specialkey", this, e);
18854 onResize: function(w, h){
18855 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18857 if(typeof w != 'number'){
18858 // we do not handle it!?!?
18861 var tw = this.trigger.getWidth();
18862 tw += this.addicon ? this.addicon.getWidth() : 0;
18863 tw += this.editicon ? this.editicon.getWidth() : 0;
18865 this.el.setWidth( this.adjustWidth('input', x));
18867 this.trigger.setStyle('left', x+'px');
18869 if(this.list && this.listWidth === undefined){
18870 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18871 this.list.setWidth(lw);
18872 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18880 * Allow or prevent the user from directly editing the field text. If false is passed,
18881 * the user will only be able to select from the items defined in the dropdown list. This method
18882 * is the runtime equivalent of setting the 'editable' config option at config time.
18883 * @param {Boolean} value True to allow the user to directly edit the field text
18885 setEditable : function(value){
18886 if(value == this.editable){
18889 this.editable = value;
18891 this.el.dom.setAttribute('readOnly', true);
18892 this.el.on('mousedown', this.onTriggerClick, this);
18893 this.el.addClass('x-combo-noedit');
18895 this.el.dom.setAttribute('readOnly', false);
18896 this.el.un('mousedown', this.onTriggerClick, this);
18897 this.el.removeClass('x-combo-noedit');
18902 onBeforeLoad : function(){
18903 if(!this.hasFocus){
18906 this.innerList.update(this.loadingText ?
18907 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18908 this.restrictHeight();
18909 this.selectedIndex = -1;
18913 onLoad : function(){
18914 if(!this.hasFocus){
18917 if(this.store.getCount() > 0){
18919 this.restrictHeight();
18920 if(this.lastQuery == this.allQuery){
18922 this.el.dom.select();
18924 if(!this.selectByValue(this.value, true)){
18925 this.select(0, true);
18929 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18930 this.taTask.delay(this.typeAheadDelay);
18934 this.onEmptyResults();
18939 onLoadException : function()
18942 Roo.log(this.store.reader.jsonData);
18943 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18944 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18950 onTypeAhead : function(){
18951 if(this.store.getCount() > 0){
18952 var r = this.store.getAt(0);
18953 var newValue = r.data[this.displayField];
18954 var len = newValue.length;
18955 var selStart = this.getRawValue().length;
18956 if(selStart != len){
18957 this.setRawValue(newValue);
18958 this.selectText(selStart, newValue.length);
18964 onSelect : function(record, index){
18965 if(this.fireEvent('beforeselect', this, record, index) !== false){
18966 this.setFromData(index > -1 ? record.data : false);
18968 this.fireEvent('select', this, record, index);
18973 * Returns the currently selected field value or empty string if no value is set.
18974 * @return {String} value The selected value
18976 getValue : function(){
18977 if(this.valueField){
18978 return typeof this.value != 'undefined' ? this.value : '';
18980 return Roo.form.ComboBox.superclass.getValue.call(this);
18984 * Clears any text/value currently set in the field
18986 clearValue : function(){
18987 if(this.hiddenField){
18988 this.hiddenField.value = '';
18991 this.setRawValue('');
18992 this.lastSelectionText = '';
18997 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18998 * will be displayed in the field. If the value does not match the data value of an existing item,
18999 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19000 * Otherwise the field will be blank (although the value will still be set).
19001 * @param {String} value The value to match
19003 setValue : function(v){
19005 if(this.valueField){
19006 var r = this.findRecord(this.valueField, v);
19008 text = r.data[this.displayField];
19009 }else if(this.valueNotFoundText !== undefined){
19010 text = this.valueNotFoundText;
19013 this.lastSelectionText = text;
19014 if(this.hiddenField){
19015 this.hiddenField.value = v;
19017 Roo.form.ComboBox.superclass.setValue.call(this, text);
19021 * @property {Object} the last set data for the element
19026 * Sets the value of the field based on a object which is related to the record format for the store.
19027 * @param {Object} value the value to set as. or false on reset?
19029 setFromData : function(o){
19030 var dv = ''; // display value
19031 var vv = ''; // value value..
19033 if (this.displayField) {
19034 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19036 // this is an error condition!!!
19037 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19040 if(this.valueField){
19041 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19043 if(this.hiddenField){
19044 this.hiddenField.value = vv;
19046 this.lastSelectionText = dv;
19047 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19051 // no hidden field.. - we store the value in 'value', but still display
19052 // display field!!!!
19053 this.lastSelectionText = dv;
19054 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19060 reset : function(){
19061 // overridden so that last data is reset..
19062 this.setValue(this.resetValue);
19063 this.originalValue = this.getValue();
19064 this.clearInvalid();
19065 this.lastData = false;
19067 this.view.clearSelections();
19071 findRecord : function(prop, value){
19073 if(this.store.getCount() > 0){
19074 this.store.each(function(r){
19075 if(r.data[prop] == value){
19085 getName: function()
19087 // returns hidden if it's set..
19088 if (!this.rendered) {return ''};
19089 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19093 onViewMove : function(e, t){
19094 this.inKeyMode = false;
19098 onViewOver : function(e, t){
19099 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19102 var item = this.view.findItemFromChild(t);
19104 var index = this.view.indexOf(item);
19105 this.select(index, false);
19110 onViewClick : function(doFocus)
19112 var index = this.view.getSelectedIndexes()[0];
19113 var r = this.store.getAt(index);
19115 this.onSelect(r, index);
19117 if(doFocus !== false && !this.blockFocus){
19123 restrictHeight : function(){
19124 this.innerList.dom.style.height = '';
19125 var inner = this.innerList.dom;
19126 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19127 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19128 this.list.beginUpdate();
19129 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19130 this.list.alignTo(this.el, this.listAlign);
19131 this.list.endUpdate();
19135 onEmptyResults : function(){
19140 * Returns true if the dropdown list is expanded, else false.
19142 isExpanded : function(){
19143 return this.list.isVisible();
19147 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19148 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19149 * @param {String} value The data value of the item to select
19150 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19151 * selected item if it is not currently in view (defaults to true)
19152 * @return {Boolean} True if the value matched an item in the list, else false
19154 selectByValue : function(v, scrollIntoView){
19155 if(v !== undefined && v !== null){
19156 var r = this.findRecord(this.valueField || this.displayField, v);
19158 this.select(this.store.indexOf(r), scrollIntoView);
19166 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19167 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19168 * @param {Number} index The zero-based index of the list item to select
19169 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19170 * selected item if it is not currently in view (defaults to true)
19172 select : function(index, scrollIntoView){
19173 this.selectedIndex = index;
19174 this.view.select(index);
19175 if(scrollIntoView !== false){
19176 var el = this.view.getNode(index);
19178 this.innerList.scrollChildIntoView(el, false);
19184 selectNext : function(){
19185 var ct = this.store.getCount();
19187 if(this.selectedIndex == -1){
19189 }else if(this.selectedIndex < ct-1){
19190 this.select(this.selectedIndex+1);
19196 selectPrev : function(){
19197 var ct = this.store.getCount();
19199 if(this.selectedIndex == -1){
19201 }else if(this.selectedIndex != 0){
19202 this.select(this.selectedIndex-1);
19208 onKeyUp : function(e){
19209 if(this.editable !== false && !e.isSpecialKey()){
19210 this.lastKey = e.getKey();
19211 this.dqTask.delay(this.queryDelay);
19216 validateBlur : function(){
19217 return !this.list || !this.list.isVisible();
19221 initQuery : function(){
19222 this.doQuery(this.getRawValue());
19226 doForce : function(){
19227 if(this.el.dom.value.length > 0){
19228 this.el.dom.value =
19229 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19235 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19236 * query allowing the query action to be canceled if needed.
19237 * @param {String} query The SQL query to execute
19238 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19239 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19240 * saved in the current store (defaults to false)
19242 doQuery : function(q, forceAll){
19243 if(q === undefined || q === null){
19248 forceAll: forceAll,
19252 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19256 forceAll = qe.forceAll;
19257 if(forceAll === true || (q.length >= this.minChars)){
19258 if(this.lastQuery != q || this.alwaysQuery){
19259 this.lastQuery = q;
19260 if(this.mode == 'local'){
19261 this.selectedIndex = -1;
19263 this.store.clearFilter();
19265 this.store.filter(this.displayField, q);
19269 this.store.baseParams[this.queryParam] = q;
19271 params: this.getParams(q)
19276 this.selectedIndex = -1;
19283 getParams : function(q){
19285 //p[this.queryParam] = q;
19288 p.limit = this.pageSize;
19294 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19296 collapse : function(){
19297 if(!this.isExpanded()){
19301 Roo.get(document).un('mousedown', this.collapseIf, this);
19302 Roo.get(document).un('mousewheel', this.collapseIf, this);
19303 if (!this.editable) {
19304 Roo.get(document).un('keydown', this.listKeyPress, this);
19306 this.fireEvent('collapse', this);
19310 collapseIf : function(e){
19311 if(!e.within(this.wrap) && !e.within(this.list)){
19317 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19319 expand : function(){
19320 if(this.isExpanded() || !this.hasFocus){
19323 this.list.alignTo(this.el, this.listAlign);
19325 Roo.get(document).on('mousedown', this.collapseIf, this);
19326 Roo.get(document).on('mousewheel', this.collapseIf, this);
19327 if (!this.editable) {
19328 Roo.get(document).on('keydown', this.listKeyPress, this);
19331 this.fireEvent('expand', this);
19335 // Implements the default empty TriggerField.onTriggerClick function
19336 onTriggerClick : function(){
19340 if(this.isExpanded()){
19342 if (!this.blockFocus) {
19347 this.hasFocus = true;
19348 if(this.triggerAction == 'all') {
19349 this.doQuery(this.allQuery, true);
19351 this.doQuery(this.getRawValue());
19353 if (!this.blockFocus) {
19358 listKeyPress : function(e)
19360 //Roo.log('listkeypress');
19361 // scroll to first matching element based on key pres..
19362 if (e.isSpecialKey()) {
19365 var k = String.fromCharCode(e.getKey()).toUpperCase();
19368 var csel = this.view.getSelectedNodes();
19369 var cselitem = false;
19371 var ix = this.view.indexOf(csel[0]);
19372 cselitem = this.store.getAt(ix);
19373 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19379 this.store.each(function(v) {
19381 // start at existing selection.
19382 if (cselitem.id == v.id) {
19388 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19389 match = this.store.indexOf(v);
19394 if (match === false) {
19395 return true; // no more action?
19398 this.view.select(match);
19399 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19400 sn.scrollIntoView(sn.dom.parentNode, false);
19404 * @cfg {Boolean} grow
19408 * @cfg {Number} growMin
19412 * @cfg {Number} growMax
19420 * Copyright(c) 2010-2012, Roo J Solutions Limited
19427 * @class Roo.form.ComboBoxArray
19428 * @extends Roo.form.TextField
19429 * A facebook style adder... for lists of email / people / countries etc...
19430 * pick multiple items from a combo box, and shows each one.
19432 * Fred [x] Brian [x] [Pick another |v]
19435 * For this to work: it needs various extra information
19436 * - normal combo problay has
19438 * + displayField, valueField
19440 * For our purpose...
19443 * If we change from 'extends' to wrapping...
19450 * Create a new ComboBoxArray.
19451 * @param {Object} config Configuration options
19455 Roo.form.ComboBoxArray = function(config)
19459 * @event beforeremove
19460 * Fires before remove the value from the list
19461 * @param {Roo.form.ComboBoxArray} _self This combo box array
19462 * @param {Roo.form.ComboBoxArray.Item} item removed item
19464 'beforeremove' : true,
19467 * Fires when remove the value from the list
19468 * @param {Roo.form.ComboBoxArray} _self This combo box array
19469 * @param {Roo.form.ComboBoxArray.Item} item removed item
19476 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19478 this.items = new Roo.util.MixedCollection(false);
19480 // construct the child combo...
19490 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19493 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19498 // behavies liek a hiddne field
19499 inputType: 'hidden',
19501 * @cfg {Number} width The width of the box that displays the selected element
19508 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19512 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19514 hiddenName : false,
19516 * @cfg {String} seperator The value seperator normally ','
19520 // private the array of items that are displayed..
19522 // private - the hidden field el.
19524 // private - the filed el..
19527 //validateValue : function() { return true; }, // all values are ok!
19528 //onAddClick: function() { },
19530 onRender : function(ct, position)
19533 // create the standard hidden element
19534 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19537 // give fake names to child combo;
19538 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19539 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19541 this.combo = Roo.factory(this.combo, Roo.form);
19542 this.combo.onRender(ct, position);
19543 if (typeof(this.combo.width) != 'undefined') {
19544 this.combo.onResize(this.combo.width,0);
19547 this.combo.initEvents();
19549 // assigned so form know we need to do this..
19550 this.store = this.combo.store;
19551 this.valueField = this.combo.valueField;
19552 this.displayField = this.combo.displayField ;
19555 this.combo.wrap.addClass('x-cbarray-grp');
19557 var cbwrap = this.combo.wrap.createChild(
19558 {tag: 'div', cls: 'x-cbarray-cb'},
19563 this.hiddenEl = this.combo.wrap.createChild({
19564 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19566 this.el = this.combo.wrap.createChild({
19567 tag: 'input', type:'hidden' , name: this.name, value : ''
19569 // this.el.dom.removeAttribute("name");
19572 this.outerWrap = this.combo.wrap;
19573 this.wrap = cbwrap;
19575 this.outerWrap.setWidth(this.width);
19576 this.outerWrap.dom.removeChild(this.el.dom);
19578 this.wrap.dom.appendChild(this.el.dom);
19579 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19580 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19582 this.combo.trigger.setStyle('position','relative');
19583 this.combo.trigger.setStyle('left', '0px');
19584 this.combo.trigger.setStyle('top', '2px');
19586 this.combo.el.setStyle('vertical-align', 'text-bottom');
19588 //this.trigger.setStyle('vertical-align', 'top');
19590 // this should use the code from combo really... on('add' ....)
19594 this.adder = this.outerWrap.createChild(
19595 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19597 this.adder.on('click', function(e) {
19598 _t.fireEvent('adderclick', this, e);
19602 //this.adder.on('click', this.onAddClick, _t);
19605 this.combo.on('select', function(cb, rec, ix) {
19606 this.addItem(rec.data);
19609 cb.el.dom.value = '';
19610 //cb.lastData = rec.data;
19619 getName: function()
19621 // returns hidden if it's set..
19622 if (!this.rendered) {return ''};
19623 return this.hiddenName ? this.hiddenName : this.name;
19628 onResize: function(w, h){
19631 // not sure if this is needed..
19632 //this.combo.onResize(w,h);
19634 if(typeof w != 'number'){
19635 // we do not handle it!?!?
19638 var tw = this.combo.trigger.getWidth();
19639 tw += this.addicon ? this.addicon.getWidth() : 0;
19640 tw += this.editicon ? this.editicon.getWidth() : 0;
19642 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19644 this.combo.trigger.setStyle('left', '0px');
19646 if(this.list && this.listWidth === undefined){
19647 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19648 this.list.setWidth(lw);
19649 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19656 addItem: function(rec)
19658 var valueField = this.combo.valueField;
19659 var displayField = this.combo.displayField;
19661 if (this.items.indexOfKey(rec[valueField]) > -1) {
19662 //console.log("GOT " + rec.data.id);
19666 var x = new Roo.form.ComboBoxArray.Item({
19667 //id : rec[this.idField],
19669 displayField : displayField ,
19670 tipField : displayField ,
19674 this.items.add(rec[valueField],x);
19675 // add it before the element..
19676 this.updateHiddenEl();
19677 x.render(this.outerWrap, this.wrap.dom);
19678 // add the image handler..
19681 updateHiddenEl : function()
19684 if (!this.hiddenEl) {
19688 var idField = this.combo.valueField;
19690 this.items.each(function(f) {
19691 ar.push(f.data[idField]);
19693 this.hiddenEl.dom.value = ar.join(this.seperator);
19699 this.items.clear();
19701 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19705 this.el.dom.value = '';
19706 if (this.hiddenEl) {
19707 this.hiddenEl.dom.value = '';
19711 getValue: function()
19713 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19715 setValue: function(v) // not a valid action - must use addItems..
19720 if (this.store.isLocal && (typeof(v) == 'string')) {
19721 // then we can use the store to find the values..
19722 // comma seperated at present.. this needs to allow JSON based encoding..
19723 this.hiddenEl.value = v;
19725 Roo.each(v.split(this.seperator), function(k) {
19726 Roo.log("CHECK " + this.valueField + ',' + k);
19727 var li = this.store.query(this.valueField, k);
19732 add[this.valueField] = k;
19733 add[this.displayField] = li.item(0).data[this.displayField];
19739 if (typeof(v) == 'object' ) {
19740 // then let's assume it's an array of objects..
19741 Roo.each(v, function(l) {
19743 if (typeof(l) == 'string') {
19745 add[this.valueField] = l;
19746 add[this.displayField] = l
19755 setFromData: function(v)
19757 // this recieves an object, if setValues is called.
19759 this.el.dom.value = v[this.displayField];
19760 this.hiddenEl.dom.value = v[this.valueField];
19761 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19764 var kv = v[this.valueField];
19765 var dv = v[this.displayField];
19766 kv = typeof(kv) != 'string' ? '' : kv;
19767 dv = typeof(dv) != 'string' ? '' : dv;
19770 var keys = kv.split(this.seperator);
19771 var display = dv.split(this.seperator);
19772 for (var i = 0 ; i < keys.length; i++) {
19774 add[this.valueField] = keys[i];
19775 add[this.displayField] = display[i];
19783 * Validates the combox array value
19784 * @return {Boolean} True if the value is valid, else false
19786 validate : function(){
19787 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19788 this.clearInvalid();
19794 validateValue : function(value){
19795 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19803 isDirty : function() {
19804 if(this.disabled) {
19809 var d = Roo.decode(String(this.originalValue));
19811 return String(this.getValue()) !== String(this.originalValue);
19814 var originalValue = [];
19816 for (var i = 0; i < d.length; i++){
19817 originalValue.push(d[i][this.valueField]);
19820 return String(this.getValue()) !== String(originalValue.join(this.seperator));
19829 * @class Roo.form.ComboBoxArray.Item
19830 * @extends Roo.BoxComponent
19831 * A selected item in the list
19832 * Fred [x] Brian [x] [Pick another |v]
19835 * Create a new item.
19836 * @param {Object} config Configuration options
19839 Roo.form.ComboBoxArray.Item = function(config) {
19840 config.id = Roo.id();
19841 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19844 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19847 displayField : false,
19851 defaultAutoCreate : {
19853 cls: 'x-cbarray-item',
19860 src : Roo.BLANK_IMAGE_URL ,
19868 onRender : function(ct, position)
19870 Roo.form.Field.superclass.onRender.call(this, ct, position);
19873 var cfg = this.getAutoCreate();
19874 this.el = ct.createChild(cfg, position);
19877 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19879 this.el.child('div').dom.innerHTML = this.cb.renderer ?
19880 this.cb.renderer(this.data) :
19881 String.format('{0}',this.data[this.displayField]);
19884 this.el.child('div').dom.setAttribute('qtip',
19885 String.format('{0}',this.data[this.tipField])
19888 this.el.child('img').on('click', this.remove, this);
19892 remove : function()
19894 if(this.cb.disabled){
19898 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19899 this.cb.items.remove(this);
19900 this.el.child('img').un('click', this.remove, this);
19902 this.cb.updateHiddenEl();
19904 this.cb.fireEvent('remove', this.cb, this);
19909 * RooJS Library 1.1.1
19910 * Copyright(c) 2008-2011 Alan Knowles
19917 * @class Roo.form.ComboNested
19918 * @extends Roo.form.ComboBox
19919 * A combobox for that allows selection of nested items in a list,
19934 * Create a new ComboNested
19935 * @param {Object} config Configuration options
19937 Roo.form.ComboNested = function(config){
19938 Roo.form.ComboCheck.superclass.constructor.call(this, config);
19939 // should verify some data...
19941 // hiddenName = required..
19942 // displayField = required
19943 // valudField == required
19944 var req= [ 'hiddenName', 'displayField', 'valueField' ];
19946 Roo.each(req, function(e) {
19947 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19948 throw "Roo.form.ComboNested : missing value for: " + e;
19955 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19958 * @config {Number} max Number of columns to show
19963 list : null, // the outermost div..
19964 innerLists : null, // the
19968 loadingChildren : false,
19970 onRender : function(ct, position)
19972 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19974 if(this.hiddenName){
19975 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
19977 this.hiddenField.value =
19978 this.hiddenValue !== undefined ? this.hiddenValue :
19979 this.value !== undefined ? this.value : '';
19981 // prevent input submission
19982 this.el.dom.removeAttribute('name');
19988 this.el.dom.setAttribute('autocomplete', 'off');
19991 var cls = 'x-combo-list';
19993 this.list = new Roo.Layer({
19994 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19997 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19998 this.list.setWidth(lw);
19999 this.list.swallowEvent('mousewheel');
20000 this.assetHeight = 0;
20003 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20004 this.assetHeight += this.header.getHeight();
20006 this.innerLists = [];
20009 for (var i =0 ; i < this.maxColumns; i++) {
20010 this.onRenderList( cls, i);
20013 // always needs footer, as we are going to have an 'OK' button.
20014 this.footer = this.list.createChild({cls:cls+'-ft'});
20015 this.pageTb = new Roo.Toolbar(this.footer);
20020 handler: function()
20026 if ( this.allowBlank && !this.disableClear) {
20028 this.pageTb.add(new Roo.Toolbar.Fill(), {
20029 cls: 'x-btn-icon x-btn-clear',
20031 handler: function()
20034 _this.clearValue();
20035 _this.onSelect(false, -1);
20040 this.assetHeight += this.footer.getHeight();
20044 onRenderList : function ( cls, i)
20047 var lw = Math.floor(
20048 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20051 this.list.setWidth(lw); // default to '1'
20053 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20054 //il.on('mouseover', this.onViewOver, this, { list: i });
20055 //il.on('mousemove', this.onViewMove, this, { list: i });
20057 il.setStyle({ 'overflow-x' : 'hidden'});
20060 this.tpl = new Roo.Template({
20061 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20062 isEmpty: function (value, allValues) {
20064 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20065 return dl ? 'has-children' : 'no-children'
20070 var store = this.store;
20072 store = new Roo.data.SimpleStore({
20073 //fields : this.store.reader.meta.fields,
20074 reader : this.store.reader,
20078 this.stores[i] = store;
20080 var view = this.views[i] = new Roo.View(
20086 selectedClass: this.selectedClass
20089 view.getEl().setWidth(lw);
20090 view.getEl().setStyle({
20091 position: i < 1 ? 'relative' : 'absolute',
20093 left: (i * lw ) + 'px',
20094 display : i > 0 ? 'none' : 'block'
20096 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20097 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20098 //view.on('click', this.onViewClick, this, { list : i });
20100 store.on('beforeload', this.onBeforeLoad, this);
20101 store.on('load', this.onLoad, this, { list : i});
20102 store.on('loadexception', this.onLoadException, this);
20104 // hide the other vies..
20110 restrictHeight : function()
20113 Roo.each(this.innerLists, function(il,i) {
20114 var el = this.views[i].getEl();
20115 el.dom.style.height = '';
20116 var inner = el.dom;
20117 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20118 // only adjust heights on other ones..
20119 mh = Math.max(h, mh);
20122 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20123 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20130 this.list.beginUpdate();
20131 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20132 this.list.alignTo(this.el, this.listAlign);
20133 this.list.endUpdate();
20138 // -- store handlers..
20140 onBeforeLoad : function()
20142 if(!this.hasFocus){
20145 this.innerLists[0].update(this.loadingText ?
20146 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20147 this.restrictHeight();
20148 this.selectedIndex = -1;
20151 onLoad : function(a,b,c,d)
20153 if (!this.loadingChildren) {
20154 // then we are loading the top level. - hide the children
20155 for (var i = 1;i < this.views.length; i++) {
20156 this.views[i].getEl().setStyle({ display : 'none' });
20158 var lw = Math.floor(
20159 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20162 this.list.setWidth(lw); // default to '1'
20166 if(!this.hasFocus){
20170 if(this.store.getCount() > 0) {
20172 this.restrictHeight();
20174 this.onEmptyResults();
20177 if (!this.loadingChildren) {
20178 this.selectActive();
20181 this.stores[1].loadData([]);
20182 this.stores[2].loadData([]);
20191 onLoadException : function()
20194 Roo.log(this.store.reader.jsonData);
20195 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20196 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20201 // no cleaning of leading spaces on blur here.
20202 cleanLeadingSpace : function(e) { },
20205 onSelectChange : function (view, sels, opts )
20207 var ix = view.getSelectedIndexes();
20209 if (opts.list > this.maxColumns - 2) {
20210 if (view.store.getCount()< 1) {
20211 this.views[opts.list ].getEl().setStyle({ display : 'none' });
20215 // used to clear ?? but if we are loading unselected
20216 this.setFromData(view.store.getAt(ix[0]).data);
20225 // this get's fired when trigger opens..
20226 // this.setFromData({});
20227 var str = this.stores[opts.list+1];
20228 str.data.clear(); // removeall wihtout the fire events..
20232 var rec = view.store.getAt(ix[0]);
20234 this.setFromData(rec.data);
20235 this.fireEvent('select', this, rec, ix[0]);
20237 var lw = Math.floor(
20239 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20240 ) / this.maxColumns
20242 this.loadingChildren = true;
20243 this.stores[opts.list+1].loadDataFromChildren( rec );
20244 this.loadingChildren = false;
20245 var dl = this.stores[opts.list+1]. getTotalCount();
20247 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20249 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20250 for (var i = opts.list+2; i < this.views.length;i++) {
20251 this.views[i].getEl().setStyle({ display : 'none' });
20254 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20255 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20257 if (this.isLoading) {
20258 // this.selectActive(opts.list);
20266 onDoubleClick : function()
20268 this.collapse(); //??
20276 recordToStack : function(store, prop, value, stack)
20278 var cstore = new Roo.data.SimpleStore({
20279 //fields : this.store.reader.meta.fields, // we need array reader.. for
20280 reader : this.store.reader,
20284 var record = false;
20286 if(store.getCount() < 1){
20289 store.each(function(r){
20290 if(r.data[prop] == value){
20295 if (r.data.cn && r.data.cn.length) {
20296 cstore.loadDataFromChildren( r);
20297 var cret = _this.recordToStack(cstore, prop, value, stack);
20298 if (cret !== false) {
20307 if (record == false) {
20310 stack.unshift(srec);
20315 * find the stack of stores that match our value.
20320 selectActive : function ()
20322 // if store is not loaded, then we will need to wait for that to happen first.
20324 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20325 for (var i = 0; i < stack.length; i++ ) {
20326 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20338 * Ext JS Library 1.1.1
20339 * Copyright(c) 2006-2007, Ext JS, LLC.
20341 * Originally Released Under LGPL - original licence link has changed is not relivant.
20344 * <script type="text/javascript">
20347 * @class Roo.form.Checkbox
20348 * @extends Roo.form.Field
20349 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20351 * Creates a new Checkbox
20352 * @param {Object} config Configuration options
20354 Roo.form.Checkbox = function(config){
20355 Roo.form.Checkbox.superclass.constructor.call(this, config);
20359 * Fires when the checkbox is checked or unchecked.
20360 * @param {Roo.form.Checkbox} this This checkbox
20361 * @param {Boolean} checked The new checked value
20367 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20369 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20371 focusClass : undefined,
20373 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20375 fieldClass: "x-form-field",
20377 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20381 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20382 * {tag: "input", type: "checkbox", autocomplete: "off"})
20384 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20386 * @cfg {String} boxLabel The text that appears beside the checkbox
20390 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20394 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20396 valueOff: '0', // value when not checked..
20398 actionMode : 'viewEl',
20401 itemCls : 'x-menu-check-item x-form-item',
20402 groupClass : 'x-menu-group-item',
20403 inputType : 'hidden',
20406 inSetChecked: false, // check that we are not calling self...
20408 inputElement: false, // real input element?
20409 basedOn: false, // ????
20411 isFormField: true, // not sure where this is needed!!!!
20413 onResize : function(){
20414 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20415 if(!this.boxLabel){
20416 this.el.alignTo(this.wrap, 'c-c');
20420 initEvents : function(){
20421 Roo.form.Checkbox.superclass.initEvents.call(this);
20422 this.el.on("click", this.onClick, this);
20423 this.el.on("change", this.onClick, this);
20427 getResizeEl : function(){
20431 getPositionEl : function(){
20436 onRender : function(ct, position){
20437 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20439 if(this.inputValue !== undefined){
20440 this.el.dom.value = this.inputValue;
20443 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20444 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20445 var viewEl = this.wrap.createChild({
20446 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20447 this.viewEl = viewEl;
20448 this.wrap.on('click', this.onClick, this);
20450 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20451 this.el.on('propertychange', this.setFromHidden, this); //ie
20456 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20457 // viewEl.on('click', this.onClick, this);
20459 //if(this.checked){
20460 this.setChecked(this.checked);
20462 //this.checked = this.el.dom;
20468 initValue : Roo.emptyFn,
20471 * Returns the checked state of the checkbox.
20472 * @return {Boolean} True if checked, else false
20474 getValue : function(){
20476 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20478 return this.valueOff;
20483 onClick : function(){
20484 if (this.disabled) {
20487 this.setChecked(!this.checked);
20489 //if(this.el.dom.checked != this.checked){
20490 // this.setValue(this.el.dom.checked);
20495 * Sets the checked state of the checkbox.
20496 * On is always based on a string comparison between inputValue and the param.
20497 * @param {Boolean/String} value - the value to set
20498 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20500 setValue : function(v,suppressEvent){
20503 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20504 //if(this.el && this.el.dom){
20505 // this.el.dom.checked = this.checked;
20506 // this.el.dom.defaultChecked = this.checked;
20508 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20509 //this.fireEvent("check", this, this.checked);
20512 setChecked : function(state,suppressEvent)
20514 if (this.inSetChecked) {
20515 this.checked = state;
20521 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20523 this.checked = state;
20524 if(suppressEvent !== true){
20525 this.fireEvent('check', this, state);
20527 this.inSetChecked = true;
20528 this.el.dom.value = state ? this.inputValue : this.valueOff;
20529 this.inSetChecked = false;
20532 // handle setting of hidden value by some other method!!?!?
20533 setFromHidden: function()
20538 //console.log("SET FROM HIDDEN");
20539 //alert('setFrom hidden');
20540 this.setValue(this.el.dom.value);
20543 onDestroy : function()
20546 Roo.get(this.viewEl).remove();
20549 Roo.form.Checkbox.superclass.onDestroy.call(this);
20552 setBoxLabel : function(str)
20554 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20559 * Ext JS Library 1.1.1
20560 * Copyright(c) 2006-2007, Ext JS, LLC.
20562 * Originally Released Under LGPL - original licence link has changed is not relivant.
20565 * <script type="text/javascript">
20569 * @class Roo.form.Radio
20570 * @extends Roo.form.Checkbox
20571 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20572 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20574 * Creates a new Radio
20575 * @param {Object} config Configuration options
20577 Roo.form.Radio = function(){
20578 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20580 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20581 inputType: 'radio',
20584 * If this radio is part of a group, it will return the selected value
20587 getGroupValue : function(){
20588 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20592 onRender : function(ct, position){
20593 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20595 if(this.inputValue !== undefined){
20596 this.el.dom.value = this.inputValue;
20599 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20600 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20601 //var viewEl = this.wrap.createChild({
20602 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20603 //this.viewEl = viewEl;
20604 //this.wrap.on('click', this.onClick, this);
20606 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20607 //this.el.on('propertychange', this.setFromHidden, this); //ie
20612 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20613 // viewEl.on('click', this.onClick, this);
20616 this.el.dom.checked = 'checked' ;
20622 });Roo.rtf = {}; // namespace
20623 Roo.rtf.Hex = function(hex)
20627 Roo.rtf.Paragraph = function(opts)
20629 this.content = []; ///??? is that used?
20630 };Roo.rtf.Span = function(opts)
20632 this.value = opts.value;
20635 Roo.rtf.Group = function(parent)
20637 // we dont want to acutally store parent - it will make debug a nightmare..
20645 Roo.rtf.Group.prototype = {
20649 addContent : function(node) {
20650 // could set styles...
20651 this.content.push(node);
20653 addChild : function(cn)
20657 // only for images really...
20658 toDataURL : function()
20660 var mimetype = false;
20662 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
20663 mimetype = "image/png";
20665 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20666 mimetype = "image/jpeg";
20669 return 'about:blank'; // ?? error?
20673 var hexstring = this.content[this.content.length-1].value;
20675 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20676 return String.fromCharCode(parseInt(a, 16));
20681 // this looks like it's normally the {rtf{ .... }}
20682 Roo.rtf.Document = function()
20684 // we dont want to acutally store parent - it will make debug a nightmare..
20690 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
20691 addChild : function(cn)
20695 case 'rtlch': // most content seems to be inside this??
20698 this.rtlch.push(cn);
20701 this[cn.type] = cn;
20706 getElementsByType : function(type)
20709 this._getElementsByType(type, ret, this.cn, 'rtf');
20712 _getElementsByType : function (type, ret, search_array, path)
20714 search_array.forEach(function(n,i) {
20715 if (n.type == type) {
20716 n.path = path + '/' + n.type + ':' + i;
20719 if (n.cn.length > 0) {
20720 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20727 Roo.rtf.Ctrl = function(opts)
20729 this.value = opts.value;
20730 this.param = opts.param;
20735 * based on this https://github.com/iarna/rtf-parser
20736 * it's really only designed to extract pict from pasted RTF
20740 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20749 Roo.rtf.Parser = function(text) {
20750 //super({objectMode: true})
20752 this.parserState = this.parseText;
20754 // these are for interpeter...
20756 ///this.parserState = this.parseTop
20757 this.groupStack = [];
20758 this.hexStore = [];
20761 this.groups = []; // where we put the return.
20763 for (var ii = 0; ii < text.length; ++ii) {
20766 if (text[ii] === '\n') {
20772 this.parserState(text[ii]);
20778 Roo.rtf.Parser.prototype = {
20779 text : '', // string being parsed..
20781 controlWordParam : '',
20785 groupStack : false,
20790 row : 1, // reportin?
20794 push : function (el)
20796 var m = 'cmd'+ el.type;
20797 if (typeof(this[m]) == 'undefined') {
20798 Roo.log('invalid cmd:' + el.type);
20804 flushHexStore : function()
20806 if (this.hexStore.length < 1) {
20809 var hexstr = this.hexStore.map(
20814 this.group.addContent( new Roo.rtf.Hex( hexstr ));
20817 this.hexStore.splice(0)
20821 cmdgroupstart : function()
20823 this.flushHexStore();
20825 this.groupStack.push(this.group);
20828 if (this.doc === false) {
20829 this.group = this.doc = new Roo.rtf.Document();
20833 this.group = new Roo.rtf.Group(this.group);
20835 cmdignorable : function()
20837 this.flushHexStore();
20838 this.group.ignorable = true;
20840 cmdendparagraph : function()
20842 this.flushHexStore();
20843 this.group.addContent(new Roo.rtf.Paragraph());
20845 cmdgroupend : function ()
20847 this.flushHexStore();
20848 var endingGroup = this.group;
20851 this.group = this.groupStack.pop();
20853 this.group.addChild(endingGroup);
20858 var doc = this.group || this.doc;
20859 //if (endingGroup instanceof FontTable) {
20860 // doc.fonts = endingGroup.table
20861 //} else if (endingGroup instanceof ColorTable) {
20862 // doc.colors = endingGroup.table
20863 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20864 if (endingGroup.ignorable === false) {
20866 this.groups.push(endingGroup);
20867 // Roo.log( endingGroup );
20869 //Roo.each(endingGroup.content, function(item)) {
20870 // doc.addContent(item);
20872 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20875 cmdtext : function (cmd)
20877 this.flushHexStore();
20878 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20879 //this.group = this.doc
20880 return; // we really don't care about stray text...
20882 this.group.addContent(new Roo.rtf.Span(cmd));
20884 cmdcontrolword : function (cmd)
20886 this.flushHexStore();
20887 if (!this.group.type) {
20888 this.group.type = cmd.value;
20891 this.group.addContent(new Roo.rtf.Ctrl(cmd));
20892 // we actually don't care about ctrl words...
20895 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20896 if (this[method]) {
20897 this[method](cmd.param)
20899 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20903 cmdhexchar : function(cmd) {
20904 this.hexStore.push(cmd);
20906 cmderror : function(cmd) {
20912 if (this.text !== '\u0000') this.emitText()
20918 parseText : function(c)
20921 this.parserState = this.parseEscapes;
20922 } else if (c === '{') {
20923 this.emitStartGroup();
20924 } else if (c === '}') {
20925 this.emitEndGroup();
20926 } else if (c === '\x0A' || c === '\x0D') {
20927 // cr/lf are noise chars
20933 parseEscapes: function (c)
20935 if (c === '\\' || c === '{' || c === '}') {
20937 this.parserState = this.parseText;
20939 this.parserState = this.parseControlSymbol;
20940 this.parseControlSymbol(c);
20943 parseControlSymbol: function(c)
20946 this.text += '\u00a0'; // nbsp
20947 this.parserState = this.parseText
20948 } else if (c === '-') {
20949 this.text += '\u00ad'; // soft hyphen
20950 } else if (c === '_') {
20951 this.text += '\u2011'; // non-breaking hyphen
20952 } else if (c === '*') {
20953 this.emitIgnorable();
20954 this.parserState = this.parseText;
20955 } else if (c === "'") {
20956 this.parserState = this.parseHexChar;
20957 } else if (c === '|') { // formula cacter
20958 this.emitFormula();
20959 this.parserState = this.parseText;
20960 } else if (c === ':') { // subentry in an index entry
20961 this.emitIndexSubEntry();
20962 this.parserState = this.parseText;
20963 } else if (c === '\x0a') {
20964 this.emitEndParagraph();
20965 this.parserState = this.parseText;
20966 } else if (c === '\x0d') {
20967 this.emitEndParagraph();
20968 this.parserState = this.parseText;
20970 this.parserState = this.parseControlWord;
20971 this.parseControlWord(c);
20974 parseHexChar: function (c)
20976 if (/^[A-Fa-f0-9]$/.test(c)) {
20978 if (this.hexChar.length >= 2) {
20979 this.emitHexChar();
20980 this.parserState = this.parseText;
20984 this.emitError("Invalid character \"" + c + "\" in hex literal.");
20985 this.parserState = this.parseText;
20988 parseControlWord : function(c)
20991 this.emitControlWord();
20992 this.parserState = this.parseText;
20993 } else if (/^[-\d]$/.test(c)) {
20994 this.parserState = this.parseControlWordParam;
20995 this.controlWordParam += c;
20996 } else if (/^[A-Za-z]$/.test(c)) {
20997 this.controlWord += c;
20999 this.emitControlWord();
21000 this.parserState = this.parseText;
21004 parseControlWordParam : function (c) {
21005 if (/^\d$/.test(c)) {
21006 this.controlWordParam += c;
21007 } else if (c === ' ') {
21008 this.emitControlWord();
21009 this.parserState = this.parseText;
21011 this.emitControlWord();
21012 this.parserState = this.parseText;
21020 emitText : function () {
21021 if (this.text === '') {
21033 emitControlWord : function ()
21036 if (this.controlWord === '') {
21037 // do we want to track this - it seems just to cause problems.
21038 //this.emitError('empty control word');
21041 type: 'controlword',
21042 value: this.controlWord,
21043 param: this.controlWordParam !== '' && Number(this.controlWordParam),
21049 this.controlWord = '';
21050 this.controlWordParam = '';
21052 emitStartGroup : function ()
21056 type: 'groupstart',
21062 emitEndGroup : function ()
21072 emitIgnorable : function ()
21082 emitHexChar : function ()
21087 value: this.hexChar,
21094 emitError : function (message)
21102 char: this.cpos //,
21103 //stack: new Error().stack
21106 emitEndParagraph : function () {
21109 type: 'endparagraph',
21117 Roo.htmleditor = {};
21120 * @class Roo.htmleditor.Filter
21121 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21122 * @cfg {DomElement} node The node to iterate and filter
21123 * @cfg {boolean|String|Array} tag Tags to replace
21125 * Create a new Filter.
21126 * @param {Object} config Configuration options
21131 Roo.htmleditor.Filter = function(cfg) {
21132 Roo.apply(this.cfg);
21133 // this does not actually call walk as it's really just a abstract class
21137 Roo.htmleditor.Filter.prototype = {
21143 // overrride to do replace comments.
21144 replaceComment : false,
21146 // overrride to do replace or do stuff with tags..
21147 replaceTag : false,
21149 walk : function(dom)
21151 Roo.each( Array.from(dom.childNodes), function( e ) {
21154 case e.nodeType == 8 && this.replaceComment !== false: // comment
21155 this.replaceComment(e);
21158 case e.nodeType != 1: //not a node.
21161 case this.tag === true: // everything
21162 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21163 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21164 if (this.replaceTag && false === this.replaceTag(e)) {
21167 if (e.hasChildNodes()) {
21172 default: // tags .. that do not match.
21173 if (e.hasChildNodes()) {
21184 * @class Roo.htmleditor.FilterAttributes
21185 * clean attributes and styles including http:// etc.. in attribute
21187 * Run a new Attribute Filter
21188 * @param {Object} config Configuration options
21190 Roo.htmleditor.FilterAttributes = function(cfg)
21192 Roo.apply(this, cfg);
21193 this.attrib_black = this.attrib_black || [];
21194 this.attrib_white = this.attrib_white || [];
21196 this.attrib_clean = this.attrib_clean || [];
21197 this.style_white = this.style_white || [];
21198 this.style_black = this.style_black || [];
21199 this.walk(cfg.node);
21202 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21204 tag: true, // all tags
21206 attrib_black : false, // array
21207 attrib_clean : false,
21208 attrib_white : false,
21210 style_white : false,
21211 style_black : false,
21214 replaceTag : function(node)
21216 if (!node.attributes || !node.attributes.length) {
21220 for (var i = node.attributes.length-1; i > -1 ; i--) {
21221 var a = node.attributes[i];
21223 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21224 node.removeAttribute(a.name);
21230 if (a.name.toLowerCase().substr(0,2)=='on') {
21231 node.removeAttribute(a.name);
21236 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21237 node.removeAttribute(a.name);
21240 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21241 this.cleanAttr(node,a.name,a.value); // fixme..
21244 if (a.name == 'style') {
21245 this.cleanStyle(node,a.name,a.value);
21248 /// clean up MS crap..
21249 // tecnically this should be a list of valid class'es..
21252 if (a.name == 'class') {
21253 if (a.value.match(/^Mso/)) {
21254 node.removeAttribute('class');
21257 if (a.value.match(/^body$/)) {
21258 node.removeAttribute('class');
21268 return true; // clean children
21271 cleanAttr: function(node, n,v)
21274 if (v.match(/^\./) || v.match(/^\//)) {
21277 if (v.match(/^(http|https):\/\//)
21278 || v.match(/^mailto:/)
21279 || v.match(/^ftp:/)
21280 || v.match(/^data:/)
21284 if (v.match(/^#/)) {
21287 if (v.match(/^\{/)) { // allow template editing.
21290 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21291 node.removeAttribute(n);
21294 cleanStyle : function(node, n,v)
21296 if (v.match(/expression/)) { //XSS?? should we even bother..
21297 node.removeAttribute(n);
21301 var parts = v.split(/;/);
21304 Roo.each(parts, function(p) {
21305 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21309 var l = p.split(':').shift().replace(/\s+/g,'');
21310 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21312 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21316 // only allow 'c whitelisted system attributes'
21317 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21325 if (clean.length) {
21326 node.setAttribute(n, clean.join(';'));
21328 node.removeAttribute(n);
21337 * @class Roo.htmleditor.FilterBlack
21338 * remove blacklisted elements.
21340 * Run a new Blacklisted Filter
21341 * @param {Object} config Configuration options
21344 Roo.htmleditor.FilterBlack = function(cfg)
21346 Roo.apply(this, cfg);
21347 this.walk(cfg.node);
21350 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21352 tag : true, // all elements.
21354 replaceTag : function(n)
21356 n.parentNode.removeChild(n);
21360 * @class Roo.htmleditor.FilterComment
21363 * Run a new Comments Filter
21364 * @param {Object} config Configuration options
21366 Roo.htmleditor.FilterComment = function(cfg)
21368 this.walk(cfg.node);
21371 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21374 replaceComment : function(n)
21376 n.parentNode.removeChild(n);
21379 * @class Roo.htmleditor.FilterKeepChildren
21380 * remove tags but keep children
21382 * Run a new Keep Children Filter
21383 * @param {Object} config Configuration options
21386 Roo.htmleditor.FilterKeepChildren = function(cfg)
21388 Roo.apply(this, cfg);
21389 if (this.tag === false) {
21390 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21392 this.walk(cfg.node);
21395 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21399 replaceTag : function(node)
21401 // walk children...
21403 var ar = Array.from(node.childNodes);
21405 for (var i = 0; i < ar.length; i++) {
21406 if (ar[i].nodeType == 1) {
21408 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21409 || // array and it matches
21410 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21412 this.replaceTag(ar[i]); // child is blacklisted as well...
21417 ar = Array.from(node.childNodes);
21418 for (var i = 0; i < ar.length; i++) {
21420 node.removeChild(ar[i]);
21421 // what if we need to walk these???
21422 node.parentNode.insertBefore(ar[i], node);
21423 if (this.tag !== false) {
21428 node.parentNode.removeChild(node);
21429 return false; // don't walk children
21434 * @class Roo.htmleditor.FilterParagraph
21435 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21436 * like on 'push' to remove the <p> tags and replace them with line breaks.
21438 * Run a new Paragraph Filter
21439 * @param {Object} config Configuration options
21442 Roo.htmleditor.FilterParagraph = function(cfg)
21444 // no need to apply config.
21445 this.walk(cfg.node);
21448 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21455 replaceTag : function(node)
21458 if (node.childNodes.length == 1 &&
21459 node.childNodes[0].nodeType == 3 &&
21460 node.childNodes[0].textContent.trim().length < 1
21462 // remove and replace with '<BR>';
21463 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21464 return false; // no need to walk..
21466 var ar = Array.from(node.childNodes);
21467 for (var i = 0; i < ar.length; i++) {
21468 node.removeChild(ar[i]);
21469 // what if we need to walk these???
21470 node.parentNode.insertBefore(ar[i], node);
21472 // now what about this?
21476 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21477 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21478 node.parentNode.removeChild(node);
21485 * @class Roo.htmleditor.FilterSpan
21486 * filter span's with no attributes out..
21488 * Run a new Span Filter
21489 * @param {Object} config Configuration options
21492 Roo.htmleditor.FilterSpan = function(cfg)
21494 // no need to apply config.
21495 this.walk(cfg.node);
21498 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21504 replaceTag : function(node)
21506 if (node.attributes && node.attributes.length > 0) {
21507 return true; // walk if there are any.
21509 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21515 * @class Roo.htmleditor.FilterTableWidth
21516 try and remove table width data - as that frequently messes up other stuff.
21518 * was cleanTableWidths.
21520 * Quite often pasting from word etc.. results in tables with column and widths.
21521 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21524 * Run a new Table Filter
21525 * @param {Object} config Configuration options
21528 Roo.htmleditor.FilterTableWidth = function(cfg)
21530 // no need to apply config.
21531 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21532 this.walk(cfg.node);
21535 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21540 replaceTag: function(node) {
21544 if (node.hasAttribute('width')) {
21545 node.removeAttribute('width');
21549 if (node.hasAttribute("style")) {
21552 var styles = node.getAttribute("style").split(";");
21554 Roo.each(styles, function(s) {
21555 if (!s.match(/:/)) {
21558 var kv = s.split(":");
21559 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21562 // what ever is left... we allow.
21565 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21566 if (!nstyle.length) {
21567 node.removeAttribute('style');
21571 return true; // continue doing children..
21574 * @class Roo.htmleditor.FilterWord
21575 * try and clean up all the mess that Word generates.
21577 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
21580 * Run a new Span Filter
21581 * @param {Object} config Configuration options
21584 Roo.htmleditor.FilterWord = function(cfg)
21586 // no need to apply config.
21587 this.replaceDocBullets(cfg.node);
21589 // this.walk(cfg.node);
21594 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21600 * Clean up MS wordisms...
21602 replaceTag : function(node)
21605 // no idea what this does - span with text, replaceds with just text.
21607 node.nodeName == 'SPAN' &&
21608 !node.hasAttributes() &&
21609 node.childNodes.length == 1 &&
21610 node.firstChild.nodeName == "#text"
21612 var textNode = node.firstChild;
21613 node.removeChild(textNode);
21614 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21615 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21617 node.parentNode.insertBefore(textNode, node);
21618 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21619 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21622 node.parentNode.removeChild(node);
21623 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21628 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21629 node.parentNode.removeChild(node);
21630 return false; // dont do chidlren
21632 //Roo.log(node.tagName);
21633 // remove - but keep children..
21634 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21635 //Roo.log('-- removed');
21636 while (node.childNodes.length) {
21637 var cn = node.childNodes[0];
21638 node.removeChild(cn);
21639 node.parentNode.insertBefore(cn, node);
21640 // move node to parent - and clean it..
21641 if (cn.nodeType == 1) {
21642 this.replaceTag(cn);
21646 node.parentNode.removeChild(node);
21647 /// no need to iterate chidlren = it's got none..
21648 //this.iterateChildren(node, this.cleanWord);
21649 return false; // no need to iterate children.
21652 if (node.className.length) {
21654 var cn = node.className.split(/\W+/);
21656 Roo.each(cn, function(cls) {
21657 if (cls.match(/Mso[a-zA-Z]+/)) {
21662 node.className = cna.length ? cna.join(' ') : '';
21664 node.removeAttribute("class");
21668 if (node.hasAttribute("lang")) {
21669 node.removeAttribute("lang");
21672 if (node.hasAttribute("style")) {
21674 var styles = node.getAttribute("style").split(";");
21676 Roo.each(styles, function(s) {
21677 if (!s.match(/:/)) {
21680 var kv = s.split(":");
21681 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21684 // what ever is left... we allow.
21687 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21688 if (!nstyle.length) {
21689 node.removeAttribute('style');
21692 return true; // do children
21698 styleToObject: function(node)
21700 var styles = (node.getAttribute("style") || '').split(";");
21702 Roo.each(styles, function(s) {
21703 if (!s.match(/:/)) {
21706 var kv = s.split(":");
21708 // what ever is left... we allow.
21709 ret[kv[0]] = kv[1];
21715 replaceDocBullets : function(doc)
21717 // this is a bit odd - but it appears some indents use ql-indent-1
21719 var listpara = doc.getElementsByClassName('ql-indent-1');
21720 while(listpara.length) {
21721 this.replaceDocBullet(listpara.item(0));
21724 var listpara = doc.getElementsByClassName('MsoListParagraph');
21725 while(listpara.length) {
21726 this.replaceDocBullet(listpara.item(0));
21730 replaceDocBullet : function(p)
21732 // gather all the siblings.
21734 parent = p.parentNode,
21735 doc = parent.ownerDocument,
21738 if (ns.nodeType != 1) {
21739 ns = ns.nextSibling;
21742 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
21746 ns = ns.nextSibling;
21749 var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
21750 parent.insertBefore(ul, p);
21752 var stack = [ ul ];
21753 var last_li = false;
21754 items.forEach(function(n) {
21755 //Roo.log("got innertHMLT=" + n.innerHTML);
21757 var spans = n.getElementsByTagName('span');
21758 if (!spans.length) {
21759 //Roo.log("No spans found");
21761 parent.removeChild(n);
21762 return; // skip it...
21768 for(var i = 0; i < spans.length; i++) {
21770 style = this.styleToObject(spans[i]);
21771 if (typeof(style['mso-list']) == 'undefined') {
21775 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
21778 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
21779 style = this.styleToObject(n); // mo-list is from the parent node.
21780 if (typeof(style['mso-list']) == 'undefined') {
21781 //Roo.log("parent is missing level");
21782 parent.removeChild(n);
21786 var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
21789 var nul = doc.createElement('ul'); // what about number lists...
21790 last_li.appendChild(nul);
21795 var nli = stack[nlvl].appendChild(doc.createElement('li'));
21797 nli.innerHTML = n.innerHTML;
21798 //Roo.log("innerHTML = " + n.innerHTML);
21799 parent.removeChild(n);
21801 // copy children of p into nli
21802 /*while(n.firstChild) {
21803 var fc = n.firstChild;
21805 nli.appendChild(fc);
21820 * @class Roo.htmleditor.FilterStyleToTag
21821 * part of the word stuff... - certain 'styles' should be converted to tags.
21823 * font-weight: bold -> bold
21824 * ?? super / subscrit etc..
21827 * Run a new style to tag filter.
21828 * @param {Object} config Configuration options
21830 Roo.htmleditor.FilterStyleToTag = function(cfg)
21834 B : [ 'fontWeight' , 'bold'],
21835 I : [ 'fontStyle' , 'italic'],
21836 //pre : [ 'font-style' , 'italic'],
21837 // h1.. h6 ?? font-size?
21838 SUP : [ 'verticalAlign' , 'super' ],
21839 SUB : [ 'verticalAlign' , 'sub' ]
21844 Roo.apply(this, cfg);
21847 this.walk(cfg.node);
21854 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21856 tag: true, // all tags
21861 replaceTag : function(node)
21865 if (node.getAttribute("style") === null) {
21869 for (var k in this.tags) {
21870 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21872 node.style.removeProperty(this.tags[k][0]);
21875 if (!inject.length) {
21878 var cn = Array.from(node.childNodes);
21880 Roo.each(inject, function(t) {
21881 var nc = node.ownerDocument.createElement(t);
21882 nn.appendChild(nc);
21885 for(var i = 0;i < cn.length;cn++) {
21886 node.removeChild(cn[i]);
21887 nn.appendChild(cn[i]);
21889 return true /// iterate thru
21893 * @class Roo.htmleditor.FilterLongBr
21894 * BR/BR/BR - keep a maximum of 2...
21896 * Run a new Long BR Filter
21897 * @param {Object} config Configuration options
21900 Roo.htmleditor.FilterLongBr = function(cfg)
21902 // no need to apply config.
21903 this.walk(cfg.node);
21906 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21913 replaceTag : function(node)
21916 var ps = node.nextSibling;
21917 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21918 ps = ps.nextSibling;
21921 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
21922 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21926 if (!ps || ps.nodeType != 1) {
21930 if (!ps || ps.tagName != 'BR') {
21939 if (!node.previousSibling) {
21942 var ps = node.previousSibling;
21944 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21945 ps = ps.previousSibling;
21947 if (!ps || ps.nodeType != 1) {
21950 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21951 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21955 node.parentNode.removeChild(node); // remove me...
21957 return false; // no need to do children
21964 * @class Roo.htmleditor.FilterBlock
21965 * removes id / data-block and contenteditable that are associated with blocks
21966 * usage should be done on a cloned copy of the dom
21968 * Run a new Attribute Filter { node : xxxx }}
21969 * @param {Object} config Configuration options
21971 Roo.htmleditor.FilterBlock = function(cfg)
21973 Roo.apply(this, cfg);
21974 var qa = cfg.node.querySelectorAll;
21975 this.removeAttributes('data-block');
21976 this.removeAttributes('contenteditable');
21977 this.removeAttributes('id');
21981 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21983 node: true, // all tags
21986 removeAttributes : function(attr)
21988 var ar = this.node.querySelectorAll('*[' + attr + ']');
21989 for (var i =0;i<ar.length;i++) {
21990 ar[i].removeAttribute(attr);
21999 * This is based loosely on tinymce
22000 * @class Roo.htmleditor.TidySerializer
22001 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22003 * @method Serializer
22004 * @param {Object} settings Name/value settings object.
22008 Roo.htmleditor.TidySerializer = function(settings)
22010 Roo.apply(this, settings);
22012 this.writer = new Roo.htmleditor.TidyWriter(settings);
22017 Roo.htmleditor.TidySerializer.prototype = {
22020 * @param {boolean} inner do the inner of the node.
22027 * Serializes the specified node into a string.
22030 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
22031 * @method serialize
22032 * @param {DomElement} node Node instance to serialize.
22033 * @return {String} String with HTML based on DOM tree.
22035 serialize : function(node) {
22037 // = settings.validate;
22038 var writer = this.writer;
22042 3: function(node) {
22044 writer.text(node.nodeValue, node);
22047 8: function(node) {
22048 writer.comment(node.nodeValue);
22050 // Processing instruction
22051 7: function(node) {
22052 writer.pi(node.name, node.nodeValue);
22055 10: function(node) {
22056 writer.doctype(node.nodeValue);
22059 4: function(node) {
22060 writer.cdata(node.nodeValue);
22062 // Document fragment
22063 11: function(node) {
22064 node = node.firstChild;
22070 node = node.nextSibling
22075 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
22076 return writer.getContent();
22079 walk: function(node)
22081 var attrName, attrValue, sortedAttrs, i, l, elementRule,
22082 handler = this.handlers[node.nodeType];
22089 var name = node.nodeName;
22090 var isEmpty = node.childNodes.length < 1;
22092 var writer = this.writer;
22093 var attrs = node.attributes;
22096 writer.start(node.nodeName, attrs, isEmpty, node);
22100 node = node.firstChild;
22107 node = node.nextSibling;
22113 // Serialize element and treat all non elements as fragments
22118 * This is based loosely on tinymce
22119 * @class Roo.htmleditor.TidyWriter
22120 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22123 * - not tested much with 'PRE' formated elements.
22129 Roo.htmleditor.TidyWriter = function(settings)
22132 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
22133 Roo.apply(this, settings);
22137 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
22140 Roo.htmleditor.TidyWriter.prototype = {
22147 // part of state...
22151 last_inline : false,
22156 * Writes the a start element such as <p id="a">.
22159 * @param {String} name Name of the element.
22160 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22161 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22163 start: function(name, attrs, empty, node)
22165 var i, l, attr, value;
22167 // there are some situations where adding line break && indentation will not work. will not work.
22168 // <span / b / i ... formating?
22170 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22171 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22173 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22175 var add_lb = name == 'BR' ? false : in_inline;
22177 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22181 var indentstr = this.indentstr;
22183 // e_inline = elements that can be inline, but still allow \n before and after?
22184 // only 'BR' ??? any others?
22186 // ADD LINE BEFORE tage
22187 if (!this.in_pre) {
22190 if (name == 'BR') {
22192 } else if (this.lastElementEndsWS()) {
22195 // otherwise - no new line. (and dont indent.)
22206 this.html.push(indentstr + '<', name.toLowerCase());
22209 for (i = 0, l = attrs.length; i < l; i++) {
22211 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22217 this.html[this.html.length] = '/>';
22219 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22221 var e_inline = name == 'BR' ? false : this.in_inline;
22223 if (!e_inline && !this.in_pre) {
22230 this.html[this.html.length] = '>';
22232 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22234 if (!in_inline && !in_pre) {
22235 var cn = node.firstChild;
22237 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22241 cn = cn.nextSibling;
22249 indentstr : in_pre ? '' : (this.indentstr + this.indent),
22251 in_inline : in_inline
22253 // add a line after if we are not in a
22255 if (!in_inline && !in_pre) {
22264 lastElementEndsWS : function()
22266 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22267 if (value === false) {
22270 return value.match(/\s+$/);
22275 * Writes the a end element such as </p>.
22278 * @param {String} name Name of the element.
22280 end: function(name) {
22283 var indentstr = '';
22284 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22286 if (!this.in_pre && !in_inline) {
22288 indentstr = this.indentstr;
22290 this.html.push(indentstr + '</', name.toLowerCase(), '>');
22291 this.last_inline = in_inline;
22293 // pop the indent state..
22296 * Writes a text node.
22298 * In pre - we should not mess with the contents.
22302 * @param {String} text String to write out.
22303 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22305 text: function(in_text, node)
22307 // if not in whitespace critical
22308 if (in_text.length < 1) {
22311 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
22314 this.html[this.html.length] = text;
22318 if (this.in_inline) {
22319 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22321 text = text.replace(/\s+/,' '); // all white space to single white space
22324 // if next tag is '<BR>', then we can trim right..
22325 if (node.nextSibling &&
22326 node.nextSibling.nodeType == 1 &&
22327 node.nextSibling.nodeName == 'BR' )
22329 text = text.replace(/\s+$/g,'');
22331 // if previous tag was a BR, we can also trim..
22332 if (node.previousSibling &&
22333 node.previousSibling.nodeType == 1 &&
22334 node.previousSibling.nodeName == 'BR' )
22336 text = this.indentstr + text.replace(/^\s+/g,'');
22338 if (text.match(/\n/)) {
22339 text = text.replace(
22340 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22342 // remoeve the last whitespace / line break.
22343 text = text.replace(/\n\s+$/,'');
22345 // repace long lines
22349 this.html[this.html.length] = text;
22352 // see if previous element was a inline element.
22353 var indentstr = this.indentstr;
22355 text = text.replace(/\s+/g," "); // all whitespace into single white space.
22357 // should trim left?
22358 if (node.previousSibling &&
22359 node.previousSibling.nodeType == 1 &&
22360 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22366 text = text.replace(/^\s+/,''); // trim left
22369 // should trim right?
22370 if (node.nextSibling &&
22371 node.nextSibling.nodeType == 1 &&
22372 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22377 text = text.replace(/\s+$/,''); // trim right
22384 if (text.length < 1) {
22387 if (!text.match(/\n/)) {
22388 this.html.push(indentstr + text);
22392 text = this.indentstr + text.replace(
22393 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22395 // remoeve the last whitespace / line break.
22396 text = text.replace(/\s+$/,'');
22398 this.html.push(text);
22400 // split and indent..
22405 * Writes a cdata node such as <![CDATA[data]]>.
22408 * @param {String} text String to write out inside the cdata.
22410 cdata: function(text) {
22411 this.html.push('<![CDATA[', text, ']]>');
22414 * Writes a comment node such as <!-- Comment -->.
22417 * @param {String} text String to write out inside the comment.
22419 comment: function(text) {
22420 this.html.push('<!--', text, '-->');
22423 * Writes a PI node such as <?xml attr="value" ?>.
22426 * @param {String} name Name of the pi.
22427 * @param {String} text String to write out inside the pi.
22429 pi: function(name, text) {
22430 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22431 this.indent != '' && this.html.push('\n');
22434 * Writes a doctype node such as <!DOCTYPE data>.
22437 * @param {String} text String to write out inside the doctype.
22439 doctype: function(text) {
22440 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22443 * Resets the internal buffer if one wants to reuse the writer.
22447 reset: function() {
22448 this.html.length = 0;
22457 * Returns the contents that got serialized.
22459 * @method getContent
22460 * @return {String} HTML contents that got written down.
22462 getContent: function() {
22463 return this.html.join('').replace(/\n$/, '');
22466 pushState : function(cfg)
22468 this.state.push(cfg);
22469 Roo.apply(this, cfg);
22472 popState : function()
22474 if (this.state.length < 1) {
22475 return; // nothing to push
22482 if (this.state.length > 0) {
22483 cfg = this.state[this.state.length-1];
22485 Roo.apply(this, cfg);
22488 addLine: function()
22490 if (this.html.length < 1) {
22495 var value = this.html[this.html.length - 1];
22496 if (value.length > 0 && '\n' !== value) {
22497 this.html.push('\n');
22502 //'pre script noscript style textarea video audio iframe object code'
22503 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
22507 Roo.htmleditor.TidyWriter.inline_elements = [
22508 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22509 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22511 Roo.htmleditor.TidyWriter.shortend_elements = [
22512 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22513 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22516 Roo.htmleditor.TidyWriter.whitespace_elements = [
22517 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22519 * This is based loosely on tinymce
22520 * @class Roo.htmleditor.TidyEntities
22522 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22524 * Not 100% sure this is actually used or needed.
22527 Roo.htmleditor.TidyEntities = {
22530 * initialize data..
22532 init : function (){
22534 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22539 buildEntitiesLookup: function(items, radix) {
22540 var i, chr, entity, lookup = {};
22544 items = typeof(items) == 'string' ? items.split(',') : items;
22545 radix = radix || 10;
22546 // Build entities lookup table
22547 for (i = 0; i < items.length; i += 2) {
22548 chr = String.fromCharCode(parseInt(items[i], radix));
22549 // Only add non base entities
22550 if (!this.baseEntities[chr]) {
22551 entity = '&' + items[i + 1] + ';';
22552 lookup[chr] = entity;
22553 lookup[entity] = chr;
22592 // Needs to be escaped since the YUI compressor would otherwise break the code
22599 // Reverse lookup table for raw entities
22600 reverseEntities : {
22608 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22609 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22610 rawCharsRegExp : /[<>&\"\']/g,
22611 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22612 namedEntities : false,
22613 namedEntitiesData : [
23114 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
23116 * @method encodeRaw
23117 * @param {String} text Text to encode.
23118 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23119 * @return {String} Entity encoded text.
23121 encodeRaw: function(text, attr)
23124 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23125 return t.baseEntities[chr] || chr;
23129 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
23130 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
23131 * and is exposed as the DOMUtils.encode function.
23133 * @method encodeAllRaw
23134 * @param {String} text Text to encode.
23135 * @return {String} Entity encoded text.
23137 encodeAllRaw: function(text) {
23139 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
23140 return t.baseEntities[chr] || chr;
23144 * Encodes the specified string using numeric entities. The core entities will be
23145 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
23147 * @method encodeNumeric
23148 * @param {String} text Text to encode.
23149 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23150 * @return {String} Entity encoded text.
23152 encodeNumeric: function(text, attr) {
23154 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23155 // Multi byte sequence convert it to a single entity
23156 if (chr.length > 1) {
23157 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23159 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23163 * Encodes the specified string using named entities. The core entities will be encoded
23164 * as named ones but all non lower ascii characters will be encoded into named entities.
23166 * @method encodeNamed
23167 * @param {String} text Text to encode.
23168 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23169 * @param {Object} entities Optional parameter with entities to use.
23170 * @return {String} Entity encoded text.
23172 encodeNamed: function(text, attr, entities) {
23174 entities = entities || this.namedEntities;
23175 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23176 return t.baseEntities[chr] || entities[chr] || chr;
23180 * Returns an encode function based on the name(s) and it's optional entities.
23182 * @method getEncodeFunc
23183 * @param {String} name Comma separated list of encoders for example named,numeric.
23184 * @param {String} entities Optional parameter with entities to use instead of the built in set.
23185 * @return {function} Encode function to be used.
23187 getEncodeFunc: function(name, entities) {
23188 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23190 function encodeNamedAndNumeric(text, attr) {
23191 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23192 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23196 function encodeCustomNamed(text, attr) {
23197 return t.encodeNamed(text, attr, entities);
23199 // Replace + with , to be compatible with previous TinyMCE versions
23200 name = this.makeMap(name.replace(/\+/g, ','));
23201 // Named and numeric encoder
23202 if (name.named && name.numeric) {
23203 return this.encodeNamedAndNumeric;
23209 return encodeCustomNamed;
23211 return this.encodeNamed;
23214 if (name.numeric) {
23215 return this.encodeNumeric;
23218 return this.encodeRaw;
23221 * Decodes the specified string, this will replace entities with raw UTF characters.
23224 * @param {String} text Text to entity decode.
23225 * @return {String} Entity decoded string.
23227 decode: function(text)
23230 return text.replace(this.entityRegExp, function(all, numeric) {
23232 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23233 // Support upper UTF
23234 if (numeric > 65535) {
23236 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23238 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23240 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23243 nativeDecode : function (text) {
23246 makeMap : function (items, delim, map) {
23248 items = items || [];
23249 delim = delim || ',';
23250 if (typeof items == "string") {
23251 items = items.split(delim);
23256 map[items[i]] = {};
23264 Roo.htmleditor.TidyEntities.init();
23266 * @class Roo.htmleditor.KeyEnter
23267 * Handle Enter press..
23268 * @cfg {Roo.HtmlEditorCore} core the editor.
23270 * Create a new Filter.
23271 * @param {Object} config Configuration options
23278 Roo.htmleditor.KeyEnter = function(cfg) {
23279 Roo.apply(this, cfg);
23280 // this does not actually call walk as it's really just a abstract class
23282 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23285 //Roo.htmleditor.KeyEnter.i = 0;
23288 Roo.htmleditor.KeyEnter.prototype = {
23292 keypress : function(e)
23294 if (e.charCode != 13 && e.charCode != 10) {
23295 Roo.log([e.charCode,e]);
23298 e.preventDefault();
23299 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23300 var doc = this.core.doc;
23304 var sel = this.core.getSelection();
23305 var range = sel.getRangeAt(0);
23306 var n = range.commonAncestorContainer;
23307 var pc = range.closest([ 'ol', 'ul']);
23308 var pli = range.closest('li');
23309 if (!pc || e.ctrlKey) {
23310 sel.insertNode('br', 'after');
23312 this.core.undoManager.addEvent();
23313 this.core.fireEditorEvent(e);
23317 // deal with <li> insetion
23318 if (pli.innerText.trim() == '' &&
23319 pli.previousSibling &&
23320 pli.previousSibling.nodeName == 'LI' &&
23321 pli.previousSibling.innerText.trim() == '') {
23322 pli.parentNode.removeChild(pli.previousSibling);
23323 sel.cursorAfter(pc);
23324 this.core.undoManager.addEvent();
23325 this.core.fireEditorEvent(e);
23329 var li = doc.createElement('LI');
23330 li.innerHTML = ' ';
23331 if (!pli || !pli.firstSibling) {
23332 pc.appendChild(li);
23334 pli.parentNode.insertBefore(li, pli.firstSibling);
23336 sel.cursorText (li.firstChild);
23338 this.core.undoManager.addEvent();
23339 this.core.fireEditorEvent(e);
23351 * @class Roo.htmleditor.Block
23352 * Base class for html editor blocks - do not use it directly .. extend it..
23353 * @cfg {DomElement} node The node to apply stuff to.
23354 * @cfg {String} friendly_name the name that appears in the context bar about this block
23355 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23358 * Create a new Filter.
23359 * @param {Object} config Configuration options
23362 Roo.htmleditor.Block = function(cfg)
23364 // do nothing .. should not be called really.
23367 * factory method to get the block from an element (using cache if necessary)
23369 * @param {HtmlElement} the dom element
23371 Roo.htmleditor.Block.factory = function(node)
23373 var cc = Roo.htmleditor.Block.cache;
23374 var id = Roo.get(node).id;
23375 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23376 Roo.htmleditor.Block.cache[id].readElement(node);
23377 return Roo.htmleditor.Block.cache[id];
23379 var db = node.getAttribute('data-block');
23381 db = node.nodeName.toLowerCase().toUpperCaseFirst();
23383 var cls = Roo.htmleditor['Block' + db];
23384 if (typeof(cls) == 'undefined') {
23385 //Roo.log(node.getAttribute('data-block'));
23386 Roo.log("OOps missing block : " + 'Block' + db);
23389 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23390 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
23394 * initalize all Elements from content that are 'blockable'
23396 * @param the body element
23398 Roo.htmleditor.Block.initAll = function(body, type)
23400 if (typeof(type) == 'undefined') {
23401 var ia = Roo.htmleditor.Block.initAll;
23407 Roo.each(Roo.get(body).query(type), function(e) {
23408 Roo.htmleditor.Block.factory(e);
23411 // question goes here... do we need to clear out this cache sometimes?
23412 // or show we make it relivant to the htmleditor.
23413 Roo.htmleditor.Block.cache = {};
23415 Roo.htmleditor.Block.prototype = {
23419 // used by context menu
23420 friendly_name : 'Based Block',
23422 // text for button to delete this element
23423 deleteTitle : false,
23427 * Update a node with values from this object
23428 * @param {DomElement} node
23430 updateElement : function(node)
23432 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23435 * convert to plain HTML for calling insertAtCursor..
23437 toHTML : function()
23439 return Roo.DomHelper.markup(this.toObject());
23442 * used by readEleemnt to extract data from a node
23443 * may need improving as it's pretty basic
23445 * @param {DomElement} node
23446 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23447 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
23448 * @param {String} style the style property - eg. text-align
23450 getVal : function(node, tag, attr, style)
23453 if (tag !== true && n.tagName != tag.toUpperCase()) {
23454 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23455 // but kiss for now.
23456 n = node.getElementsByTagName(tag).item(0);
23461 if (attr === false) {
23464 if (attr == 'html') {
23465 return n.innerHTML;
23467 if (attr == 'style') {
23468 return n.style[style];
23471 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23475 * create a DomHelper friendly object - for use with
23476 * Roo.DomHelper.markup / overwrite / etc..
23479 toObject : function()
23484 * Read a node that has a 'data-block' property - and extract the values from it.
23485 * @param {DomElement} node - the node
23487 readElement : function(node)
23498 * @class Roo.htmleditor.BlockFigure
23499 * Block that has an image and a figcaption
23500 * @cfg {String} image_src the url for the image
23501 * @cfg {String} align (left|right) alignment for the block default left
23502 * @cfg {String} caption the text to appear below (and in the alt tag)
23503 * @cfg {String} caption_display (block|none) display or not the caption
23504 * @cfg {String|number} image_width the width of the image number or %?
23505 * @cfg {String|number} image_height the height of the image number or %?
23508 * Create a new Filter.
23509 * @param {Object} config Configuration options
23512 Roo.htmleditor.BlockFigure = function(cfg)
23515 this.readElement(cfg.node);
23516 this.updateElement(cfg.node);
23518 Roo.apply(this, cfg);
23520 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23527 caption_display : 'block',
23533 // margin: '2%', not used
23535 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
23538 // used by context menu
23539 friendly_name : 'Image with caption',
23540 deleteTitle : "Delete Image and Caption",
23542 contextMenu : function(toolbar)
23545 var block = function() {
23546 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23550 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23552 var syncValue = toolbar.editorcore.syncValue;
23558 xtype : 'TextItem',
23560 xns : rooui.Toolbar //Boostrap?
23564 text: 'Change Image URL',
23567 click: function (btn, state)
23571 Roo.MessageBox.show({
23572 title : "Image Source URL",
23573 msg : "Enter the url for the image",
23574 buttons: Roo.MessageBox.OKCANCEL,
23575 fn: function(btn, val){
23582 toolbar.editorcore.onEditorEvent();
23586 //multiline: multiline,
23588 value : b.image_src
23592 xns : rooui.Toolbar
23597 text: 'Change Link URL',
23600 click: function (btn, state)
23604 Roo.MessageBox.show({
23605 title : "Link URL",
23606 msg : "Enter the url for the link - leave blank to have no link",
23607 buttons: Roo.MessageBox.OKCANCEL,
23608 fn: function(btn, val){
23615 toolbar.editorcore.onEditorEvent();
23619 //multiline: multiline,
23625 xns : rooui.Toolbar
23629 text: 'Show Video URL',
23632 click: function (btn, state)
23634 Roo.MessageBox.alert("Video URL",
23635 block().video_url == '' ? 'This image is not linked ot a video' :
23636 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23639 xns : rooui.Toolbar
23644 xtype : 'TextItem',
23646 xns : rooui.Toolbar //Boostrap?
23649 xtype : 'ComboBox',
23650 allowBlank : false,
23651 displayField : 'val',
23654 triggerAction : 'all',
23656 valueField : 'val',
23660 select : function (combo, r, index)
23662 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23664 b.width = r.get('val');
23667 toolbar.editorcore.onEditorEvent();
23672 xtype : 'SimpleStore',
23683 xtype : 'TextItem',
23685 xns : rooui.Toolbar //Boostrap?
23688 xtype : 'ComboBox',
23689 allowBlank : false,
23690 displayField : 'val',
23693 triggerAction : 'all',
23695 valueField : 'val',
23699 select : function (combo, r, index)
23701 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23703 b.align = r.get('val');
23706 toolbar.editorcore.onEditorEvent();
23711 xtype : 'SimpleStore',
23725 text: 'Hide Caption',
23726 name : 'caption_display',
23728 enableToggle : true,
23729 setValue : function(v) {
23730 // this trigger toggle.
23732 this.setText(v ? "Hide Caption" : "Show Caption");
23733 this.setPressed(v != 'block');
23736 toggle: function (btn, state)
23739 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23740 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23743 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23744 toolbar.editorcore.onEditorEvent();
23747 xns : rooui.Toolbar
23753 * create a DomHelper friendly object - for use with
23754 * Roo.DomHelper.markup / overwrite / etc..
23756 toObject : function()
23758 var d = document.createElement('div');
23759 d.innerHTML = this.caption;
23761 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
23763 var iw = this.align == 'center' ? this.width : '100%';
23766 contenteditable : 'false',
23767 src : this.image_src,
23768 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23771 maxWidth : iw + ' !important', // this is not getting rendered?
23777 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23779 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
23784 if (this.href.length > 0) {
23788 contenteditable : 'true',
23796 if (this.video_url.length > 0) {
23801 allowfullscreen : true,
23802 width : 420, // these are for video tricks - that we replace the outer
23804 src : this.video_url,
23810 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
23811 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
23816 'data-block' : 'Figure',
23817 'data-width' : this.width,
23818 contenteditable : 'false',
23822 float : this.align ,
23823 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
23824 width : this.align == 'center' ? '100%' : this.width,
23826 padding: this.align == 'center' ? '0' : '0 10px' ,
23827 textAlign : this.align // seems to work for email..
23832 align : this.align,
23838 'data-display' : this.caption_display,
23840 textAlign : 'left',
23842 lineHeight : '24px',
23843 display : this.caption_display,
23844 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
23846 width: this.align == 'center' ? this.width : '100%'
23850 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
23855 marginTop : '16px',
23861 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
23863 contenteditable : true,
23879 readElement : function(node)
23881 // this should not really come from the link...
23882 this.video_url = this.getVal(node, 'div', 'src');
23883 this.cls = this.getVal(node, 'div', 'class');
23884 this.href = this.getVal(node, 'a', 'href');
23887 this.image_src = this.getVal(node, 'img', 'src');
23889 this.align = this.getVal(node, 'figure', 'align');
23890 var figcaption = this.getVal(node, 'figcaption', false);
23891 if (figcaption !== '') {
23892 this.caption = this.getVal(figcaption, 'i', 'html');
23896 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
23897 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23898 this.width = this.getVal(node, true, 'data-width');
23899 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23902 removeNode : function()
23919 * @class Roo.htmleditor.BlockTable
23920 * Block that manages a table
23923 * Create a new Filter.
23924 * @param {Object} config Configuration options
23927 Roo.htmleditor.BlockTable = function(cfg)
23930 this.readElement(cfg.node);
23931 this.updateElement(cfg.node);
23933 Roo.apply(this, cfg);
23936 for(var r = 0; r < this.no_row; r++) {
23938 for(var c = 0; c < this.no_col; c++) {
23939 this.rows[r][c] = this.emptyCell();
23946 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23955 // used by context menu
23956 friendly_name : 'Table',
23957 deleteTitle : 'Delete Table',
23958 // context menu is drawn once..
23960 contextMenu : function(toolbar)
23963 var block = function() {
23964 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23968 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23970 var syncValue = toolbar.editorcore.syncValue;
23976 xtype : 'TextItem',
23978 xns : rooui.Toolbar //Boostrap?
23981 xtype : 'ComboBox',
23982 allowBlank : false,
23983 displayField : 'val',
23986 triggerAction : 'all',
23988 valueField : 'val',
23992 select : function (combo, r, index)
23994 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23996 b.width = r.get('val');
23999 toolbar.editorcore.onEditorEvent();
24004 xtype : 'SimpleStore',
24016 xtype : 'TextItem',
24017 text : "Columns: ",
24018 xns : rooui.Toolbar //Boostrap?
24025 click : function (_self, e)
24027 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24028 block().removeColumn();
24030 toolbar.editorcore.onEditorEvent();
24033 xns : rooui.Toolbar
24039 click : function (_self, e)
24041 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24042 block().addColumn();
24044 toolbar.editorcore.onEditorEvent();
24047 xns : rooui.Toolbar
24051 xtype : 'TextItem',
24053 xns : rooui.Toolbar //Boostrap?
24060 click : function (_self, e)
24062 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24063 block().removeRow();
24065 toolbar.editorcore.onEditorEvent();
24068 xns : rooui.Toolbar
24074 click : function (_self, e)
24078 toolbar.editorcore.onEditorEvent();
24081 xns : rooui.Toolbar
24086 text: 'Reset Column Widths',
24089 click : function (_self, e)
24091 block().resetWidths();
24093 toolbar.editorcore.onEditorEvent();
24096 xns : rooui.Toolbar
24107 * create a DomHelper friendly object - for use with
24108 * Roo.DomHelper.markup / overwrite / etc..
24109 * ?? should it be called with option to hide all editing features?
24111 toObject : function()
24116 contenteditable : 'false', // this stops cell selection from picking the table.
24117 'data-block' : 'Table',
24120 border : 'solid 1px #000', // ??? hard coded?
24121 'border-collapse' : 'collapse'
24124 { tag : 'tbody' , cn : [] }
24128 // do we have a head = not really
24130 Roo.each(this.rows, function( row ) {
24135 border : 'solid 1px #000',
24141 ret.cn[0].cn.push(tr);
24142 // does the row have any properties? ?? height?
24144 Roo.each(row, function( cell ) {
24148 contenteditable : 'true',
24149 'data-block' : 'Td',
24153 if (cell.colspan > 1) {
24154 td.colspan = cell.colspan ;
24155 nc += cell.colspan;
24159 if (cell.rowspan > 1) {
24160 td.rowspan = cell.rowspan ;
24169 ncols = Math.max(nc, ncols);
24173 // add the header row..
24182 readElement : function(node)
24184 node = node ? node : this.node ;
24185 this.width = this.getVal(node, true, 'style', 'width') || '100%';
24189 var trs = Array.from(node.rows);
24190 trs.forEach(function(tr) {
24192 this.rows.push(row);
24196 Array.from(tr.cells).forEach(function(td) {
24199 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24200 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24201 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24202 html : td.innerHTML
24204 no_column += add.colspan;
24211 this.no_col = Math.max(this.no_col, no_column);
24218 normalizeRows: function()
24222 this.rows.forEach(function(row) {
24225 row = this.normalizeRow(row);
24227 row.forEach(function(c) {
24228 while (typeof(ret[rid][cid]) != 'undefined') {
24231 if (typeof(ret[rid]) == 'undefined') {
24237 if (c.rowspan < 2) {
24241 for(var i = 1 ;i < c.rowspan; i++) {
24242 if (typeof(ret[rid+i]) == 'undefined') {
24245 ret[rid+i][cid] = c;
24253 normalizeRow: function(row)
24256 row.forEach(function(c) {
24257 if (c.colspan < 2) {
24261 for(var i =0 ;i < c.colspan; i++) {
24269 deleteColumn : function(sel)
24271 if (!sel || sel.type != 'col') {
24274 if (this.no_col < 2) {
24278 this.rows.forEach(function(row) {
24279 var cols = this.normalizeRow(row);
24280 var col = cols[sel.col];
24281 if (col.colspan > 1) {
24291 removeColumn : function()
24293 this.deleteColumn({
24295 col : this.no_col-1
24297 this.updateElement();
24301 addColumn : function()
24304 this.rows.forEach(function(row) {
24305 row.push(this.emptyCell());
24308 this.updateElement();
24311 deleteRow : function(sel)
24313 if (!sel || sel.type != 'row') {
24317 if (this.no_row < 2) {
24321 var rows = this.normalizeRows();
24324 rows[sel.row].forEach(function(col) {
24325 if (col.rowspan > 1) {
24328 col.remove = 1; // flage it as removed.
24333 this.rows.forEach(function(row) {
24335 row.forEach(function(c) {
24336 if (typeof(c.remove) == 'undefined') {
24341 if (newrow.length > 0) {
24345 this.rows = newrows;
24350 this.updateElement();
24353 removeRow : function()
24357 row : this.no_row-1
24363 addRow : function()
24367 for (var i = 0; i < this.no_col; i++ ) {
24369 row.push(this.emptyCell());
24372 this.rows.push(row);
24373 this.updateElement();
24377 // the default cell object... at present...
24378 emptyCell : function() {
24379 return (new Roo.htmleditor.BlockTd({})).toObject();
24384 removeNode : function()
24391 resetWidths : function()
24393 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24394 var nn = Roo.htmleditor.Block.factory(n);
24396 nn.updateElement(n);
24409 * since selections really work on the table cell, then editing really should work from there
24411 * The original plan was to support merging etc... - but that may not be needed yet..
24413 * So this simple version will support:
24415 * adjust the width +/-
24416 * reset the width...
24425 * @class Roo.htmleditor.BlockTable
24426 * Block that manages a table
24429 * Create a new Filter.
24430 * @param {Object} config Configuration options
24433 Roo.htmleditor.BlockTd = function(cfg)
24436 this.readElement(cfg.node);
24437 this.updateElement(cfg.node);
24439 Roo.apply(this, cfg);
24444 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24449 textAlign : 'left',
24456 // used by context menu
24457 friendly_name : 'Table Cell',
24458 deleteTitle : false, // use our customer delete
24460 // context menu is drawn once..
24462 contextMenu : function(toolbar)
24465 var cell = function() {
24466 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24469 var table = function() {
24470 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24474 var saveSel = function()
24476 lr = toolbar.editorcore.getSelection().getRangeAt(0);
24478 var restoreSel = function()
24482 toolbar.editorcore.focus();
24483 var cr = toolbar.editorcore.getSelection();
24484 cr.removeAllRanges();
24486 toolbar.editorcore.onEditorEvent();
24487 }).defer(10, this);
24493 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24495 var syncValue = toolbar.editorcore.syncValue;
24502 text : 'Edit Table',
24504 click : function() {
24505 var t = toolbar.tb.selectedNode.closest('table');
24506 toolbar.editorcore.selectNode(t);
24507 toolbar.editorcore.onEditorEvent();
24516 xtype : 'TextItem',
24517 text : "Column Width: ",
24518 xns : rooui.Toolbar
24525 click : function (_self, e)
24527 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24528 cell().shrinkColumn();
24530 toolbar.editorcore.onEditorEvent();
24533 xns : rooui.Toolbar
24539 click : function (_self, e)
24541 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24542 cell().growColumn();
24544 toolbar.editorcore.onEditorEvent();
24547 xns : rooui.Toolbar
24551 xtype : 'TextItem',
24552 text : "Vertical Align: ",
24553 xns : rooui.Toolbar //Boostrap?
24556 xtype : 'ComboBox',
24557 allowBlank : false,
24558 displayField : 'val',
24561 triggerAction : 'all',
24563 valueField : 'val',
24567 select : function (combo, r, index)
24569 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24571 b.valign = r.get('val');
24574 toolbar.editorcore.onEditorEvent();
24579 xtype : 'SimpleStore',
24583 ['bottom'] // there are afew more...
24591 xtype : 'TextItem',
24592 text : "Merge Cells: ",
24593 xns : rooui.Toolbar
24602 click : function (_self, e)
24604 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24605 cell().mergeRight();
24606 //block().growColumn();
24608 toolbar.editorcore.onEditorEvent();
24611 xns : rooui.Toolbar
24618 click : function (_self, e)
24620 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24621 cell().mergeBelow();
24622 //block().growColumn();
24624 toolbar.editorcore.onEditorEvent();
24627 xns : rooui.Toolbar
24630 xtype : 'TextItem',
24632 xns : rooui.Toolbar
24640 click : function (_self, e)
24642 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24645 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24646 toolbar.editorcore.onEditorEvent();
24650 xns : rooui.Toolbar
24654 xns : rooui.Toolbar
24663 xns : rooui.Toolbar,
24672 click : function (_self, e)
24676 cell().deleteColumn();
24678 toolbar.editorcore.selectNode(t.node);
24679 toolbar.editorcore.onEditorEvent();
24688 click : function (_self, e)
24691 cell().deleteRow();
24694 toolbar.editorcore.selectNode(t.node);
24695 toolbar.editorcore.onEditorEvent();
24702 xtype : 'Separator',
24709 click : function (_self, e)
24712 var nn = t.node.nextSibling || t.node.previousSibling;
24713 t.node.parentNode.removeChild(t.node);
24715 toolbar.editorcore.selectNode(nn, true);
24717 toolbar.editorcore.onEditorEvent();
24727 // align... << fixme
24735 * create a DomHelper friendly object - for use with
24736 * Roo.DomHelper.markup / overwrite / etc..
24737 * ?? should it be called with option to hide all editing features?
24740 * create a DomHelper friendly object - for use with
24741 * Roo.DomHelper.markup / overwrite / etc..
24742 * ?? should it be called with option to hide all editing features?
24744 toObject : function()
24749 contenteditable : 'true', // this stops cell selection from picking the table.
24750 'data-block' : 'Td',
24751 valign : this.valign,
24753 'text-align' : this.textAlign,
24754 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24755 'border-collapse' : 'collapse',
24756 padding : '6px', // 8 for desktop / 4 for mobile
24757 'vertical-align': this.valign
24761 if (this.width != '') {
24762 ret.width = this.width;
24763 ret.style.width = this.width;
24767 if (this.colspan > 1) {
24768 ret.colspan = this.colspan ;
24770 if (this.rowspan > 1) {
24771 ret.rowspan = this.rowspan ;
24780 readElement : function(node)
24782 node = node ? node : this.node ;
24783 this.width = node.style.width;
24784 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24785 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24786 this.html = node.innerHTML;
24791 // the default cell object... at present...
24792 emptyCell : function() {
24796 textAlign : 'left',
24797 html : " " // is this going to be editable now?
24802 removeNode : function()
24804 return this.node.closest('table');
24812 toTableArray : function()
24815 var tab = this.node.closest('tr').closest('table');
24816 Array.from(tab.rows).forEach(function(r, ri){
24820 this.colWidths = [];
24821 var all_auto = true;
24822 Array.from(tab.rows).forEach(function(r, ri){
24825 Array.from(r.cells).forEach(function(ce, ci){
24830 colspan : ce.colSpan,
24831 rowspan : ce.rowSpan
24833 if (ce.isEqualNode(this.node)) {
24836 // if we have been filled up by a row?
24837 if (typeof(ret[rn][cn]) != 'undefined') {
24838 while(typeof(ret[rn][cn]) != 'undefined') {
24844 if (typeof(this.colWidths[cn]) == 'undefined') {
24845 this.colWidths[cn] = ce.style.width;
24846 if (this.colWidths[cn] != '') {
24852 if (c.colspan < 2 && c.rowspan < 2 ) {
24857 for(var j = 0; j < c.rowspan; j++) {
24858 if (typeof(ret[rn+j]) == 'undefined') {
24859 continue; // we have a problem..
24862 for(var i = 0; i < c.colspan; i++) {
24863 ret[rn+j][cn+i] = c;
24872 // initalize widths.?
24873 // either all widths or no widths..
24875 this.colWidths[0] = false; // no widths flag.
24886 mergeRight: function()
24889 // get the contents of the next cell along..
24890 var tr = this.node.closest('tr');
24891 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24892 if (i >= tr.childNodes.length - 1) {
24893 return; // no cells on right to merge with.
24895 var table = this.toTableArray();
24897 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24898 return; // nothing right?
24900 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24901 // right cell - must be same rowspan and on the same row.
24902 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24903 return; // right hand side is not same rowspan.
24908 this.node.innerHTML += ' ' + rc.cell.innerHTML;
24909 tr.removeChild(rc.cell);
24910 this.colspan += rc.colspan;
24911 this.node.setAttribute('colspan', this.colspan);
24916 mergeBelow : function()
24918 var table = this.toTableArray();
24919 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24920 return; // no row below
24922 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24923 return; // nothing right?
24925 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24927 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24928 return; // right hand side is not same rowspan.
24930 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
24931 rc.cell.parentNode.removeChild(rc.cell);
24932 this.rowspan += rc.rowspan;
24933 this.node.setAttribute('rowspan', this.rowspan);
24938 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24941 var table = this.toTableArray();
24942 var cd = this.cellData;
24946 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24950 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24951 if (r == cd.row && c == cd.col) {
24952 this.node.removeAttribute('rowspan');
24953 this.node.removeAttribute('colspan');
24957 var ntd = this.node.cloneNode(); // which col/row should be 0..
24958 ntd.removeAttribute('id'); //
24959 //ntd.style.width = '';
24960 ntd.innerHTML = '';
24961 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
24965 this.redrawAllCells(table);
24973 redrawAllCells: function(table)
24977 var tab = this.node.closest('tr').closest('table');
24978 var ctr = tab.rows[0].parentNode;
24979 Array.from(tab.rows).forEach(function(r, ri){
24981 Array.from(r.cells).forEach(function(ce, ci){
24982 ce.parentNode.removeChild(ce);
24984 r.parentNode.removeChild(r);
24986 for(var r = 0 ; r < table.length; r++) {
24987 var re = tab.rows[r];
24989 var re = tab.ownerDocument.createElement('tr');
24990 ctr.appendChild(re);
24991 for(var c = 0 ; c < table[r].length; c++) {
24992 if (table[r][c].cell === false) {
24996 re.appendChild(table[r][c].cell);
24998 table[r][c].cell = false;
25003 updateWidths : function(table)
25005 for(var r = 0 ; r < table.length; r++) {
25007 for(var c = 0 ; c < table[r].length; c++) {
25008 if (table[r][c].cell === false) {
25012 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
25013 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
25014 el.width = Math.floor(this.colWidths[c]) +'%';
25015 el.updateElement(el.node);
25017 table[r][c].cell = false; // done
25021 normalizeWidths : function(table)
25024 if (this.colWidths[0] === false) {
25025 var nw = 100.0 / this.colWidths.length;
25026 this.colWidths.forEach(function(w,i) {
25027 this.colWidths[i] = nw;
25032 var t = 0, missing = [];
25034 this.colWidths.forEach(function(w,i) {
25036 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
25037 var add = this.colWidths[i];
25046 var nc = this.colWidths.length;
25047 if (missing.length) {
25048 var mult = (nc - missing.length) / (1.0 * nc);
25050 var ew = (100 -t) / (1.0 * missing.length);
25051 this.colWidths.forEach(function(w,i) {
25053 this.colWidths[i] = w * mult;
25057 this.colWidths[i] = ew;
25059 // have to make up numbers..
25062 // now we should have all the widths..
25067 shrinkColumn : function()
25069 var table = this.toTableArray();
25070 this.normalizeWidths(table);
25071 var col = this.cellData.col;
25072 var nw = this.colWidths[col] * 0.8;
25076 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
25077 this.colWidths.forEach(function(w,i) {
25079 this.colWidths[i] = nw;
25082 this.colWidths[i] += otherAdd
25084 this.updateWidths(table);
25087 growColumn : function()
25089 var table = this.toTableArray();
25090 this.normalizeWidths(table);
25091 var col = this.cellData.col;
25092 var nw = this.colWidths[col] * 1.2;
25096 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
25097 this.colWidths.forEach(function(w,i) {
25099 this.colWidths[i] = nw;
25102 this.colWidths[i] -= otherSub
25104 this.updateWidths(table);
25107 deleteRow : function()
25109 // delete this rows 'tr'
25110 // if any of the cells in this row have a rowspan > 1 && row!= this row..
25111 // then reduce the rowspan.
25112 var table = this.toTableArray();
25113 // this.cellData.row;
25114 for (var i =0;i< table[this.cellData.row].length ; i++) {
25115 var c = table[this.cellData.row][i];
25116 if (c.row != this.cellData.row) {
25119 c.cell.setAttribute('rowspan', c.rowspan);
25122 if (c.rowspan > 1) {
25124 c.cell.setAttribute('rowspan', c.rowspan);
25127 table.splice(this.cellData.row,1);
25128 this.redrawAllCells(table);
25131 deleteColumn : function()
25133 var table = this.toTableArray();
25135 for (var i =0;i< table.length ; i++) {
25136 var c = table[i][this.cellData.col];
25137 if (c.col != this.cellData.col) {
25138 table[i][this.cellData.col].colspan--;
25139 } else if (c.colspan > 1) {
25141 c.cell.setAttribute('colspan', c.colspan);
25143 table[i].splice(this.cellData.col,1);
25146 this.redrawAllCells(table);
25154 //<script type="text/javascript">
25157 * Based Ext JS Library 1.1.1
25158 * Copyright(c) 2006-2007, Ext JS, LLC.
25164 * @class Roo.HtmlEditorCore
25165 * @extends Roo.Component
25166 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25168 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25171 Roo.HtmlEditorCore = function(config){
25174 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25179 * @event initialize
25180 * Fires when the editor is fully initialized (including the iframe)
25181 * @param {Roo.HtmlEditorCore} this
25186 * Fires when the editor is first receives the focus. Any insertion must wait
25187 * until after this event.
25188 * @param {Roo.HtmlEditorCore} this
25192 * @event beforesync
25193 * Fires before the textarea is updated with content from the editor iframe. Return false
25194 * to cancel the sync.
25195 * @param {Roo.HtmlEditorCore} this
25196 * @param {String} html
25200 * @event beforepush
25201 * Fires before the iframe editor is updated with content from the textarea. Return false
25202 * to cancel the push.
25203 * @param {Roo.HtmlEditorCore} this
25204 * @param {String} html
25209 * Fires when the textarea is updated with content from the editor iframe.
25210 * @param {Roo.HtmlEditorCore} this
25211 * @param {String} html
25216 * Fires when the iframe editor is updated with content from the textarea.
25217 * @param {Roo.HtmlEditorCore} this
25218 * @param {String} html
25223 * @event editorevent
25224 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25225 * @param {Roo.HtmlEditorCore} this
25232 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25234 // defaults : white / black...
25235 this.applyBlacklists();
25242 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25246 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25252 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25257 * @cfg {Number} height (in pixels)
25261 * @cfg {Number} width (in pixels)
25265 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25266 * if you are doing an email editor, this probably needs disabling, it's designed
25271 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25273 enableBlocks : true,
25275 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25278 stylesheets: false,
25280 * @cfg {String} language default en - language of text (usefull for rtl languages)
25286 * @cfg {boolean} allowComments - default false - allow comments in HTML source
25287 * - by default they are stripped - if you are editing email you may need this.
25289 allowComments: false,
25293 // private properties
25294 validationEvent : false,
25296 initialized : false,
25298 sourceEditMode : false,
25299 onFocus : Roo.emptyFn,
25301 hideMode:'offsets',
25305 // blacklist + whitelisted elements..
25312 undoManager : false,
25314 * Protected method that will not generally be called directly. It
25315 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25316 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25318 getDocMarkup : function(){
25322 // inherit styels from page...??
25323 if (this.stylesheets === false) {
25325 Roo.get(document.head).select('style').each(function(node) {
25326 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25329 Roo.get(document.head).select('link').each(function(node) {
25330 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25333 } else if (!this.stylesheets.length) {
25335 st = '<style type="text/css">' +
25336 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25339 for (var i in this.stylesheets) {
25340 if (typeof(this.stylesheets[i]) != 'string') {
25343 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25348 st += '<style type="text/css">' +
25349 'IMG { cursor: pointer } ' +
25352 st += '<meta name="google" content="notranslate">';
25354 var cls = 'notranslate roo-htmleditor-body';
25356 if(this.bodyCls.length){
25357 cls += ' ' + this.bodyCls;
25360 return '<html class="notranslate" translate="no"><head>' + st +
25361 //<style type="text/css">' +
25362 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25364 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25368 onRender : function(ct, position)
25371 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25372 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25375 this.el.dom.style.border = '0 none';
25376 this.el.dom.setAttribute('tabIndex', -1);
25377 this.el.addClass('x-hidden hide');
25381 if(Roo.isIE){ // fix IE 1px bogus margin
25382 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25386 this.frameId = Roo.id();
25390 var iframe = this.owner.wrap.createChild({
25392 cls: 'form-control', // bootstrap..
25394 name: this.frameId,
25395 frameBorder : 'no',
25396 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25401 this.iframe = iframe.dom;
25403 this.assignDocWin();
25405 this.doc.designMode = 'on';
25408 this.doc.write(this.getDocMarkup());
25412 var task = { // must defer to wait for browser to be ready
25414 //console.log("run task?" + this.doc.readyState);
25415 this.assignDocWin();
25416 if(this.doc.body || this.doc.readyState == 'complete'){
25418 this.doc.designMode="on";
25423 Roo.TaskMgr.stop(task);
25424 this.initEditor.defer(10, this);
25431 Roo.TaskMgr.start(task);
25436 onResize : function(w, h)
25438 Roo.log('resize: ' +w + ',' + h );
25439 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25443 if(typeof w == 'number'){
25445 this.iframe.style.width = w + 'px';
25447 if(typeof h == 'number'){
25449 this.iframe.style.height = h + 'px';
25451 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25458 * Toggles the editor between standard and source edit mode.
25459 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25461 toggleSourceEdit : function(sourceEditMode){
25463 this.sourceEditMode = sourceEditMode === true;
25465 if(this.sourceEditMode){
25467 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
25470 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25471 //this.iframe.className = '';
25474 //this.setSize(this.owner.wrap.getSize());
25475 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25482 * Protected method that will not generally be called directly. If you need/want
25483 * custom HTML cleanup, this is the method you should override.
25484 * @param {String} html The HTML to be cleaned
25485 * return {String} The cleaned HTML
25487 cleanHtml : function(html)
25489 html = String(html);
25490 if(html.length > 5){
25491 if(Roo.isSafari){ // strip safari nonsense
25492 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25495 if(html == ' '){
25502 * HTML Editor -> Textarea
25503 * Protected method that will not generally be called directly. Syncs the contents
25504 * of the editor iframe with the textarea.
25506 syncValue : function()
25508 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25509 if(this.initialized){
25511 if (this.undoManager) {
25512 this.undoManager.addEvent();
25516 var bd = (this.doc.body || this.doc.documentElement);
25519 var sel = this.win.getSelection();
25521 var div = document.createElement('div');
25522 div.innerHTML = bd.innerHTML;
25523 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25524 if (gtx.length > 0) {
25525 var rm = gtx.item(0).parentNode;
25526 rm.parentNode.removeChild(rm);
25530 if (this.enableBlocks) {
25531 new Roo.htmleditor.FilterBlock({ node : div });
25534 var tidy = new Roo.htmleditor.TidySerializer({
25537 var html = tidy.serialize(div);
25541 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25542 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25544 html = '<div style="'+m[0]+'">' + html + '</div>';
25547 html = this.cleanHtml(html);
25548 // fix up the special chars.. normaly like back quotes in word...
25549 // however we do not want to do this with chinese..
25550 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25552 var cc = match.charCodeAt();
25554 // Get the character value, handling surrogate pairs
25555 if (match.length == 2) {
25556 // It's a surrogate pair, calculate the Unicode code point
25557 var high = match.charCodeAt(0) - 0xD800;
25558 var low = match.charCodeAt(1) - 0xDC00;
25559 cc = (high * 0x400) + low + 0x10000;
25561 (cc >= 0x4E00 && cc < 0xA000 ) ||
25562 (cc >= 0x3400 && cc < 0x4E00 ) ||
25563 (cc >= 0xf900 && cc < 0xfb00 )
25568 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25569 return "&#" + cc + ";";
25576 if(this.owner.fireEvent('beforesync', this, html) !== false){
25577 this.el.dom.value = html;
25578 this.owner.fireEvent('sync', this, html);
25584 * TEXTAREA -> EDITABLE
25585 * Protected method that will not generally be called directly. Pushes the value of the textarea
25586 * into the iframe editor.
25588 pushValue : function()
25590 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25591 if(this.initialized){
25592 var v = this.el.dom.value.trim();
25595 if(this.owner.fireEvent('beforepush', this, v) !== false){
25596 var d = (this.doc.body || this.doc.documentElement);
25599 this.el.dom.value = d.innerHTML;
25600 this.owner.fireEvent('push', this, v);
25602 if (this.autoClean) {
25603 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25604 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25606 if (this.enableBlocks) {
25607 Roo.htmleditor.Block.initAll(this.doc.body);
25610 this.updateLanguage();
25612 var lc = this.doc.body.lastChild;
25613 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25614 // add an extra line at the end.
25615 this.doc.body.appendChild(this.doc.createElement('br'));
25623 deferFocus : function(){
25624 this.focus.defer(10, this);
25628 focus : function(){
25629 if(this.win && !this.sourceEditMode){
25636 assignDocWin: function()
25638 var iframe = this.iframe;
25641 this.doc = iframe.contentWindow.document;
25642 this.win = iframe.contentWindow;
25644 // if (!Roo.get(this.frameId)) {
25647 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25648 // this.win = Roo.get(this.frameId).dom.contentWindow;
25650 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25654 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25655 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25660 initEditor : function(){
25661 //console.log("INIT EDITOR");
25662 this.assignDocWin();
25666 this.doc.designMode="on";
25668 this.doc.write(this.getDocMarkup());
25671 var dbody = (this.doc.body || this.doc.documentElement);
25672 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25673 // this copies styles from the containing element into thsi one..
25674 // not sure why we need all of this..
25675 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25677 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25678 //ss['background-attachment'] = 'fixed'; // w3c
25679 dbody.bgProperties = 'fixed'; // ie
25680 dbody.setAttribute("translate", "no");
25682 //Roo.DomHelper.applyStyles(dbody, ss);
25683 Roo.EventManager.on(this.doc, {
25685 'mouseup': this.onEditorEvent,
25686 'dblclick': this.onEditorEvent,
25687 'click': this.onEditorEvent,
25688 'keyup': this.onEditorEvent,
25693 Roo.EventManager.on(this.doc, {
25694 'paste': this.onPasteEvent,
25698 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25701 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25702 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25704 this.initialized = true;
25707 // initialize special key events - enter
25708 new Roo.htmleditor.KeyEnter({core : this});
25712 this.owner.fireEvent('initialize', this);
25715 // this is to prevent a href clicks resulting in a redirect?
25717 onPasteEvent : function(e,v)
25719 // I think we better assume paste is going to be a dirty load of rubish from word..
25721 // even pasting into a 'email version' of this widget will have to clean up that mess.
25722 var cd = (e.browserEvent.clipboardData || window.clipboardData);
25724 // check what type of paste - if it's an image, then handle it differently.
25725 if (cd.files && cd.files.length > 0) {
25727 var urlAPI = (window.createObjectURL && window) ||
25728 (window.URL && URL.revokeObjectURL && URL) ||
25729 (window.webkitURL && webkitURL);
25731 var url = urlAPI.createObjectURL( cd.files[0]);
25732 this.insertAtCursor('<img src=" + url + ">');
25735 if (cd.types.indexOf('text/html') < 0 ) {
25739 var html = cd.getData('text/html'); // clipboard event
25740 if (cd.types.indexOf('text/rtf') > -1) {
25741 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25742 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25747 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
25748 .map(function(g) { return g.toDataURL(); })
25749 .filter(function(g) { return g != 'about:blank'; });
25752 html = this.cleanWordChars(html);
25754 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25757 var sn = this.getParentElement();
25758 // check if d contains a table, and prevent nesting??
25759 //Roo.log(d.getElementsByTagName('table'));
25761 //Roo.log(sn.closest('table'));
25762 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25763 e.preventDefault();
25764 this.insertAtCursor("You can not nest tables");
25765 //Roo.log("prevent?"); // fixme -
25769 if (images.length > 0) {
25770 Roo.each(d.getElementsByTagName('img'), function(img, i) {
25771 img.setAttribute('src', images[i]);
25774 if (this.autoClean) {
25775 new Roo.htmleditor.FilterWord({ node : d });
25777 new Roo.htmleditor.FilterStyleToTag({ node : d });
25778 new Roo.htmleditor.FilterAttributes({
25780 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
25781 attrib_clean : ['href', 'src' ]
25783 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25784 // should be fonts..
25785 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25786 new Roo.htmleditor.FilterParagraph({ node : d });
25787 new Roo.htmleditor.FilterSpan({ node : d });
25788 new Roo.htmleditor.FilterLongBr({ node : d });
25789 new Roo.htmleditor.FilterComment({ node : d });
25793 if (this.enableBlocks) {
25795 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25796 if (img.closest('figure')) { // assume!! that it's aready
25799 var fig = new Roo.htmleditor.BlockFigure({
25800 image_src : img.src
25802 fig.updateElement(img); // replace it..
25808 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
25809 if (this.enableBlocks) {
25810 Roo.htmleditor.Block.initAll(this.doc.body);
25814 e.preventDefault();
25816 // default behaveiour should be our local cleanup paste? (optional?)
25817 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25818 //this.owner.fireEvent('paste', e, v);
25821 onDestroy : function(){
25827 //for (var i =0; i < this.toolbars.length;i++) {
25828 // // fixme - ask toolbars for heights?
25829 // this.toolbars[i].onDestroy();
25832 //this.wrap.dom.innerHTML = '';
25833 //this.wrap.remove();
25838 onFirstFocus : function(){
25840 this.assignDocWin();
25841 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25843 this.activated = true;
25846 if(Roo.isGecko){ // prevent silly gecko errors
25848 var s = this.win.getSelection();
25849 if(!s.focusNode || s.focusNode.nodeType != 3){
25850 var r = s.getRangeAt(0);
25851 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25856 this.execCmd('useCSS', true);
25857 this.execCmd('styleWithCSS', false);
25860 this.owner.fireEvent('activate', this);
25864 adjustFont: function(btn){
25865 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25866 //if(Roo.isSafari){ // safari
25869 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25870 if(Roo.isSafari){ // safari
25871 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25872 v = (v < 10) ? 10 : v;
25873 v = (v > 48) ? 48 : v;
25874 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25879 v = Math.max(1, v+adjust);
25881 this.execCmd('FontSize', v );
25884 onEditorEvent : function(e)
25888 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25889 return; // we do not handle this.. (undo manager does..)
25891 // in theory this detects if the last element is not a br, then we try and do that.
25892 // its so clicking in space at bottom triggers adding a br and moving the cursor.
25894 e.target.nodeName == 'BODY' &&
25895 e.type == "mouseup" &&
25896 this.doc.body.lastChild
25898 var lc = this.doc.body.lastChild;
25899 // gtx-trans is google translate plugin adding crap.
25900 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25901 lc = lc.previousSibling;
25903 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25904 // if last element is <BR> - then dont do anything.
25906 var ns = this.doc.createElement('br');
25907 this.doc.body.appendChild(ns);
25908 range = this.doc.createRange();
25909 range.setStartAfter(ns);
25910 range.collapse(true);
25911 var sel = this.win.getSelection();
25912 sel.removeAllRanges();
25913 sel.addRange(range);
25919 this.fireEditorEvent(e);
25920 // this.updateToolbar();
25921 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25924 fireEditorEvent: function(e)
25926 this.owner.fireEvent('editorevent', this, e);
25929 insertTag : function(tg)
25931 // could be a bit smarter... -> wrap the current selected tRoo..
25932 if (tg.toLowerCase() == 'span' ||
25933 tg.toLowerCase() == 'code' ||
25934 tg.toLowerCase() == 'sup' ||
25935 tg.toLowerCase() == 'sub'
25938 range = this.createRange(this.getSelection());
25939 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25940 wrappingNode.appendChild(range.extractContents());
25941 range.insertNode(wrappingNode);
25948 this.execCmd("formatblock", tg);
25949 this.undoManager.addEvent();
25952 insertText : function(txt)
25956 var range = this.createRange();
25957 range.deleteContents();
25958 //alert(Sender.getAttribute('label'));
25960 range.insertNode(this.doc.createTextNode(txt));
25961 this.undoManager.addEvent();
25967 * Executes a Midas editor command on the editor document and performs necessary focus and
25968 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25969 * @param {String} cmd The Midas command
25970 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25972 relayCmd : function(cmd, value)
25976 case 'justifyleft':
25977 case 'justifyright':
25978 case 'justifycenter':
25979 // if we are in a cell, then we will adjust the
25980 var n = this.getParentElement();
25981 var td = n.closest('td');
25983 var bl = Roo.htmleditor.Block.factory(td);
25984 bl.textAlign = cmd.replace('justify','');
25985 bl.updateElement();
25986 this.owner.fireEvent('editorevent', this);
25989 this.execCmd('styleWithCSS', true); //
25993 // if there is no selection, then we insert, and set the curson inside it..
25994 this.execCmd('styleWithCSS', false);
26004 this.execCmd(cmd, value);
26005 this.owner.fireEvent('editorevent', this);
26006 //this.updateToolbar();
26007 this.owner.deferFocus();
26011 * Executes a Midas editor command directly on the editor document.
26012 * For visual commands, you should use {@link #relayCmd} instead.
26013 * <b>This should only be called after the editor is initialized.</b>
26014 * @param {String} cmd The Midas command
26015 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26017 execCmd : function(cmd, value){
26018 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26025 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26027 * @param {String} text | dom node..
26029 insertAtCursor : function(text)
26032 if(!this.activated){
26036 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26040 // from jquery ui (MIT licenced)
26042 var win = this.win;
26044 if (win.getSelection && win.getSelection().getRangeAt) {
26046 // delete the existing?
26048 this.createRange(this.getSelection()).deleteContents();
26049 range = win.getSelection().getRangeAt(0);
26050 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26051 range.insertNode(node);
26052 range = range.cloneRange();
26053 range.collapse(false);
26055 win.getSelection().removeAllRanges();
26056 win.getSelection().addRange(range);
26060 } else if (win.document.selection && win.document.selection.createRange) {
26061 // no firefox support
26062 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26063 win.document.selection.createRange().pasteHTML(txt);
26066 // no firefox support
26067 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26068 this.execCmd('InsertHTML', txt);
26076 mozKeyPress : function(e){
26078 var c = e.getCharCode(), cmd;
26081 c = String.fromCharCode(c).toLowerCase();
26095 // this.cleanUpPaste.defer(100, this);
26101 this.relayCmd(cmd);
26102 //this.win.focus();
26103 //this.execCmd(cmd);
26104 //this.deferFocus();
26105 e.preventDefault();
26113 fixKeys : function(){ // load time branching for fastest keydown performance
26117 return function(e){
26118 var k = e.getKey(), r;
26121 r = this.doc.selection.createRange();
26124 r.pasteHTML('    ');
26129 /// this is handled by Roo.htmleditor.KeyEnter
26132 r = this.doc.selection.createRange();
26134 var target = r.parentElement();
26135 if(!target || target.tagName.toLowerCase() != 'li'){
26137 r.pasteHTML('<br/>');
26144 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26145 // this.cleanUpPaste.defer(100, this);
26151 }else if(Roo.isOpera){
26152 return function(e){
26153 var k = e.getKey();
26157 this.execCmd('InsertHTML','    ');
26161 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26162 // this.cleanUpPaste.defer(100, this);
26167 }else if(Roo.isSafari){
26168 return function(e){
26169 var k = e.getKey();
26173 this.execCmd('InsertText','\t');
26177 this.mozKeyPress(e);
26179 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26180 // this.cleanUpPaste.defer(100, this);
26188 getAllAncestors: function()
26190 var p = this.getSelectedNode();
26193 a.push(p); // push blank onto stack..
26194 p = this.getParentElement();
26198 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26202 a.push(this.doc.body);
26206 lastSelNode : false,
26209 getSelection : function()
26211 this.assignDocWin();
26212 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26215 * Select a dom node
26216 * @param {DomElement} node the node to select
26218 selectNode : function(node, collapse)
26220 var nodeRange = node.ownerDocument.createRange();
26222 nodeRange.selectNode(node);
26224 nodeRange.selectNodeContents(node);
26226 if (collapse === true) {
26227 nodeRange.collapse(true);
26230 var s = this.win.getSelection();
26231 s.removeAllRanges();
26232 s.addRange(nodeRange);
26235 getSelectedNode: function()
26237 // this may only work on Gecko!!!
26239 // should we cache this!!!!
26243 var range = this.createRange(this.getSelection()).cloneRange();
26246 var parent = range.parentElement();
26248 var testRange = range.duplicate();
26249 testRange.moveToElementText(parent);
26250 if (testRange.inRange(range)) {
26253 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26256 parent = parent.parentElement;
26261 // is ancestor a text element.
26262 var ac = range.commonAncestorContainer;
26263 if (ac.nodeType == 3) {
26264 ac = ac.parentNode;
26267 var ar = ac.childNodes;
26270 var other_nodes = [];
26271 var has_other_nodes = false;
26272 for (var i=0;i<ar.length;i++) {
26273 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26276 // fullly contained node.
26278 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26283 // probably selected..
26284 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26285 other_nodes.push(ar[i]);
26289 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26294 has_other_nodes = true;
26296 if (!nodes.length && other_nodes.length) {
26297 nodes= other_nodes;
26299 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26307 createRange: function(sel)
26309 // this has strange effects when using with
26310 // top toolbar - not sure if it's a great idea.
26311 //this.editor.contentWindow.focus();
26312 if (typeof sel != "undefined") {
26314 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26316 return this.doc.createRange();
26319 return this.doc.createRange();
26322 getParentElement: function()
26325 this.assignDocWin();
26326 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26328 var range = this.createRange(sel);
26331 var p = range.commonAncestorContainer;
26332 while (p.nodeType == 3) { // text node
26343 * Range intersection.. the hard stuff...
26347 * [ -- selected range --- ]
26351 * if end is before start or hits it. fail.
26352 * if start is after end or hits it fail.
26354 * if either hits (but other is outside. - then it's not
26360 // @see http://www.thismuchiknow.co.uk/?p=64.
26361 rangeIntersectsNode : function(range, node)
26363 var nodeRange = node.ownerDocument.createRange();
26365 nodeRange.selectNode(node);
26367 nodeRange.selectNodeContents(node);
26370 var rangeStartRange = range.cloneRange();
26371 rangeStartRange.collapse(true);
26373 var rangeEndRange = range.cloneRange();
26374 rangeEndRange.collapse(false);
26376 var nodeStartRange = nodeRange.cloneRange();
26377 nodeStartRange.collapse(true);
26379 var nodeEndRange = nodeRange.cloneRange();
26380 nodeEndRange.collapse(false);
26382 return rangeStartRange.compareBoundaryPoints(
26383 Range.START_TO_START, nodeEndRange) == -1 &&
26384 rangeEndRange.compareBoundaryPoints(
26385 Range.START_TO_START, nodeStartRange) == 1;
26389 rangeCompareNode : function(range, node)
26391 var nodeRange = node.ownerDocument.createRange();
26393 nodeRange.selectNode(node);
26395 nodeRange.selectNodeContents(node);
26399 range.collapse(true);
26401 nodeRange.collapse(true);
26403 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26404 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26406 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26408 var nodeIsBefore = ss == 1;
26409 var nodeIsAfter = ee == -1;
26411 if (nodeIsBefore && nodeIsAfter) {
26414 if (!nodeIsBefore && nodeIsAfter) {
26415 return 1; //right trailed.
26418 if (nodeIsBefore && !nodeIsAfter) {
26419 return 2; // left trailed.
26425 cleanWordChars : function(input) {// change the chars to hex code
26428 [ 8211, "–" ],
26429 [ 8212, "—" ],
26437 var output = input;
26438 Roo.each(swapCodes, function(sw) {
26439 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26441 output = output.replace(swapper, sw[1]);
26451 cleanUpChild : function (node)
26454 new Roo.htmleditor.FilterComment({node : node});
26455 new Roo.htmleditor.FilterAttributes({
26457 attrib_black : this.ablack,
26458 attrib_clean : this.aclean,
26459 style_white : this.cwhite,
26460 style_black : this.cblack
26462 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26463 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26469 * Clean up MS wordisms...
26470 * @deprecated - use filter directly
26472 cleanWord : function(node)
26474 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26481 * @deprecated - use filters
26483 cleanTableWidths : function(node)
26485 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26492 applyBlacklists : function()
26494 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26495 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26497 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
26498 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
26499 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
26503 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26504 if (b.indexOf(tag) > -1) {
26507 this.white.push(tag);
26511 Roo.each(w, function(tag) {
26512 if (b.indexOf(tag) > -1) {
26515 if (this.white.indexOf(tag) > -1) {
26518 this.white.push(tag);
26523 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26524 if (w.indexOf(tag) > -1) {
26527 this.black.push(tag);
26531 Roo.each(b, function(tag) {
26532 if (w.indexOf(tag) > -1) {
26535 if (this.black.indexOf(tag) > -1) {
26538 this.black.push(tag);
26543 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26544 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26548 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26549 if (b.indexOf(tag) > -1) {
26552 this.cwhite.push(tag);
26556 Roo.each(w, function(tag) {
26557 if (b.indexOf(tag) > -1) {
26560 if (this.cwhite.indexOf(tag) > -1) {
26563 this.cwhite.push(tag);
26568 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26569 if (w.indexOf(tag) > -1) {
26572 this.cblack.push(tag);
26576 Roo.each(b, function(tag) {
26577 if (w.indexOf(tag) > -1) {
26580 if (this.cblack.indexOf(tag) > -1) {
26583 this.cblack.push(tag);
26588 setStylesheets : function(stylesheets)
26590 if(typeof(stylesheets) == 'string'){
26591 Roo.get(this.iframe.contentDocument.head).createChild({
26593 rel : 'stylesheet',
26602 Roo.each(stylesheets, function(s) {
26607 Roo.get(_this.iframe.contentDocument.head).createChild({
26609 rel : 'stylesheet',
26619 updateLanguage : function()
26621 if (!this.iframe || !this.iframe.contentDocument) {
26624 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26628 removeStylesheets : function()
26632 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26637 setStyle : function(style)
26639 Roo.get(this.iframe.contentDocument.head).createChild({
26648 // hide stuff that is not compatible
26662 * @event specialkey
26666 * @cfg {String} fieldClass @hide
26669 * @cfg {String} focusClass @hide
26672 * @cfg {String} autoCreate @hide
26675 * @cfg {String} inputType @hide
26678 * @cfg {String} invalidClass @hide
26681 * @cfg {String} invalidText @hide
26684 * @cfg {String} msgFx @hide
26687 * @cfg {String} validateOnBlur @hide
26691 Roo.HtmlEditorCore.white = [
26692 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26694 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
26695 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
26696 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
26697 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
26698 'TABLE', 'UL', 'XMP',
26700 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
26703 'DIR', 'MENU', 'OL', 'UL', 'DL',
26709 Roo.HtmlEditorCore.black = [
26710 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26712 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
26713 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
26714 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
26715 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
26716 //'FONT' // CLEAN LATER..
26717 'COLGROUP', 'COL' // messy tables.
26721 Roo.HtmlEditorCore.clean = [ // ?? needed???
26722 'SCRIPT', 'STYLE', 'TITLE', 'XML'
26724 Roo.HtmlEditorCore.tag_remove = [
26729 Roo.HtmlEditorCore.ablack = [
26733 Roo.HtmlEditorCore.aclean = [
26734 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26738 Roo.HtmlEditorCore.pwhite= [
26739 'http', 'https', 'mailto'
26742 // white listed style attributes.
26743 Roo.HtmlEditorCore.cwhite= [
26744 // 'text-align', /// default is to allow most things..
26750 // black listed style attributes.
26751 Roo.HtmlEditorCore.cblack= [
26752 // 'font-size' -- this can be set by the project
26758 //<script type="text/javascript">
26761 * Ext JS Library 1.1.1
26762 * Copyright(c) 2006-2007, Ext JS, LLC.
26768 Roo.form.HtmlEditor = function(config){
26772 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26774 if (!this.toolbars) {
26775 this.toolbars = [];
26777 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26783 * @class Roo.form.HtmlEditor
26784 * @extends Roo.form.Field
26785 * Provides a lightweight HTML Editor component.
26787 * This has been tested on Fireforx / Chrome.. IE may not be so great..
26789 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26790 * supported by this editor.</b><br/><br/>
26791 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26792 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26794 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26796 * @cfg {Boolean} clearUp
26800 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26805 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26810 * @cfg {Number} height (in pixels)
26814 * @cfg {Number} width (in pixels)
26819 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
26822 stylesheets: false,
26826 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26831 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26837 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26842 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26847 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26849 allowComments: false,
26851 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26853 enableBlocks : true,
26856 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26857 * if you are doing an email editor, this probably needs disabling, it's designed
26861 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
26865 * @cfg {String} language default en - language of text (usefull for rtl languages)
26874 // private properties
26875 validationEvent : false,
26877 initialized : false,
26880 onFocus : Roo.emptyFn,
26882 hideMode:'offsets',
26884 actionMode : 'container', // defaults to hiding it...
26886 defaultAutoCreate : { // modified by initCompnoent..
26888 style:"width:500px;height:300px;",
26889 autocomplete: "new-password"
26893 initComponent : function(){
26896 * @event initialize
26897 * Fires when the editor is fully initialized (including the iframe)
26898 * @param {HtmlEditor} this
26903 * Fires when the editor is first receives the focus. Any insertion must wait
26904 * until after this event.
26905 * @param {HtmlEditor} this
26909 * @event beforesync
26910 * Fires before the textarea is updated with content from the editor iframe. Return false
26911 * to cancel the sync.
26912 * @param {HtmlEditor} this
26913 * @param {String} html
26917 * @event beforepush
26918 * Fires before the iframe editor is updated with content from the textarea. Return false
26919 * to cancel the push.
26920 * @param {HtmlEditor} this
26921 * @param {String} html
26926 * Fires when the textarea is updated with content from the editor iframe.
26927 * @param {HtmlEditor} this
26928 * @param {String} html
26933 * Fires when the iframe editor is updated with content from the textarea.
26934 * @param {HtmlEditor} this
26935 * @param {String} html
26939 * @event editmodechange
26940 * Fires when the editor switches edit modes
26941 * @param {HtmlEditor} this
26942 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26944 editmodechange: true,
26946 * @event editorevent
26947 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26948 * @param {HtmlEditor} this
26952 * @event firstfocus
26953 * Fires when on first focus - needed by toolbars..
26954 * @param {HtmlEditor} this
26959 * Auto save the htmlEditor value as a file into Events
26960 * @param {HtmlEditor} this
26964 * @event savedpreview
26965 * preview the saved version of htmlEditor
26966 * @param {HtmlEditor} this
26968 savedpreview: true,
26971 * @event stylesheetsclick
26972 * Fires when press the Sytlesheets button
26973 * @param {Roo.HtmlEditorCore} this
26975 stylesheetsclick: true,
26978 * Fires when press user pastes into the editor
26979 * @param {Roo.HtmlEditorCore} this
26983 this.defaultAutoCreate = {
26985 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26986 autocomplete: "new-password"
26991 * Protected method that will not generally be called directly. It
26992 * is called when the editor creates its toolbar. Override this method if you need to
26993 * add custom toolbar buttons.
26994 * @param {HtmlEditor} editor
26996 createToolbar : function(editor){
26997 Roo.log("create toolbars");
26998 if (!editor.toolbars || !editor.toolbars.length) {
26999 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
27002 for (var i =0 ; i < editor.toolbars.length;i++) {
27003 editor.toolbars[i] = Roo.factory(
27004 typeof(editor.toolbars[i]) == 'string' ?
27005 { xtype: editor.toolbars[i]} : editor.toolbars[i],
27006 Roo.form.HtmlEditor);
27007 editor.toolbars[i].init(editor);
27013 * get the Context selected node
27014 * @returns {DomElement|boolean} selected node if active or false if none
27017 getSelectedNode : function()
27019 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
27022 return this.toolbars[1].tb.selectedNode;
27026 onRender : function(ct, position)
27029 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27031 this.wrap = this.el.wrap({
27032 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27035 this.editorcore.onRender(ct, position);
27037 if (this.resizable) {
27038 this.resizeEl = new Roo.Resizable(this.wrap, {
27042 minHeight : this.height,
27043 height: this.height,
27044 handles : this.resizable,
27047 resize : function(r, w, h) {
27048 _t.onResize(w,h); // -something
27054 this.createToolbar(this);
27058 this.setSize(this.wrap.getSize());
27060 if (this.resizeEl) {
27061 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27062 // should trigger onReize..
27065 this.keyNav = new Roo.KeyNav(this.el, {
27067 "tab" : function(e){
27068 e.preventDefault();
27070 var value = this.getValue();
27072 var start = this.el.dom.selectionStart;
27073 var end = this.el.dom.selectionEnd;
27077 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
27078 this.el.dom.setSelectionRange(end + 1, end + 1);
27082 var f = value.substring(0, start).split("\t");
27084 if(f.pop().length != 0){
27088 this.setValue(f.join("\t") + value.substring(end));
27089 this.el.dom.setSelectionRange(start - 1, start - 1);
27093 "home" : function(e){
27094 e.preventDefault();
27096 var curr = this.el.dom.selectionStart;
27097 var lines = this.getValue().split("\n");
27104 this.el.dom.setSelectionRange(0, 0);
27110 for (var i = 0; i < lines.length;i++) {
27111 pos += lines[i].length;
27121 pos -= lines[i].length;
27127 this.el.dom.setSelectionRange(pos, pos);
27131 this.el.dom.selectionStart = pos;
27132 this.el.dom.selectionEnd = curr;
27135 "end" : function(e){
27136 e.preventDefault();
27138 var curr = this.el.dom.selectionStart;
27139 var lines = this.getValue().split("\n");
27146 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
27152 for (var i = 0; i < lines.length;i++) {
27154 pos += lines[i].length;
27168 this.el.dom.setSelectionRange(pos, pos);
27172 this.el.dom.selectionStart = curr;
27173 this.el.dom.selectionEnd = pos;
27178 doRelay : function(foo, bar, hname){
27179 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
27185 // if(this.autosave && this.w){
27186 // this.autoSaveFn = setInterval(this.autosave, 1000);
27191 onResize : function(w, h)
27193 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27198 if(typeof w == 'number'){
27199 var aw = w - this.wrap.getFrameWidth('lr');
27200 this.el.setWidth(this.adjustWidth('textarea', aw));
27203 if(typeof h == 'number'){
27205 for (var i =0; i < this.toolbars.length;i++) {
27206 // fixme - ask toolbars for heights?
27207 tbh += this.toolbars[i].tb.el.getHeight();
27208 if (this.toolbars[i].footer) {
27209 tbh += this.toolbars[i].footer.el.getHeight();
27216 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27217 ah -= 5; // knock a few pixes off for look..
27219 this.el.setHeight(this.adjustWidth('textarea', ah));
27223 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27224 this.editorcore.onResize(ew,eh);
27229 * Toggles the editor between standard and source edit mode.
27230 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27232 toggleSourceEdit : function(sourceEditMode)
27234 this.editorcore.toggleSourceEdit(sourceEditMode);
27236 if(this.editorcore.sourceEditMode){
27237 Roo.log('editor - showing textarea');
27240 // Roo.log(this.syncValue());
27241 this.editorcore.syncValue();
27242 this.el.removeClass('x-hidden');
27243 this.el.dom.removeAttribute('tabIndex');
27245 this.el.dom.scrollTop = 0;
27248 for (var i = 0; i < this.toolbars.length; i++) {
27249 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27250 this.toolbars[i].tb.hide();
27251 this.toolbars[i].footer.hide();
27256 Roo.log('editor - hiding textarea');
27258 // Roo.log(this.pushValue());
27259 this.editorcore.pushValue();
27261 this.el.addClass('x-hidden');
27262 this.el.dom.setAttribute('tabIndex', -1);
27264 for (var i = 0; i < this.toolbars.length; i++) {
27265 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27266 this.toolbars[i].tb.show();
27267 this.toolbars[i].footer.show();
27271 //this.deferFocus();
27274 this.setSize(this.wrap.getSize());
27275 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27277 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27280 // private (for BoxComponent)
27281 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27283 // private (for BoxComponent)
27284 getResizeEl : function(){
27288 // private (for BoxComponent)
27289 getPositionEl : function(){
27294 initEvents : function(){
27295 this.originalValue = this.getValue();
27299 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27302 markInvalid : Roo.emptyFn,
27304 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27307 clearInvalid : Roo.emptyFn,
27309 setValue : function(v){
27310 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27311 this.editorcore.pushValue();
27315 * update the language in the body - really done by core
27316 * @param {String} language - eg. en / ar / zh-CN etc..
27318 updateLanguage : function(lang)
27320 this.language = lang;
27321 this.editorcore.language = lang;
27322 this.editorcore.updateLanguage();
27326 deferFocus : function(){
27327 this.focus.defer(10, this);
27331 focus : function(){
27332 this.editorcore.focus();
27338 onDestroy : function(){
27344 for (var i =0; i < this.toolbars.length;i++) {
27345 // fixme - ask toolbars for heights?
27346 this.toolbars[i].onDestroy();
27349 this.wrap.dom.innerHTML = '';
27350 this.wrap.remove();
27355 onFirstFocus : function(){
27356 //Roo.log("onFirstFocus");
27357 this.editorcore.onFirstFocus();
27358 for (var i =0; i < this.toolbars.length;i++) {
27359 this.toolbars[i].onFirstFocus();
27365 syncValue : function()
27367 this.editorcore.syncValue();
27370 pushValue : function()
27372 this.editorcore.pushValue();
27375 setStylesheets : function(stylesheets)
27377 this.editorcore.setStylesheets(stylesheets);
27380 removeStylesheets : function()
27382 this.editorcore.removeStylesheets();
27386 // hide stuff that is not compatible
27400 * @event specialkey
27404 * @cfg {String} fieldClass @hide
27407 * @cfg {String} focusClass @hide
27410 * @cfg {String} autoCreate @hide
27413 * @cfg {String} inputType @hide
27416 * @cfg {String} invalidClass @hide
27419 * @cfg {String} invalidText @hide
27422 * @cfg {String} msgFx @hide
27425 * @cfg {String} validateOnBlur @hide
27431 * Ext JS Library 1.1.1
27432 * Copyright(c) 2006-2007, Ext JS, LLC.
27438 * @class Roo.form.HtmlEditor.ToolbarStandard
27443 new Roo.form.HtmlEditor({
27446 new Roo.form.HtmlEditorToolbar1({
27447 disable : { fonts: 1 , format: 1, ..., ... , ...],
27453 * @cfg {Object} disable List of elements to disable..
27454 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27458 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27461 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27464 Roo.apply(this, config);
27466 // default disabled, based on 'good practice'..
27467 this.disable = this.disable || {};
27468 Roo.applyIf(this.disable, {
27471 specialElements : true
27475 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27476 // dont call parent... till later.
27479 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27486 editorcore : false,
27488 * @cfg {Object} disable List of toolbar elements to disable
27495 * @cfg {String} createLinkText The default text for the create link prompt
27497 createLinkText : 'Please enter the URL for the link:',
27499 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27501 defaultLinkValue : 'http:/'+'/',
27505 * @cfg {Array} fontFamilies An array of available font families
27523 // "á" , ?? a acute?
27528 "°" // , // degrees
27530 // "é" , // e ecute
27531 // "ú" , // u ecute?
27534 specialElements : [
27536 text: "Insert Table",
27539 ihtml : '<table><tr><td>Cell</td></tr></table>'
27543 text: "Insert Image",
27546 ihtml : '<img src="about:blank"/>'
27555 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
27556 "input:submit", "input:button", "select", "textarea", "label" ],
27559 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
27561 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27570 * @cfg {String} defaultFont default font to use.
27572 defaultFont: 'tahoma',
27574 fontSelect : false,
27577 formatCombo : false,
27579 init : function(editor)
27581 this.editor = editor;
27582 this.editorcore = editor.editorcore ? editor.editorcore : editor;
27583 var editorcore = this.editorcore;
27587 var fid = editorcore.frameId;
27589 function btn(id, toggle, handler){
27590 var xid = fid + '-'+ id ;
27594 cls : 'x-btn-icon x-edit-'+id,
27595 enableToggle:toggle !== false,
27596 scope: _t, // was editor...
27597 handler:handler||_t.relayBtnCmd,
27598 clickEvent:'mousedown',
27599 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27606 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27608 // stop form submits
27609 tb.el.on('click', function(e){
27610 e.preventDefault(); // what does this do?
27613 if(!this.disable.font) { // && !Roo.isSafari){
27614 /* why no safari for fonts
27615 editor.fontSelect = tb.el.createChild({
27618 cls:'x-font-select',
27619 html: this.createFontOptions()
27622 editor.fontSelect.on('change', function(){
27623 var font = editor.fontSelect.dom.value;
27624 editor.relayCmd('fontname', font);
27625 editor.deferFocus();
27629 editor.fontSelect.dom,
27635 if(!this.disable.formats){
27636 this.formatCombo = new Roo.form.ComboBox({
27637 store: new Roo.data.SimpleStore({
27640 data : this.formats // from states.js
27644 //autoCreate : {tag: "div", size: "20"},
27645 displayField:'tag',
27649 triggerAction: 'all',
27650 emptyText:'Add tag',
27651 selectOnFocus:true,
27654 'select': function(c, r, i) {
27655 editorcore.insertTag(r.get('tag'));
27661 tb.addField(this.formatCombo);
27665 if(!this.disable.format){
27670 btn('strikethrough')
27673 if(!this.disable.fontSize){
27678 btn('increasefontsize', false, editorcore.adjustFont),
27679 btn('decreasefontsize', false, editorcore.adjustFont)
27684 if(!this.disable.colors){
27687 id:editorcore.frameId +'-forecolor',
27688 cls:'x-btn-icon x-edit-forecolor',
27689 clickEvent:'mousedown',
27690 tooltip: this.buttonTips['forecolor'] || undefined,
27692 menu : new Roo.menu.ColorMenu({
27693 allowReselect: true,
27694 focus: Roo.emptyFn,
27697 selectHandler: function(cp, color){
27698 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27699 editor.deferFocus();
27702 clickEvent:'mousedown'
27705 id:editorcore.frameId +'backcolor',
27706 cls:'x-btn-icon x-edit-backcolor',
27707 clickEvent:'mousedown',
27708 tooltip: this.buttonTips['backcolor'] || undefined,
27710 menu : new Roo.menu.ColorMenu({
27711 focus: Roo.emptyFn,
27714 allowReselect: true,
27715 selectHandler: function(cp, color){
27717 editorcore.execCmd('useCSS', false);
27718 editorcore.execCmd('hilitecolor', color);
27719 editorcore.execCmd('useCSS', true);
27720 editor.deferFocus();
27722 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
27723 Roo.isSafari || Roo.isIE ? '#'+color : color);
27724 editor.deferFocus();
27728 clickEvent:'mousedown'
27733 // now add all the items...
27736 if(!this.disable.alignments){
27739 btn('justifyleft'),
27740 btn('justifycenter'),
27741 btn('justifyright')
27745 //if(!Roo.isSafari){
27746 if(!this.disable.links){
27749 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
27753 if(!this.disable.lists){
27756 btn('insertorderedlist'),
27757 btn('insertunorderedlist')
27760 if(!this.disable.sourceEdit){
27763 btn('sourceedit', true, function(btn){
27764 this.toggleSourceEdit(btn.pressed);
27771 // special menu.. - needs to be tidied up..
27772 if (!this.disable.special) {
27775 cls: 'x-edit-none',
27781 for (var i =0; i < this.specialChars.length; i++) {
27782 smenu.menu.items.push({
27784 html: this.specialChars[i],
27785 handler: function(a,b) {
27786 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27787 //editor.insertAtCursor(a.html);
27801 if (!this.disable.cleanStyles) {
27803 cls: 'x-btn-icon x-btn-clear',
27809 for (var i =0; i < this.cleanStyles.length; i++) {
27810 cmenu.menu.items.push({
27811 actiontype : this.cleanStyles[i],
27812 html: 'Remove ' + this.cleanStyles[i],
27813 handler: function(a,b) {
27816 var c = Roo.get(editorcore.doc.body);
27817 c.select('[style]').each(function(s) {
27818 s.dom.style.removeProperty(a.actiontype);
27820 editorcore.syncValue();
27825 cmenu.menu.items.push({
27826 actiontype : 'tablewidths',
27827 html: 'Remove Table Widths',
27828 handler: function(a,b) {
27829 editorcore.cleanTableWidths();
27830 editorcore.syncValue();
27834 cmenu.menu.items.push({
27835 actiontype : 'word',
27836 html: 'Remove MS Word Formating',
27837 handler: function(a,b) {
27838 editorcore.cleanWord();
27839 editorcore.syncValue();
27844 cmenu.menu.items.push({
27845 actiontype : 'all',
27846 html: 'Remove All Styles',
27847 handler: function(a,b) {
27849 var c = Roo.get(editorcore.doc.body);
27850 c.select('[style]').each(function(s) {
27851 s.dom.removeAttribute('style');
27853 editorcore.syncValue();
27858 cmenu.menu.items.push({
27859 actiontype : 'all',
27860 html: 'Remove All CSS Classes',
27861 handler: function(a,b) {
27863 var c = Roo.get(editorcore.doc.body);
27864 c.select('[class]').each(function(s) {
27865 s.dom.removeAttribute('class');
27867 editorcore.cleanWord();
27868 editorcore.syncValue();
27873 cmenu.menu.items.push({
27874 actiontype : 'tidy',
27875 html: 'Tidy HTML Source',
27876 handler: function(a,b) {
27877 new Roo.htmleditor.Tidy(editorcore.doc.body);
27878 editorcore.syncValue();
27887 if (!this.disable.specialElements) {
27890 cls: 'x-edit-none',
27895 for (var i =0; i < this.specialElements.length; i++) {
27896 semenu.menu.items.push(
27898 handler: function(a,b) {
27899 editor.insertAtCursor(this.ihtml);
27901 }, this.specialElements[i])
27913 for(var i =0; i< this.btns.length;i++) {
27914 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27915 b.cls = 'x-edit-none';
27917 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27918 b.cls += ' x-init-enable';
27921 b.scope = editorcore;
27929 // disable everything...
27931 this.tb.items.each(function(item){
27934 item.id != editorcore.frameId+ '-sourceedit' &&
27935 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27941 this.rendered = true;
27943 // the all the btns;
27944 editor.on('editorevent', this.updateToolbar, this);
27945 // other toolbars need to implement this..
27946 //editor.on('editmodechange', this.updateToolbar, this);
27950 relayBtnCmd : function(btn) {
27951 this.editorcore.relayCmd(btn.cmd);
27953 // private used internally
27954 createLink : function(){
27955 //Roo.log("create link?");
27956 var ec = this.editorcore;
27957 var ar = ec.getAllAncestors();
27959 for(var i = 0;i< ar.length;i++) {
27960 if (ar[i] && ar[i].nodeName == 'A') {
27968 Roo.MessageBox.show({
27969 title : "Add / Edit Link URL",
27970 msg : "Enter the url for the link",
27971 buttons: Roo.MessageBox.OKCANCEL,
27972 fn: function(btn, url){
27976 if(url && url != 'http:/'+'/'){
27978 n.setAttribute('href', url);
27980 ec.relayCmd('createlink', url);
27986 //multiline: multiline,
27988 value : n ? n.getAttribute('href') : ''
27992 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27998 * Protected method that will not generally be called directly. It triggers
27999 * a toolbar update by reading the markup state of the current selection in the editor.
28001 updateToolbar: function(){
28003 if(!this.editorcore.activated){
28004 this.editor.onFirstFocus();
28008 var btns = this.tb.items.map,
28009 doc = this.editorcore.doc,
28010 frameId = this.editorcore.frameId;
28012 if(!this.disable.font && !Roo.isSafari){
28014 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
28015 if(name != this.fontSelect.dom.value){
28016 this.fontSelect.dom.value = name;
28020 if(!this.disable.format){
28021 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
28022 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
28023 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
28024 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
28026 if(!this.disable.alignments){
28027 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
28028 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
28029 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
28031 if(!Roo.isSafari && !this.disable.lists){
28032 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
28033 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
28036 var ans = this.editorcore.getAllAncestors();
28037 if (this.formatCombo) {
28040 var store = this.formatCombo.store;
28041 this.formatCombo.setValue("");
28042 for (var i =0; i < ans.length;i++) {
28043 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28045 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28053 // hides menus... - so this cant be on a menu...
28054 Roo.menu.MenuMgr.hideAll();
28056 //this.editorsyncValue();
28060 createFontOptions : function(){
28061 var buf = [], fs = this.fontFamilies, ff, lc;
28065 for(var i = 0, len = fs.length; i< len; i++){
28067 lc = ff.toLowerCase();
28069 '<option value="',lc,'" style="font-family:',ff,';"',
28070 (this.defaultFont == lc ? ' selected="true">' : '>'),
28075 return buf.join('');
28078 toggleSourceEdit : function(sourceEditMode){
28080 Roo.log("toolbar toogle");
28081 if(sourceEditMode === undefined){
28082 sourceEditMode = !this.sourceEditMode;
28084 this.sourceEditMode = sourceEditMode === true;
28085 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
28086 // just toggle the button?
28087 if(btn.pressed !== this.sourceEditMode){
28088 btn.toggle(this.sourceEditMode);
28092 if(sourceEditMode){
28093 Roo.log("disabling buttons");
28094 this.tb.items.each(function(item){
28095 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
28101 Roo.log("enabling buttons");
28102 if(this.editorcore.initialized){
28103 this.tb.items.each(function(item){
28106 // initialize 'blocks'
28107 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
28108 Roo.htmleditor.Block.factory(e).updateElement(e);
28114 Roo.log("calling toggole on editor");
28115 // tell the editor that it's been pressed..
28116 this.editor.toggleSourceEdit(sourceEditMode);
28120 * Object collection of toolbar tooltips for the buttons in the editor. The key
28121 * is the command id associated with that button and the value is a valid QuickTips object.
28126 title: 'Bold (Ctrl+B)',
28127 text: 'Make the selected text bold.',
28128 cls: 'x-html-editor-tip'
28131 title: 'Italic (Ctrl+I)',
28132 text: 'Make the selected text italic.',
28133 cls: 'x-html-editor-tip'
28141 title: 'Bold (Ctrl+B)',
28142 text: 'Make the selected text bold.',
28143 cls: 'x-html-editor-tip'
28146 title: 'Italic (Ctrl+I)',
28147 text: 'Make the selected text italic.',
28148 cls: 'x-html-editor-tip'
28151 title: 'Underline (Ctrl+U)',
28152 text: 'Underline the selected text.',
28153 cls: 'x-html-editor-tip'
28156 title: 'Strikethrough',
28157 text: 'Strikethrough the selected text.',
28158 cls: 'x-html-editor-tip'
28160 increasefontsize : {
28161 title: 'Grow Text',
28162 text: 'Increase the font size.',
28163 cls: 'x-html-editor-tip'
28165 decreasefontsize : {
28166 title: 'Shrink Text',
28167 text: 'Decrease the font size.',
28168 cls: 'x-html-editor-tip'
28171 title: 'Text Highlight Color',
28172 text: 'Change the background color of the selected text.',
28173 cls: 'x-html-editor-tip'
28176 title: 'Font Color',
28177 text: 'Change the color of the selected text.',
28178 cls: 'x-html-editor-tip'
28181 title: 'Align Text Left',
28182 text: 'Align text to the left.',
28183 cls: 'x-html-editor-tip'
28186 title: 'Center Text',
28187 text: 'Center text in the editor.',
28188 cls: 'x-html-editor-tip'
28191 title: 'Align Text Right',
28192 text: 'Align text to the right.',
28193 cls: 'x-html-editor-tip'
28195 insertunorderedlist : {
28196 title: 'Bullet List',
28197 text: 'Start a bulleted list.',
28198 cls: 'x-html-editor-tip'
28200 insertorderedlist : {
28201 title: 'Numbered List',
28202 text: 'Start a numbered list.',
28203 cls: 'x-html-editor-tip'
28206 title: 'Hyperlink',
28207 text: 'Make the selected text a hyperlink.',
28208 cls: 'x-html-editor-tip'
28211 title: 'Source Edit',
28212 text: 'Switch to source editing mode.',
28213 cls: 'x-html-editor-tip'
28217 onDestroy : function(){
28220 this.tb.items.each(function(item){
28222 item.menu.removeAll();
28224 item.menu.el.destroy();
28232 onFirstFocus: function() {
28233 this.tb.items.each(function(item){
28242 // <script type="text/javascript">
28245 * Ext JS Library 1.1.1
28246 * Copyright(c) 2006-2007, Ext JS, LLC.
28253 * @class Roo.form.HtmlEditor.ToolbarContext
28258 new Roo.form.HtmlEditor({
28261 { xtype: 'ToolbarStandard', styles : {} }
28262 { xtype: 'ToolbarContext', disable : {} }
28268 * @config : {Object} disable List of elements to disable.. (not done yet.)
28269 * @config : {Object} styles Map of styles available.
28273 Roo.form.HtmlEditor.ToolbarContext = function(config)
28276 Roo.apply(this, config);
28277 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28278 // dont call parent... till later.
28279 this.styles = this.styles || {};
28284 Roo.form.HtmlEditor.ToolbarContext.types = {
28299 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28325 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28396 name : 'selectoptions',
28402 // should we really allow this??
28403 // should this just be
28420 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28421 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28423 Roo.form.HtmlEditor.ToolbarContext.options = {
28425 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28426 [ 'Courier New', 'Courier New'],
28427 [ 'Tahoma', 'Tahoma'],
28428 [ 'Times New Roman,serif', 'Times'],
28429 [ 'Verdana','Verdana' ]
28433 // fixme - these need to be configurable..
28436 //Roo.form.HtmlEditor.ToolbarContext.types
28439 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
28446 editorcore : false,
28448 * @cfg {Object} disable List of toolbar elements to disable
28453 * @cfg {Object} styles List of styles
28454 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
28456 * These must be defined in the page, so they get rendered correctly..
28467 init : function(editor)
28469 this.editor = editor;
28470 this.editorcore = editor.editorcore ? editor.editorcore : editor;
28471 var editorcore = this.editorcore;
28473 var fid = editorcore.frameId;
28475 function btn(id, toggle, handler){
28476 var xid = fid + '-'+ id ;
28480 cls : 'x-btn-icon x-edit-'+id,
28481 enableToggle:toggle !== false,
28482 scope: editorcore, // was editor...
28483 handler:handler||editorcore.relayBtnCmd,
28484 clickEvent:'mousedown',
28485 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28489 // create a new element.
28490 var wdiv = editor.wrap.createChild({
28492 }, editor.wrap.dom.firstChild.nextSibling, true);
28494 // can we do this more than once??
28496 // stop form submits
28499 // disable everything...
28500 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28501 this.toolbars = {};
28502 // block toolbars are built in updateToolbar when needed.
28503 for (var i in ty) {
28505 this.toolbars[i] = this.buildToolbar(ty[i],i);
28507 this.tb = this.toolbars.BODY;
28509 this.buildFooter();
28510 this.footer.show();
28511 editor.on('hide', function( ) { this.footer.hide() }, this);
28512 editor.on('show', function( ) { this.footer.show() }, this);
28515 this.rendered = true;
28517 // the all the btns;
28518 editor.on('editorevent', this.updateToolbar, this);
28519 // other toolbars need to implement this..
28520 //editor.on('editmodechange', this.updateToolbar, this);
28526 * Protected method that will not generally be called directly. It triggers
28527 * a toolbar update by reading the markup state of the current selection in the editor.
28529 * Note you can force an update by calling on('editorevent', scope, false)
28531 updateToolbar: function(editor ,ev, sel)
28535 ev.stopEvent(); // se if we can stop this looping with mutiple events.
28539 // capture mouse up - this is handy for selecting images..
28540 // perhaps should go somewhere else...
28541 if(!this.editorcore.activated){
28542 this.editor.onFirstFocus();
28545 //Roo.log(ev ? ev.target : 'NOTARGET');
28548 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28549 // selectNode - might want to handle IE?
28554 (ev.type == 'mouseup' || ev.type == 'click' ) &&
28555 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28556 // they have click on an image...
28557 // let's see if we can change the selection...
28560 // this triggers looping?
28561 //this.editorcore.selectNode(sel);
28565 // this forces an id..
28566 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28567 e.classList.remove('roo-ed-selection');
28569 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28570 //Roo.get(node).addClass('roo-ed-selection');
28572 //var updateFooter = sel ? false : true;
28575 var ans = this.editorcore.getAllAncestors();
28578 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28581 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
28582 sel = sel ? sel : this.editorcore.doc.body;
28583 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28587 var tn = sel.tagName.toUpperCase();
28588 var lastSel = this.tb.selectedNode;
28589 this.tb.selectedNode = sel;
28590 var left_label = tn;
28592 // ok see if we are editing a block?
28595 // you are not actually selecting the block.
28596 if (sel && sel.hasAttribute('data-block')) {
28598 } else if (sel && sel.closest('[data-block]')) {
28600 db = sel.closest('[data-block]');
28601 //var cepar = sel.closest('[contenteditable=true]');
28602 //if (db && cepar && cepar.tagName != 'BODY') {
28603 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28609 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28610 if (db && this.editorcore.enableBlocks) {
28611 block = Roo.htmleditor.Block.factory(db);
28616 db.classList.length > 0 ? db.className + ' ' : ''
28617 ) + 'roo-ed-selection';
28619 // since we removed it earlier... its not there..
28620 tn = 'BLOCK.' + db.getAttribute('data-block');
28622 //this.editorcore.selectNode(db);
28623 if (typeof(this.toolbars[tn]) == 'undefined') {
28624 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
28626 this.toolbars[tn].selectedNode = db;
28627 left_label = block.friendly_name;
28628 ans = this.editorcore.getAllAncestors();
28636 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28637 return; // no change?
28643 ///console.log("show: " + tn);
28644 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28648 this.tb.items.first().el.innerHTML = left_label + ': ';
28651 // update attributes
28652 if (block && this.tb.fields) {
28654 this.tb.fields.each(function(e) {
28655 e.setValue(block[e.name]);
28659 } else if (this.tb.fields && this.tb.selectedNode) {
28660 this.tb.fields.each( function(e) {
28662 e.setValue(this.tb.selectedNode.style[e.stylename]);
28665 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28667 this.updateToolbarStyles(this.tb.selectedNode);
28672 Roo.menu.MenuMgr.hideAll();
28677 // update the footer
28679 this.updateFooter(ans);
28683 updateToolbarStyles : function(sel)
28685 var hasStyles = false;
28686 for(var i in this.styles) {
28692 if (hasStyles && this.tb.hasStyles) {
28693 var st = this.tb.fields.item(0);
28695 st.store.removeAll();
28696 var cn = sel.className.split(/\s+/);
28699 if (this.styles['*']) {
28701 Roo.each(this.styles['*'], function(v) {
28702 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
28705 if (this.styles[tn]) {
28706 Roo.each(this.styles[tn], function(v) {
28707 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
28711 st.store.loadData(avs);
28718 updateFooter : function(ans)
28721 if (ans === false) {
28722 this.footDisp.dom.innerHTML = '';
28726 this.footerEls = ans.reverse();
28727 Roo.each(this.footerEls, function(a,i) {
28728 if (!a) { return; }
28729 html += html.length ? ' > ' : '';
28731 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28736 var sz = this.footDisp.up('td').getSize();
28737 this.footDisp.dom.style.width = (sz.width -10) + 'px';
28738 this.footDisp.dom.style.marginLeft = '5px';
28740 this.footDisp.dom.style.overflow = 'hidden';
28742 this.footDisp.dom.innerHTML = html;
28749 onDestroy : function(){
28752 this.tb.items.each(function(item){
28754 item.menu.removeAll();
28756 item.menu.el.destroy();
28764 onFirstFocus: function() {
28765 // need to do this for all the toolbars..
28766 this.tb.items.each(function(item){
28770 buildToolbar: function(tlist, nm, friendly_name, block)
28772 var editor = this.editor;
28773 var editorcore = this.editorcore;
28774 // create a new element.
28775 var wdiv = editor.wrap.createChild({
28777 }, editor.wrap.dom.firstChild.nextSibling, true);
28780 var tb = new Roo.Toolbar(wdiv);
28781 ///this.tb = tb; // << this sets the active toolbar..
28782 if (tlist === false && block) {
28783 tlist = block.contextMenu(this);
28786 tb.hasStyles = false;
28789 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
28791 var styles = Array.from(this.styles);
28795 if (styles && styles.length) {
28796 tb.hasStyles = true;
28797 // this needs a multi-select checkbox...
28798 tb.addField( new Roo.form.ComboBox({
28799 store: new Roo.data.SimpleStore({
28801 fields: ['val', 'selected'],
28804 name : '-roo-edit-className',
28805 attrname : 'className',
28806 displayField: 'val',
28810 triggerAction: 'all',
28811 emptyText:'Select Style',
28812 selectOnFocus:true,
28815 'select': function(c, r, i) {
28816 // initial support only for on class per el..
28817 tb.selectedNode.className = r ? r.get('val') : '';
28818 editorcore.syncValue();
28825 var tbc = Roo.form.HtmlEditor.ToolbarContext;
28828 for (var i = 0; i < tlist.length; i++) {
28830 // newer versions will use xtype cfg to create menus.
28831 if (typeof(tlist[i].xtype) != 'undefined') {
28833 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28839 var item = tlist[i];
28840 tb.add(item.title + ": ");
28843 //optname == used so you can configure the options available..
28844 var opts = item.opts ? item.opts : false;
28845 if (item.optname) { // use the b
28846 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28851 // opts == pulldown..
28852 tb.addField( new Roo.form.ComboBox({
28853 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28855 fields: ['val', 'display'],
28858 name : '-roo-edit-' + tlist[i].name,
28860 attrname : tlist[i].name,
28861 stylename : item.style ? item.style : false,
28863 displayField: item.displayField ? item.displayField : 'val',
28864 valueField : 'val',
28866 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
28868 triggerAction: 'all',
28869 emptyText:'Select',
28870 selectOnFocus:true,
28871 width: item.width ? item.width : 130,
28873 'select': function(c, r, i) {
28877 tb.selectedNode.style[c.stylename] = r.get('val');
28878 editorcore.syncValue();
28882 tb.selectedNode.removeAttribute(c.attrname);
28883 editorcore.syncValue();
28886 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28887 editorcore.syncValue();
28896 tb.addField( new Roo.form.TextField({
28899 //allowBlank:false,
28905 tb.addField( new Roo.form.TextField({
28906 name: '-roo-edit-' + tlist[i].name,
28907 attrname : tlist[i].name,
28913 'change' : function(f, nv, ov) {
28916 tb.selectedNode.setAttribute(f.attrname, nv);
28917 editorcore.syncValue();
28925 var show_delete = !block || block.deleteTitle !== false;
28927 show_delete = false;
28931 text: 'Stylesheets',
28934 click : function ()
28936 _this.editor.fireEvent('stylesheetsclick', _this.editor);
28945 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28948 click : function ()
28950 var sn = tb.selectedNode;
28952 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28958 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28959 if (sn.hasAttribute('data-block')) {
28960 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
28961 sn.parentNode.removeChild(sn);
28963 } else if (sn && sn.tagName != 'BODY') {
28964 // remove and keep parents.
28965 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28970 var range = editorcore.createRange();
28972 range.setStart(stn,0);
28973 range.setEnd(stn,0);
28974 var selection = editorcore.getSelection();
28975 selection.removeAllRanges();
28976 selection.addRange(range);
28979 //_this.updateToolbar(null, null, pn);
28980 _this.updateToolbar(null, null, null);
28981 _this.updateFooter(false);
28992 tb.el.on('click', function(e){
28993 e.preventDefault(); // what does this do?
28995 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28998 // dont need to disable them... as they will get hidden
29003 buildFooter : function()
29006 var fel = this.editor.wrap.createChild();
29007 this.footer = new Roo.Toolbar(fel);
29008 // toolbar has scrolly on left / right?
29009 var footDisp= new Roo.Toolbar.Fill();
29015 handler : function() {
29016 _t.footDisp.scrollTo('left',0,true)
29020 this.footer.add( footDisp );
29025 handler : function() {
29027 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
29031 var fel = Roo.get(footDisp.el);
29032 fel.addClass('x-editor-context');
29033 this.footDispWrap = fel;
29034 this.footDispWrap.overflow = 'hidden';
29036 this.footDisp = fel.createChild();
29037 this.footDispWrap.on('click', this.onContextClick, this)
29041 // when the footer contect changes
29042 onContextClick : function (ev,dom)
29044 ev.preventDefault();
29045 var cn = dom.className;
29047 if (!cn.match(/x-ed-loc-/)) {
29050 var n = cn.split('-').pop();
29051 var ans = this.footerEls;
29054 this.editorcore.selectNode(sel);
29057 this.updateToolbar(null, null, sel);
29074 * Ext JS Library 1.1.1
29075 * Copyright(c) 2006-2007, Ext JS, LLC.
29077 * Originally Released Under LGPL - original licence link has changed is not relivant.
29080 * <script type="text/javascript">
29084 * @class Roo.form.BasicForm
29085 * @extends Roo.util.Observable
29086 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
29088 * @param {String/HTMLElement/Roo.Element} el The form element or its id
29089 * @param {Object} config Configuration options
29091 Roo.form.BasicForm = function(el, config){
29092 this.allItems = [];
29093 this.childForms = [];
29094 Roo.apply(this, config);
29096 * The Roo.form.Field items in this form.
29097 * @type MixedCollection
29101 this.items = new Roo.util.MixedCollection(false, function(o){
29102 return o.id || (o.id = Roo.id());
29106 * @event beforeaction
29107 * Fires before any action is performed. Return false to cancel the action.
29108 * @param {Form} this
29109 * @param {Action} action The action to be performed
29111 beforeaction: true,
29113 * @event actionfailed
29114 * Fires when an action fails.
29115 * @param {Form} this
29116 * @param {Action} action The action that failed
29118 actionfailed : true,
29120 * @event actioncomplete
29121 * Fires when an action is completed.
29122 * @param {Form} this
29123 * @param {Action} action The action that completed
29125 actioncomplete : true
29130 Roo.form.BasicForm.superclass.constructor.call(this);
29132 Roo.form.BasicForm.popover.apply();
29135 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
29137 * @cfg {String} method
29138 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
29141 * @cfg {DataReader} reader
29142 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
29143 * This is optional as there is built-in support for processing JSON.
29146 * @cfg {DataReader} errorReader
29147 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
29148 * This is completely optional as there is built-in support for processing JSON.
29151 * @cfg {String} url
29152 * The URL to use for form actions if one isn't supplied in the action options.
29155 * @cfg {Boolean} fileUpload
29156 * Set to true if this form is a file upload.
29160 * @cfg {Object} baseParams
29161 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
29166 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
29171 activeAction : null,
29174 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
29175 * or setValues() data instead of when the form was first created.
29177 trackResetOnLoad : false,
29181 * childForms - used for multi-tab forms
29184 childForms : false,
29187 * allItems - full list of fields.
29193 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
29194 * element by passing it or its id or mask the form itself by passing in true.
29197 waitMsgTarget : false,
29202 disableMask : false,
29205 * @cfg {Boolean} errorMask (true|false) default false
29210 * @cfg {Number} maskOffset Default 100
29215 initEl : function(el){
29216 this.el = Roo.get(el);
29217 this.id = this.el.id || Roo.id();
29218 this.el.on('submit', this.onSubmit, this);
29219 this.el.addClass('x-form');
29223 onSubmit : function(e){
29228 * Returns true if client-side validation on the form is successful.
29231 isValid : function(){
29233 var target = false;
29234 this.items.each(function(f){
29241 if(!target && f.el.isVisible(true)){
29246 if(this.errorMask && !valid){
29247 Roo.form.BasicForm.popover.mask(this, target);
29253 * Returns array of invalid form fields.
29257 invalidFields : function()
29260 this.items.each(function(f){
29273 * DEPRICATED Returns true if any fields in this form have changed since their original load.
29276 isDirty : function(){
29278 this.items.each(function(f){
29288 * Returns true if any fields in this form have changed since their original load. (New version)
29292 hasChanged : function()
29295 this.items.each(function(f){
29296 if(f.hasChanged()){
29305 * Resets all hasChanged to 'false' -
29306 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29307 * So hasChanged storage is only to be used for this purpose
29310 resetHasChanged : function()
29312 this.items.each(function(f){
29313 f.resetHasChanged();
29320 * Performs a predefined action (submit or load) or custom actions you define on this form.
29321 * @param {String} actionName The name of the action type
29322 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
29323 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29324 * accept other config options):
29326 Property Type Description
29327 ---------------- --------------- ----------------------------------------------------------------------------------
29328 url String The url for the action (defaults to the form's url)
29329 method String The form method to use (defaults to the form's method, or POST if not defined)
29330 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
29331 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
29332 validate the form on the client (defaults to false)
29334 * @return {BasicForm} this
29336 doAction : function(action, options){
29337 if(typeof action == 'string'){
29338 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29340 if(this.fireEvent('beforeaction', this, action) !== false){
29341 this.beforeAction(action);
29342 action.run.defer(100, action);
29348 * Shortcut to do a submit action.
29349 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29350 * @return {BasicForm} this
29352 submit : function(options){
29353 this.doAction('submit', options);
29358 * Shortcut to do a load action.
29359 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29360 * @return {BasicForm} this
29362 load : function(options){
29363 this.doAction('load', options);
29368 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29369 * @param {Record} record The record to edit
29370 * @return {BasicForm} this
29372 updateRecord : function(record){
29373 record.beginEdit();
29374 var fs = record.fields;
29375 fs.each(function(f){
29376 var field = this.findField(f.name);
29378 record.set(f.name, field.getValue());
29386 * Loads an Roo.data.Record into this form.
29387 * @param {Record} record The record to load
29388 * @return {BasicForm} this
29390 loadRecord : function(record){
29391 this.setValues(record.data);
29396 beforeAction : function(action){
29397 var o = action.options;
29399 if(!this.disableMask) {
29400 if(this.waitMsgTarget === true){
29401 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29402 }else if(this.waitMsgTarget){
29403 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29404 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29406 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29414 afterAction : function(action, success){
29415 this.activeAction = null;
29416 var o = action.options;
29418 if(!this.disableMask) {
29419 if(this.waitMsgTarget === true){
29421 }else if(this.waitMsgTarget){
29422 this.waitMsgTarget.unmask();
29424 Roo.MessageBox.updateProgress(1);
29425 Roo.MessageBox.hide();
29433 Roo.callback(o.success, o.scope, [this, action]);
29434 this.fireEvent('actioncomplete', this, action);
29438 // failure condition..
29439 // we have a scenario where updates need confirming.
29440 // eg. if a locking scenario exists..
29441 // we look for { errors : { needs_confirm : true }} in the response.
29443 (typeof(action.result) != 'undefined') &&
29444 (typeof(action.result.errors) != 'undefined') &&
29445 (typeof(action.result.errors.needs_confirm) != 'undefined')
29448 Roo.MessageBox.confirm(
29449 "Change requires confirmation",
29450 action.result.errorMsg,
29455 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
29465 Roo.callback(o.failure, o.scope, [this, action]);
29466 // show an error message if no failed handler is set..
29467 if (!this.hasListener('actionfailed')) {
29468 Roo.MessageBox.alert("Error",
29469 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29470 action.result.errorMsg :
29471 "Saving Failed, please check your entries or try again"
29475 this.fireEvent('actionfailed', this, action);
29481 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29482 * @param {String} id The value to search for
29485 findField : function(id){
29486 var field = this.items.get(id);
29488 this.items.each(function(f){
29489 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29495 return field || null;
29499 * Add a secondary form to this one,
29500 * Used to provide tabbed forms. One form is primary, with hidden values
29501 * which mirror the elements from the other forms.
29503 * @param {Roo.form.Form} form to add.
29506 addForm : function(form)
29509 if (this.childForms.indexOf(form) > -1) {
29513 this.childForms.push(form);
29515 Roo.each(form.allItems, function (fe) {
29517 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29518 if (this.findField(n)) { // already added..
29521 var add = new Roo.form.Hidden({
29524 add.render(this.el);
29531 * Mark fields in this form invalid in bulk.
29532 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29533 * @return {BasicForm} this
29535 markInvalid : function(errors){
29536 if(errors instanceof Array){
29537 for(var i = 0, len = errors.length; i < len; i++){
29538 var fieldError = errors[i];
29539 var f = this.findField(fieldError.id);
29541 f.markInvalid(fieldError.msg);
29547 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29548 field.markInvalid(errors[id]);
29552 Roo.each(this.childForms || [], function (f) {
29553 f.markInvalid(errors);
29560 * Set values for fields in this form in bulk.
29561 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29562 * @return {BasicForm} this
29564 setValues : function(values){
29565 if(values instanceof Array){ // array of objects
29566 for(var i = 0, len = values.length; i < len; i++){
29568 var f = this.findField(v.id);
29570 f.setValue(v.value);
29571 if(this.trackResetOnLoad){
29572 f.originalValue = f.getValue();
29576 }else{ // object hash
29579 if(typeof values[id] != 'function' && (field = this.findField(id))){
29581 if (field.setFromData &&
29582 field.valueField &&
29583 field.displayField &&
29584 // combos' with local stores can
29585 // be queried via setValue()
29586 // to set their value..
29587 (field.store && !field.store.isLocal)
29591 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29592 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29593 field.setFromData(sd);
29596 field.setValue(values[id]);
29600 if(this.trackResetOnLoad){
29601 field.originalValue = field.getValue();
29606 this.resetHasChanged();
29609 Roo.each(this.childForms || [], function (f) {
29610 f.setValues(values);
29611 f.resetHasChanged();
29618 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29619 * they are returned as an array.
29620 * @param {Boolean} asString
29623 getValues : function(asString)
29625 if (this.childForms) {
29626 // copy values from the child forms
29627 Roo.each(this.childForms, function (f) {
29628 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29633 if (typeof(FormData) != 'undefined' && asString !== true) {
29634 // this relies on a 'recent' version of chrome apparently...
29636 var fd = (new FormData(this.el.dom)).entries();
29638 var ent = fd.next();
29639 while (!ent.done) {
29640 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29651 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29652 if(asString === true){
29655 return Roo.urlDecode(fs);
29659 * Returns the fields in this form as an object with key/value pairs.
29660 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29661 * Normally this will not return readOnly data
29662 * @param {Boolean} with_readonly return readonly field data.
29665 getFieldValues : function(with_readonly)
29667 if (this.childForms) {
29668 // copy values from the child forms
29669 // should this call getFieldValues - probably not as we do not currently copy
29670 // hidden fields when we generate..
29671 Roo.each(this.childForms, function (f) {
29672 this.setValues(f.getFieldValues());
29677 this.items.each(function(f){
29679 if (f.readOnly && with_readonly !== true) {
29680 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29681 // if a subform contains a copy of them.
29682 // if you have subforms with the same editable data, you will need to copy the data back
29686 if (!f.getName()) {
29689 var v = f.getValue();
29690 if (f.inputType =='radio') {
29691 if (typeof(ret[f.getName()]) == 'undefined') {
29692 ret[f.getName()] = ''; // empty..
29695 if (!f.el.dom.checked) {
29699 v = f.el.dom.value;
29703 // not sure if this supported any more..
29704 if ((typeof(v) == 'object') && f.getRawValue) {
29705 v = f.getRawValue() ; // dates..
29707 // combo boxes where name != hiddenName...
29708 if (f.name != f.getName()) {
29709 ret[f.name] = f.getRawValue();
29711 ret[f.getName()] = v;
29718 * Clears all invalid messages in this form.
29719 * @return {BasicForm} this
29721 clearInvalid : function(){
29722 this.items.each(function(f){
29726 Roo.each(this.childForms || [], function (f) {
29735 * Resets this form.
29736 * @return {BasicForm} this
29738 reset : function(){
29739 this.items.each(function(f){
29743 Roo.each(this.childForms || [], function (f) {
29746 this.resetHasChanged();
29752 * Add Roo.form components to this form.
29753 * @param {Field} field1
29754 * @param {Field} field2 (optional)
29755 * @param {Field} etc (optional)
29756 * @return {BasicForm} this
29759 this.items.addAll(Array.prototype.slice.call(arguments, 0));
29765 * Removes a field from the items collection (does NOT remove its markup).
29766 * @param {Field} field
29767 * @return {BasicForm} this
29769 remove : function(field){
29770 this.items.remove(field);
29775 * Looks at the fields in this form, checks them for an id attribute,
29776 * and calls applyTo on the existing dom element with that id.
29777 * @return {BasicForm} this
29779 render : function(){
29780 this.items.each(function(f){
29781 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29789 * Calls {@link Ext#apply} for all fields in this form with the passed object.
29790 * @param {Object} values
29791 * @return {BasicForm} this
29793 applyToFields : function(o){
29794 this.items.each(function(f){
29801 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29802 * @param {Object} values
29803 * @return {BasicForm} this
29805 applyIfToFields : function(o){
29806 this.items.each(function(f){
29814 Roo.BasicForm = Roo.form.BasicForm;
29816 Roo.apply(Roo.form.BasicForm, {
29830 intervalID : false,
29836 if(this.isApplied){
29841 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29842 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29843 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29844 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29847 this.maskEl.top.enableDisplayMode("block");
29848 this.maskEl.left.enableDisplayMode("block");
29849 this.maskEl.bottom.enableDisplayMode("block");
29850 this.maskEl.right.enableDisplayMode("block");
29852 Roo.get(document.body).on('click', function(){
29856 Roo.get(document.body).on('touchstart', function(){
29860 this.isApplied = true
29863 mask : function(form, target)
29867 this.target = target;
29869 if(!this.form.errorMask || !target.el){
29873 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29875 var ot = this.target.el.calcOffsetsTo(scrollable);
29877 var scrollTo = ot[1] - this.form.maskOffset;
29879 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29881 scrollable.scrollTo('top', scrollTo);
29883 var el = this.target.wrap || this.target.el;
29885 var box = el.getBox();
29887 this.maskEl.top.setStyle('position', 'absolute');
29888 this.maskEl.top.setStyle('z-index', 10000);
29889 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29890 this.maskEl.top.setLeft(0);
29891 this.maskEl.top.setTop(0);
29892 this.maskEl.top.show();
29894 this.maskEl.left.setStyle('position', 'absolute');
29895 this.maskEl.left.setStyle('z-index', 10000);
29896 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29897 this.maskEl.left.setLeft(0);
29898 this.maskEl.left.setTop(box.y - this.padding);
29899 this.maskEl.left.show();
29901 this.maskEl.bottom.setStyle('position', 'absolute');
29902 this.maskEl.bottom.setStyle('z-index', 10000);
29903 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29904 this.maskEl.bottom.setLeft(0);
29905 this.maskEl.bottom.setTop(box.bottom + this.padding);
29906 this.maskEl.bottom.show();
29908 this.maskEl.right.setStyle('position', 'absolute');
29909 this.maskEl.right.setStyle('z-index', 10000);
29910 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29911 this.maskEl.right.setLeft(box.right + this.padding);
29912 this.maskEl.right.setTop(box.y - this.padding);
29913 this.maskEl.right.show();
29915 this.intervalID = window.setInterval(function() {
29916 Roo.form.BasicForm.popover.unmask();
29919 window.onwheel = function(){ return false;};
29921 (function(){ this.isMasked = true; }).defer(500, this);
29925 unmask : function()
29927 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29931 this.maskEl.top.setStyle('position', 'absolute');
29932 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29933 this.maskEl.top.hide();
29935 this.maskEl.left.setStyle('position', 'absolute');
29936 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29937 this.maskEl.left.hide();
29939 this.maskEl.bottom.setStyle('position', 'absolute');
29940 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29941 this.maskEl.bottom.hide();
29943 this.maskEl.right.setStyle('position', 'absolute');
29944 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29945 this.maskEl.right.hide();
29947 window.onwheel = function(){ return true;};
29949 if(this.intervalID){
29950 window.clearInterval(this.intervalID);
29951 this.intervalID = false;
29954 this.isMasked = false;
29962 * Ext JS Library 1.1.1
29963 * Copyright(c) 2006-2007, Ext JS, LLC.
29965 * Originally Released Under LGPL - original licence link has changed is not relivant.
29968 * <script type="text/javascript">
29972 * @class Roo.form.Form
29973 * @extends Roo.form.BasicForm
29974 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29975 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29977 * @param {Object} config Configuration options
29979 Roo.form.Form = function(config){
29981 if (config.items) {
29982 xitems = config.items;
29983 delete config.items;
29987 Roo.form.Form.superclass.constructor.call(this, null, config);
29988 this.url = this.url || this.action;
29990 this.root = new Roo.form.Layout(Roo.applyIf({
29994 this.active = this.root;
29996 * Array of all the buttons that have been added to this form via {@link addButton}
30000 this.allItems = [];
30003 * @event clientvalidation
30004 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
30005 * @param {Form} this
30006 * @param {Boolean} valid true if the form has passed client-side validation
30008 clientvalidation: true,
30011 * Fires when the form is rendered
30012 * @param {Roo.form.Form} form
30017 if (this.progressUrl) {
30018 // push a hidden field onto the list of fields..
30022 name : 'UPLOAD_IDENTIFIER'
30027 Roo.each(xitems, this.addxtype, this);
30031 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
30033 * @cfg {Roo.Button} buttons[] buttons at bottom of form
30037 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
30040 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
30043 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
30045 buttonAlign:'center',
30048 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
30053 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
30054 * This property cascades to child containers if not set.
30059 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
30060 * fires a looping event with that state. This is required to bind buttons to the valid
30061 * state using the config value formBind:true on the button.
30063 monitorValid : false,
30066 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
30071 * @cfg {String} progressUrl - Url to return progress data
30074 progressUrl : false,
30076 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
30077 * sending a formdata with extra parameters - eg uploaded elements.
30083 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
30084 * fields are added and the column is closed. If no fields are passed the column remains open
30085 * until end() is called.
30086 * @param {Object} config The config to pass to the column
30087 * @param {Field} field1 (optional)
30088 * @param {Field} field2 (optional)
30089 * @param {Field} etc (optional)
30090 * @return Column The column container object
30092 column : function(c){
30093 var col = new Roo.form.Column(c);
30095 if(arguments.length > 1){ // duplicate code required because of Opera
30096 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30103 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
30104 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
30105 * until end() is called.
30106 * @param {Object} config The config to pass to the fieldset
30107 * @param {Field} field1 (optional)
30108 * @param {Field} field2 (optional)
30109 * @param {Field} etc (optional)
30110 * @return FieldSet The fieldset container object
30112 fieldset : function(c){
30113 var fs = new Roo.form.FieldSet(c);
30115 if(arguments.length > 1){ // duplicate code required because of Opera
30116 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30123 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
30124 * fields are added and the container is closed. If no fields are passed the container remains open
30125 * until end() is called.
30126 * @param {Object} config The config to pass to the Layout
30127 * @param {Field} field1 (optional)
30128 * @param {Field} field2 (optional)
30129 * @param {Field} etc (optional)
30130 * @return Layout The container object
30132 container : function(c){
30133 var l = new Roo.form.Layout(c);
30135 if(arguments.length > 1){ // duplicate code required because of Opera
30136 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30143 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
30144 * @param {Object} container A Roo.form.Layout or subclass of Layout
30145 * @return {Form} this
30147 start : function(c){
30148 // cascade label info
30149 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
30150 this.active.stack.push(c);
30151 c.ownerCt = this.active;
30157 * Closes the current open container
30158 * @return {Form} this
30161 if(this.active == this.root){
30164 this.active = this.active.ownerCt;
30169 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
30170 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
30171 * as the label of the field.
30172 * @param {Field} field1
30173 * @param {Field} field2 (optional)
30174 * @param {Field} etc. (optional)
30175 * @return {Form} this
30178 this.active.stack.push.apply(this.active.stack, arguments);
30179 this.allItems.push.apply(this.allItems,arguments);
30181 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
30182 if(a[i].isFormField){
30187 Roo.form.Form.superclass.add.apply(this, r);
30197 * Find any element that has been added to a form, using it's ID or name
30198 * This can include framesets, columns etc. along with regular fields..
30199 * @param {String} id - id or name to find.
30201 * @return {Element} e - or false if nothing found.
30203 findbyId : function(id)
30209 Roo.each(this.allItems, function(f){
30210 if (f.id == id || f.name == id ){
30221 * Render this form into the passed container. This should only be called once!
30222 * @param {String/HTMLElement/Element} container The element this component should be rendered into
30223 * @return {Form} this
30225 render : function(ct)
30231 var o = this.autoCreate || {
30233 method : this.method || 'POST',
30234 id : this.id || Roo.id()
30236 this.initEl(ct.createChild(o));
30238 this.root.render(this.el);
30242 this.items.each(function(f){
30243 f.render('x-form-el-'+f.id);
30246 if(this.buttons.length > 0){
30247 // tables are required to maintain order and for correct IE layout
30248 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30249 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30250 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30252 var tr = tb.getElementsByTagName('tr')[0];
30253 for(var i = 0, len = this.buttons.length; i < len; i++) {
30254 var b = this.buttons[i];
30255 var td = document.createElement('td');
30256 td.className = 'x-form-btn-td';
30257 b.render(tr.appendChild(td));
30260 if(this.monitorValid){ // initialize after render
30261 this.startMonitoring();
30263 this.fireEvent('rendered', this);
30268 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30269 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30270 * object or a valid Roo.DomHelper element config
30271 * @param {Function} handler The function called when the button is clicked
30272 * @param {Object} scope (optional) The scope of the handler function
30273 * @return {Roo.Button}
30275 addButton : function(config, handler, scope){
30279 minWidth: this.minButtonWidth,
30282 if(typeof config == "string"){
30285 Roo.apply(bc, config);
30287 var btn = new Roo.Button(null, bc);
30288 this.buttons.push(btn);
30293 * Adds a series of form elements (using the xtype property as the factory method.
30294 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30295 * @param {Object} config
30298 addxtype : function()
30300 var ar = Array.prototype.slice.call(arguments, 0);
30302 for(var i = 0; i < ar.length; i++) {
30304 continue; // skip -- if this happends something invalid got sent, we
30305 // should ignore it, as basically that interface element will not show up
30306 // and that should be pretty obvious!!
30309 if (Roo.form[ar[i].xtype]) {
30311 var fe = Roo.factory(ar[i], Roo.form);
30317 fe.store.form = this;
30322 this.allItems.push(fe);
30323 if (fe.items && fe.addxtype) {
30324 fe.addxtype.apply(fe, fe.items);
30334 // console.log('adding ' + ar[i].xtype);
30336 if (ar[i].xtype == 'Button') {
30337 //console.log('adding button');
30338 //console.log(ar[i]);
30339 this.addButton(ar[i]);
30340 this.allItems.push(fe);
30344 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30345 alert('end is not supported on xtype any more, use items');
30347 // //console.log('adding end');
30355 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30356 * option "monitorValid"
30358 startMonitoring : function(){
30361 Roo.TaskMgr.start({
30362 run : this.bindHandler,
30363 interval : this.monitorPoll || 200,
30370 * Stops monitoring of the valid state of this form
30372 stopMonitoring : function(){
30373 this.bound = false;
30377 bindHandler : function(){
30379 return false; // stops binding
30382 this.items.each(function(f){
30383 if(!f.isValid(true)){
30388 for(var i = 0, len = this.buttons.length; i < len; i++){
30389 var btn = this.buttons[i];
30390 if(btn.formBind === true && btn.disabled === valid){
30391 btn.setDisabled(!valid);
30394 this.fireEvent('clientvalidation', this, valid);
30408 Roo.Form = Roo.form.Form;
30411 * Ext JS Library 1.1.1
30412 * Copyright(c) 2006-2007, Ext JS, LLC.
30414 * Originally Released Under LGPL - original licence link has changed is not relivant.
30417 * <script type="text/javascript">
30420 // as we use this in bootstrap.
30421 Roo.namespace('Roo.form');
30423 * @class Roo.form.Action
30424 * Internal Class used to handle form actions
30426 * @param {Roo.form.BasicForm} el The form element or its id
30427 * @param {Object} config Configuration options
30432 // define the action interface
30433 Roo.form.Action = function(form, options){
30435 this.options = options || {};
30438 * Client Validation Failed
30441 Roo.form.Action.CLIENT_INVALID = 'client';
30443 * Server Validation Failed
30446 Roo.form.Action.SERVER_INVALID = 'server';
30448 * Connect to Server Failed
30451 Roo.form.Action.CONNECT_FAILURE = 'connect';
30453 * Reading Data from Server Failed
30456 Roo.form.Action.LOAD_FAILURE = 'load';
30458 Roo.form.Action.prototype = {
30460 failureType : undefined,
30461 response : undefined,
30462 result : undefined,
30464 // interface method
30465 run : function(options){
30469 // interface method
30470 success : function(response){
30474 // interface method
30475 handleResponse : function(response){
30479 // default connection failure
30480 failure : function(response){
30482 this.response = response;
30483 this.failureType = Roo.form.Action.CONNECT_FAILURE;
30484 this.form.afterAction(this, false);
30487 processResponse : function(response){
30488 this.response = response;
30489 if(!response.responseText){
30492 this.result = this.handleResponse(response);
30493 return this.result;
30496 // utility functions used internally
30497 getUrl : function(appendParams){
30498 var url = this.options.url || this.form.url || this.form.el.dom.action;
30500 var p = this.getParams();
30502 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30508 getMethod : function(){
30509 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30512 getParams : function(){
30513 var bp = this.form.baseParams;
30514 var p = this.options.params;
30516 if(typeof p == "object"){
30517 p = Roo.urlEncode(Roo.applyIf(p, bp));
30518 }else if(typeof p == 'string' && bp){
30519 p += '&' + Roo.urlEncode(bp);
30522 p = Roo.urlEncode(bp);
30527 createCallback : function(){
30529 success: this.success,
30530 failure: this.failure,
30532 timeout: (this.form.timeout*1000),
30533 upload: this.form.fileUpload ? this.success : undefined
30538 Roo.form.Action.Submit = function(form, options){
30539 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30542 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30545 haveProgress : false,
30546 uploadComplete : false,
30548 // uploadProgress indicator.
30549 uploadProgress : function()
30551 if (!this.form.progressUrl) {
30555 if (!this.haveProgress) {
30556 Roo.MessageBox.progress("Uploading", "Uploading");
30558 if (this.uploadComplete) {
30559 Roo.MessageBox.hide();
30563 this.haveProgress = true;
30565 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30567 var c = new Roo.data.Connection();
30569 url : this.form.progressUrl,
30574 success : function(req){
30575 //console.log(data);
30579 rdata = Roo.decode(req.responseText)
30581 Roo.log("Invalid data from server..");
30585 if (!rdata || !rdata.success) {
30587 Roo.MessageBox.alert(Roo.encode(rdata));
30590 var data = rdata.data;
30592 if (this.uploadComplete) {
30593 Roo.MessageBox.hide();
30598 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30599 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30602 this.uploadProgress.defer(2000,this);
30605 failure: function(data) {
30606 Roo.log('progress url failed ');
30617 // run get Values on the form, so it syncs any secondary forms.
30618 this.form.getValues();
30620 var o = this.options;
30621 var method = this.getMethod();
30622 var isPost = method == 'POST';
30623 if(o.clientValidation === false || this.form.isValid()){
30625 if (this.form.progressUrl) {
30626 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30627 (new Date() * 1) + '' + Math.random());
30632 Roo.Ajax.request(Roo.apply(this.createCallback(), {
30633 form:this.form.el.dom,
30634 url:this.getUrl(!isPost),
30636 params:isPost ? this.getParams() : null,
30637 isUpload: this.form.fileUpload,
30638 formData : this.form.formData
30641 this.uploadProgress();
30643 }else if (o.clientValidation !== false){ // client validation failed
30644 this.failureType = Roo.form.Action.CLIENT_INVALID;
30645 this.form.afterAction(this, false);
30649 success : function(response)
30651 this.uploadComplete= true;
30652 if (this.haveProgress) {
30653 Roo.MessageBox.hide();
30657 var result = this.processResponse(response);
30658 if(result === true || result.success){
30659 this.form.afterAction(this, true);
30663 this.form.markInvalid(result.errors);
30664 this.failureType = Roo.form.Action.SERVER_INVALID;
30666 this.form.afterAction(this, false);
30668 failure : function(response)
30670 this.uploadComplete= true;
30671 if (this.haveProgress) {
30672 Roo.MessageBox.hide();
30675 this.response = response;
30676 this.failureType = Roo.form.Action.CONNECT_FAILURE;
30677 this.form.afterAction(this, false);
30680 handleResponse : function(response){
30681 if(this.form.errorReader){
30682 var rs = this.form.errorReader.read(response);
30685 for(var i = 0, len = rs.records.length; i < len; i++) {
30686 var r = rs.records[i];
30687 errors[i] = r.data;
30690 if(errors.length < 1){
30694 success : rs.success,
30700 ret = Roo.decode(response.responseText);
30704 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30714 Roo.form.Action.Load = function(form, options){
30715 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30716 this.reader = this.form.reader;
30719 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30724 Roo.Ajax.request(Roo.apply(
30725 this.createCallback(), {
30726 method:this.getMethod(),
30727 url:this.getUrl(false),
30728 params:this.getParams()
30732 success : function(response){
30734 var result = this.processResponse(response);
30735 if(result === true || !result.success || !result.data){
30736 this.failureType = Roo.form.Action.LOAD_FAILURE;
30737 this.form.afterAction(this, false);
30740 this.form.clearInvalid();
30741 this.form.setValues(result.data);
30742 this.form.afterAction(this, true);
30745 handleResponse : function(response){
30746 if(this.form.reader){
30747 var rs = this.form.reader.read(response);
30748 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30750 success : rs.success,
30754 return Roo.decode(response.responseText);
30758 Roo.form.Action.ACTION_TYPES = {
30759 'load' : Roo.form.Action.Load,
30760 'submit' : Roo.form.Action.Submit
30763 * Ext JS Library 1.1.1
30764 * Copyright(c) 2006-2007, Ext JS, LLC.
30766 * Originally Released Under LGPL - original licence link has changed is not relivant.
30769 * <script type="text/javascript">
30773 * @class Roo.form.Layout
30774 * @extends Roo.Component
30775 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30776 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30778 * @param {Object} config Configuration options
30780 Roo.form.Layout = function(config){
30782 if (config.items) {
30783 xitems = config.items;
30784 delete config.items;
30786 Roo.form.Layout.superclass.constructor.call(this, config);
30788 Roo.each(xitems, this.addxtype, this);
30792 Roo.extend(Roo.form.Layout, Roo.Component, {
30794 * @cfg {String/Object} autoCreate
30795 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30798 * @cfg {String/Object/Function} style
30799 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30800 * a function which returns such a specification.
30803 * @cfg {String} labelAlign
30804 * Valid values are "left," "top" and "right" (defaults to "left")
30807 * @cfg {Number} labelWidth
30808 * Fixed width in pixels of all field labels (defaults to undefined)
30811 * @cfg {Boolean} clear
30812 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30816 * @cfg {String} labelSeparator
30817 * The separator to use after field labels (defaults to ':')
30819 labelSeparator : ':',
30821 * @cfg {Boolean} hideLabels
30822 * True to suppress the display of field labels in this layout (defaults to false)
30824 hideLabels : false,
30827 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30832 onRender : function(ct, position){
30833 if(this.el){ // from markup
30834 this.el = Roo.get(this.el);
30835 }else { // generate
30836 var cfg = this.getAutoCreate();
30837 this.el = ct.createChild(cfg, position);
30840 this.el.applyStyles(this.style);
30842 if(this.labelAlign){
30843 this.el.addClass('x-form-label-'+this.labelAlign);
30845 if(this.hideLabels){
30846 this.labelStyle = "display:none";
30847 this.elementStyle = "padding-left:0;";
30849 if(typeof this.labelWidth == 'number'){
30850 this.labelStyle = "width:"+this.labelWidth+"px;";
30851 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30853 if(this.labelAlign == 'top'){
30854 this.labelStyle = "width:auto;";
30855 this.elementStyle = "padding-left:0;";
30858 var stack = this.stack;
30859 var slen = stack.length;
30861 if(!this.fieldTpl){
30862 var t = new Roo.Template(
30863 '<div class="x-form-item {5}">',
30864 '<label for="{0}" style="{2}">{1}{4}</label>',
30865 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30867 '</div><div class="x-form-clear-left"></div>'
30869 t.disableFormats = true;
30871 Roo.form.Layout.prototype.fieldTpl = t;
30873 for(var i = 0; i < slen; i++) {
30874 if(stack[i].isFormField){
30875 this.renderField(stack[i]);
30877 this.renderComponent(stack[i]);
30882 this.el.createChild({cls:'x-form-clear'});
30887 renderField : function(f){
30888 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30891 f.labelStyle||this.labelStyle||'', //2
30892 this.elementStyle||'', //3
30893 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30894 f.itemCls||this.itemCls||'' //5
30895 ], true).getPrevSibling());
30899 renderComponent : function(c){
30900 c.render(c.isLayout ? this.el : this.el.createChild());
30903 * Adds a object form elements (using the xtype property as the factory method.)
30904 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
30905 * @param {Object} config
30907 addxtype : function(o)
30909 // create the lement.
30910 o.form = this.form;
30911 var fe = Roo.factory(o, Roo.form);
30912 this.form.allItems.push(fe);
30913 this.stack.push(fe);
30915 if (fe.isFormField) {
30916 this.form.items.add(fe);
30924 * @class Roo.form.Column
30925 * @extends Roo.form.Layout
30926 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30927 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30929 * @param {Object} config Configuration options
30931 Roo.form.Column = function(config){
30932 Roo.form.Column.superclass.constructor.call(this, config);
30935 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30937 * @cfg {Number/String} width
30938 * The fixed width of the column in pixels or CSS value (defaults to "auto")
30941 * @cfg {String/Object} autoCreate
30942 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30946 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30949 onRender : function(ct, position){
30950 Roo.form.Column.superclass.onRender.call(this, ct, position);
30952 this.el.setWidth(this.width);
30959 * @class Roo.form.Row
30960 * @extends Roo.form.Layout
30961 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30962 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30964 * @param {Object} config Configuration options
30968 Roo.form.Row = function(config){
30969 Roo.form.Row.superclass.constructor.call(this, config);
30972 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30974 * @cfg {Number/String} width
30975 * The fixed width of the column in pixels or CSS value (defaults to "auto")
30978 * @cfg {Number/String} height
30979 * The fixed height of the column in pixels or CSS value (defaults to "auto")
30981 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30985 onRender : function(ct, position){
30986 //console.log('row render');
30988 var t = new Roo.Template(
30989 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30990 '<label for="{0}" style="{2}">{1}{4}</label>',
30991 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30995 t.disableFormats = true;
30997 Roo.form.Layout.prototype.rowTpl = t;
30999 this.fieldTpl = this.rowTpl;
31001 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
31002 var labelWidth = 100;
31004 if ((this.labelAlign != 'top')) {
31005 if (typeof this.labelWidth == 'number') {
31006 labelWidth = this.labelWidth
31008 this.padWidth = 20 + labelWidth;
31012 Roo.form.Column.superclass.onRender.call(this, ct, position);
31014 this.el.setWidth(this.width);
31017 this.el.setHeight(this.height);
31022 renderField : function(f){
31023 f.fieldEl = this.fieldTpl.append(this.el, [
31024 f.id, f.fieldLabel,
31025 f.labelStyle||this.labelStyle||'',
31026 this.elementStyle||'',
31027 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
31028 f.itemCls||this.itemCls||'',
31029 f.width ? f.width + this.padWidth : 160 + this.padWidth
31036 * @class Roo.form.FieldSet
31037 * @extends Roo.form.Layout
31038 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
31039 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
31041 * @param {Object} config Configuration options
31043 Roo.form.FieldSet = function(config){
31044 Roo.form.FieldSet.superclass.constructor.call(this, config);
31047 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
31049 * @cfg {String} legend
31050 * The text to display as the legend for the FieldSet (defaults to '')
31053 * @cfg {String/Object} autoCreate
31054 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
31058 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
31061 onRender : function(ct, position){
31062 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
31064 this.setLegend(this.legend);
31069 setLegend : function(text){
31071 this.el.child('legend').update(text);
31076 * Ext JS Library 1.1.1
31077 * Copyright(c) 2006-2007, Ext JS, LLC.
31079 * Originally Released Under LGPL - original licence link has changed is not relivant.
31082 * <script type="text/javascript">
31085 * @class Roo.form.VTypes
31086 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
31089 Roo.form.VTypes = function(){
31090 // closure these in so they are only created once.
31091 var alpha = /^[a-zA-Z_]+$/;
31092 var alphanum = /^[a-zA-Z0-9_]+$/;
31093 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
31094 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
31096 // All these messages and functions are configurable
31099 * The function used to validate email addresses
31100 * @param {String} value The email address
31102 'email' : function(v){
31103 return email.test(v);
31106 * The error text to display when the email validation function returns false
31109 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
31111 * The keystroke filter mask to be applied on email input
31114 'emailMask' : /[a-z0-9_\.\-@]/i,
31117 * The function used to validate URLs
31118 * @param {String} value The URL
31120 'url' : function(v){
31121 return url.test(v);
31124 * The error text to display when the url validation function returns false
31127 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
31130 * The function used to validate alpha values
31131 * @param {String} value The value
31133 'alpha' : function(v){
31134 return alpha.test(v);
31137 * The error text to display when the alpha validation function returns false
31140 'alphaText' : 'This field should only contain letters and _',
31142 * The keystroke filter mask to be applied on alpha input
31145 'alphaMask' : /[a-z_]/i,
31148 * The function used to validate alphanumeric values
31149 * @param {String} value The value
31151 'alphanum' : function(v){
31152 return alphanum.test(v);
31155 * The error text to display when the alphanumeric validation function returns false
31158 'alphanumText' : 'This field should only contain letters, numbers and _',
31160 * The keystroke filter mask to be applied on alphanumeric input
31163 'alphanumMask' : /[a-z0-9_]/i
31165 }();//<script type="text/javascript">
31168 * @class Roo.form.FCKeditor
31169 * @extends Roo.form.TextArea
31170 * Wrapper around the FCKEditor http://www.fckeditor.net
31172 * Creates a new FCKeditor
31173 * @param {Object} config Configuration options
31175 Roo.form.FCKeditor = function(config){
31176 Roo.form.FCKeditor.superclass.constructor.call(this, config);
31179 * @event editorinit
31180 * Fired when the editor is initialized - you can add extra handlers here..
31181 * @param {FCKeditor} this
31182 * @param {Object} the FCK object.
31189 Roo.form.FCKeditor.editors = { };
31190 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
31192 //defaultAutoCreate : {
31193 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
31197 * @cfg {Object} fck options - see fck manual for details.
31202 * @cfg {Object} fck toolbar set (Basic or Default)
31204 toolbarSet : 'Basic',
31206 * @cfg {Object} fck BasePath
31208 basePath : '/fckeditor/',
31216 onRender : function(ct, position)
31219 this.defaultAutoCreate = {
31221 style:"width:300px;height:60px;",
31222 autocomplete: "new-password"
31225 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31228 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31229 if(this.preventScrollbars){
31230 this.el.setStyle("overflow", "hidden");
31232 this.el.setHeight(this.growMin);
31235 //console.log('onrender' + this.getId() );
31236 Roo.form.FCKeditor.editors[this.getId()] = this;
31239 this.replaceTextarea() ;
31243 getEditor : function() {
31244 return this.fckEditor;
31247 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
31248 * @param {Mixed} value The value to set
31252 setValue : function(value)
31254 //console.log('setValue: ' + value);
31256 if(typeof(value) == 'undefined') { // not sure why this is happending...
31259 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31261 //if(!this.el || !this.getEditor()) {
31262 // this.value = value;
31263 //this.setValue.defer(100,this,[value]);
31267 if(!this.getEditor()) {
31271 this.getEditor().SetData(value);
31278 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
31279 * @return {Mixed} value The field value
31281 getValue : function()
31284 if (this.frame && this.frame.dom.style.display == 'none') {
31285 return Roo.form.FCKeditor.superclass.getValue.call(this);
31288 if(!this.el || !this.getEditor()) {
31290 // this.getValue.defer(100,this);
31295 var value=this.getEditor().GetData();
31296 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31297 return Roo.form.FCKeditor.superclass.getValue.call(this);
31303 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
31304 * @return {Mixed} value The field value
31306 getRawValue : function()
31308 if (this.frame && this.frame.dom.style.display == 'none') {
31309 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31312 if(!this.el || !this.getEditor()) {
31313 //this.getRawValue.defer(100,this);
31320 var value=this.getEditor().GetData();
31321 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31322 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31326 setSize : function(w,h) {
31330 //if (this.frame && this.frame.dom.style.display == 'none') {
31331 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31334 //if(!this.el || !this.getEditor()) {
31335 // this.setSize.defer(100,this, [w,h]);
31341 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31343 this.frame.dom.setAttribute('width', w);
31344 this.frame.dom.setAttribute('height', h);
31345 this.frame.setSize(w,h);
31349 toggleSourceEdit : function(value) {
31353 this.el.dom.style.display = value ? '' : 'none';
31354 this.frame.dom.style.display = value ? 'none' : '';
31359 focus: function(tag)
31361 if (this.frame.dom.style.display == 'none') {
31362 return Roo.form.FCKeditor.superclass.focus.call(this);
31364 if(!this.el || !this.getEditor()) {
31365 this.focus.defer(100,this, [tag]);
31372 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31373 this.getEditor().Focus();
31375 if (!this.getEditor().Selection.GetSelection()) {
31376 this.focus.defer(100,this, [tag]);
31381 var r = this.getEditor().EditorDocument.createRange();
31382 r.setStart(tgs[0],0);
31383 r.setEnd(tgs[0],0);
31384 this.getEditor().Selection.GetSelection().removeAllRanges();
31385 this.getEditor().Selection.GetSelection().addRange(r);
31386 this.getEditor().Focus();
31393 replaceTextarea : function()
31395 if ( document.getElementById( this.getId() + '___Frame' ) ) {
31398 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31400 // We must check the elements firstly using the Id and then the name.
31401 var oTextarea = document.getElementById( this.getId() );
31403 var colElementsByName = document.getElementsByName( this.getId() ) ;
31405 oTextarea.style.display = 'none' ;
31407 if ( oTextarea.tabIndex ) {
31408 this.TabIndex = oTextarea.tabIndex ;
31411 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31412 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31413 this.frame = Roo.get(this.getId() + '___Frame')
31416 _getConfigHtml : function()
31420 for ( var o in this.fckconfig ) {
31421 sConfig += sConfig.length > 0 ? '&' : '';
31422 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31425 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31429 _getIFrameHtml : function()
31431 var sFile = 'fckeditor.html' ;
31432 /* no idea what this is about..
31435 if ( (/fcksource=true/i).test( window.top.location.search ) )
31436 sFile = 'fckeditor.original.html' ;
31441 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31442 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
31445 var html = '<iframe id="' + this.getId() +
31446 '___Frame" src="' + sLink +
31447 '" width="' + this.width +
31448 '" height="' + this.height + '"' +
31449 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
31450 ' frameborder="0" scrolling="no"></iframe>' ;
31455 _insertHtmlBefore : function( html, element )
31457 if ( element.insertAdjacentHTML ) {
31459 element.insertAdjacentHTML( 'beforeBegin', html ) ;
31461 var oRange = document.createRange() ;
31462 oRange.setStartBefore( element ) ;
31463 var oFragment = oRange.createContextualFragment( html );
31464 element.parentNode.insertBefore( oFragment, element ) ;
31477 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31479 function FCKeditor_OnComplete(editorInstance){
31480 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31481 f.fckEditor = editorInstance;
31482 //console.log("loaded");
31483 f.fireEvent('editorinit', f, editorInstance);
31503 //<script type="text/javascript">
31505 * @class Roo.form.GridField
31506 * @extends Roo.form.Field
31507 * Embed a grid (or editable grid into a form)
31510 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31512 * xgrid.store = Roo.data.Store
31513 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31514 * xgrid.store.reader = Roo.data.JsonReader
31518 * Creates a new GridField
31519 * @param {Object} config Configuration options
31521 Roo.form.GridField = function(config){
31522 Roo.form.GridField.superclass.constructor.call(this, config);
31526 Roo.extend(Roo.form.GridField, Roo.form.Field, {
31528 * @cfg {Number} width - used to restrict width of grid..
31532 * @cfg {Number} height - used to restrict height of grid..
31536 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31542 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31543 * {tag: "input", type: "checkbox", autocomplete: "off"})
31545 // defaultAutoCreate : { tag: 'div' },
31546 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31548 * @cfg {String} addTitle Text to include for adding a title.
31552 onResize : function(){
31553 Roo.form.Field.superclass.onResize.apply(this, arguments);
31556 initEvents : function(){
31557 // Roo.form.Checkbox.superclass.initEvents.call(this);
31558 // has no events...
31563 getResizeEl : function(){
31567 getPositionEl : function(){
31572 onRender : function(ct, position){
31574 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31575 var style = this.style;
31578 Roo.form.GridField.superclass.onRender.call(this, ct, position);
31579 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31580 this.viewEl = this.wrap.createChild({ tag: 'div' });
31582 this.viewEl.applyStyles(style);
31585 this.viewEl.setWidth(this.width);
31588 this.viewEl.setHeight(this.height);
31590 //if(this.inputValue !== undefined){
31591 //this.setValue(this.value);
31594 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31597 this.grid.render();
31598 this.grid.getDataSource().on('remove', this.refreshValue, this);
31599 this.grid.getDataSource().on('update', this.refreshValue, this);
31600 this.grid.on('afteredit', this.refreshValue, this);
31606 * Sets the value of the item.
31607 * @param {String} either an object or a string..
31609 setValue : function(v){
31611 v = v || []; // empty set..
31612 // this does not seem smart - it really only affects memoryproxy grids..
31613 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31614 var ds = this.grid.getDataSource();
31615 // assumes a json reader..
31617 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
31618 ds.loadData( data);
31620 // clear selection so it does not get stale.
31621 if (this.grid.sm) {
31622 this.grid.sm.clearSelections();
31625 Roo.form.GridField.superclass.setValue.call(this, v);
31626 this.refreshValue();
31627 // should load data in the grid really....
31631 refreshValue: function() {
31633 this.grid.getDataSource().each(function(r) {
31636 this.el.dom.value = Roo.encode(val);
31644 * Ext JS Library 1.1.1
31645 * Copyright(c) 2006-2007, Ext JS, LLC.
31647 * Originally Released Under LGPL - original licence link has changed is not relivant.
31650 * <script type="text/javascript">
31653 * @class Roo.form.DisplayField
31654 * @extends Roo.form.Field
31655 * A generic Field to display non-editable data.
31656 * @cfg {Boolean} closable (true|false) default false
31658 * Creates a new Display Field item.
31659 * @param {Object} config Configuration options
31661 Roo.form.DisplayField = function(config){
31662 Roo.form.DisplayField.superclass.constructor.call(this, config);
31667 * Fires after the click the close btn
31668 * @param {Roo.form.DisplayField} this
31674 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
31675 inputType: 'hidden',
31681 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31683 focusClass : undefined,
31685 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31687 fieldClass: 'x-form-field',
31690 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31692 valueRenderer: undefined,
31696 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31697 * {tag: "input", type: "checkbox", autocomplete: "off"})
31700 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31704 onResize : function(){
31705 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31709 initEvents : function(){
31710 // Roo.form.Checkbox.superclass.initEvents.call(this);
31711 // has no events...
31714 this.closeEl.on('click', this.onClose, this);
31720 getResizeEl : function(){
31724 getPositionEl : function(){
31729 onRender : function(ct, position){
31731 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31732 //if(this.inputValue !== undefined){
31733 this.wrap = this.el.wrap();
31735 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31738 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31741 if (this.bodyStyle) {
31742 this.viewEl.applyStyles(this.bodyStyle);
31744 //this.viewEl.setStyle('padding', '2px');
31746 this.setValue(this.value);
31751 initValue : Roo.emptyFn,
31756 onClick : function(){
31761 * Sets the checked state of the checkbox.
31762 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31764 setValue : function(v){
31766 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
31767 // this might be called before we have a dom element..
31768 if (!this.viewEl) {
31771 this.viewEl.dom.innerHTML = html;
31772 Roo.form.DisplayField.superclass.setValue.call(this, v);
31776 onClose : function(e)
31778 e.preventDefault();
31780 this.fireEvent('close', this);
31789 * @class Roo.form.DayPicker
31790 * @extends Roo.form.Field
31791 * A Day picker show [M] [T] [W] ....
31793 * Creates a new Day Picker
31794 * @param {Object} config Configuration options
31796 Roo.form.DayPicker= function(config){
31797 Roo.form.DayPicker.superclass.constructor.call(this, config);
31801 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
31803 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31805 focusClass : undefined,
31807 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31809 fieldClass: "x-form-field",
31812 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31813 * {tag: "input", type: "checkbox", autocomplete: "off"})
31815 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31818 actionMode : 'viewEl',
31822 inputType : 'hidden',
31825 inputElement: false, // real input element?
31826 basedOn: false, // ????
31828 isFormField: true, // not sure where this is needed!!!!
31830 onResize : function(){
31831 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31832 if(!this.boxLabel){
31833 this.el.alignTo(this.wrap, 'c-c');
31837 initEvents : function(){
31838 Roo.form.Checkbox.superclass.initEvents.call(this);
31839 this.el.on("click", this.onClick, this);
31840 this.el.on("change", this.onClick, this);
31844 getResizeEl : function(){
31848 getPositionEl : function(){
31854 onRender : function(ct, position){
31855 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31857 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31859 var r1 = '<table><tr>';
31860 var r2 = '<tr class="x-form-daypick-icons">';
31861 for (var i=0; i < 7; i++) {
31862 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31863 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
31866 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31867 viewEl.select('img').on('click', this.onClick, this);
31868 this.viewEl = viewEl;
31871 // this will not work on Chrome!!!
31872 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
31873 this.el.on('propertychange', this.setFromHidden, this); //ie
31881 initValue : Roo.emptyFn,
31884 * Returns the checked state of the checkbox.
31885 * @return {Boolean} True if checked, else false
31887 getValue : function(){
31888 return this.el.dom.value;
31893 onClick : function(e){
31894 //this.setChecked(!this.checked);
31895 Roo.get(e.target).toggleClass('x-menu-item-checked');
31896 this.refreshValue();
31897 //if(this.el.dom.checked != this.checked){
31898 // this.setValue(this.el.dom.checked);
31903 refreshValue : function()
31906 this.viewEl.select('img',true).each(function(e,i,n) {
31907 val += e.is(".x-menu-item-checked") ? String(n) : '';
31909 this.setValue(val, true);
31913 * Sets the checked state of the checkbox.
31914 * On is always based on a string comparison between inputValue and the param.
31915 * @param {Boolean/String} value - the value to set
31916 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31918 setValue : function(v,suppressEvent){
31919 if (!this.el.dom) {
31922 var old = this.el.dom.value ;
31923 this.el.dom.value = v;
31924 if (suppressEvent) {
31928 // update display..
31929 this.viewEl.select('img',true).each(function(e,i,n) {
31931 var on = e.is(".x-menu-item-checked");
31932 var newv = v.indexOf(String(n)) > -1;
31934 e.toggleClass('x-menu-item-checked');
31940 this.fireEvent('change', this, v, old);
31945 // handle setting of hidden value by some other method!!?!?
31946 setFromHidden: function()
31951 //console.log("SET FROM HIDDEN");
31952 //alert('setFrom hidden');
31953 this.setValue(this.el.dom.value);
31956 onDestroy : function()
31959 Roo.get(this.viewEl).remove();
31962 Roo.form.DayPicker.superclass.onDestroy.call(this);
31966 * RooJS Library 1.1.1
31967 * Copyright(c) 2008-2011 Alan Knowles
31974 * @class Roo.form.ComboCheck
31975 * @extends Roo.form.ComboBox
31976 * A combobox for multiple select items.
31978 * FIXME - could do with a reset button..
31981 * Create a new ComboCheck
31982 * @param {Object} config Configuration options
31984 Roo.form.ComboCheck = function(config){
31985 Roo.form.ComboCheck.superclass.constructor.call(this, config);
31986 // should verify some data...
31988 // hiddenName = required..
31989 // displayField = required
31990 // valudField == required
31991 var req= [ 'hiddenName', 'displayField', 'valueField' ];
31993 Roo.each(req, function(e) {
31994 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31995 throw "Roo.form.ComboCheck : missing value for: " + e;
32002 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
32007 selectedClass: 'x-menu-item-checked',
32010 onRender : function(ct, position){
32016 var cls = 'x-combo-list';
32019 this.tpl = new Roo.Template({
32020 html : '<div class="'+cls+'-item x-menu-check-item">' +
32021 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
32022 '<span>{' + this.displayField + '}</span>' +
32029 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
32030 this.view.singleSelect = false;
32031 this.view.multiSelect = true;
32032 this.view.toggleSelect = true;
32033 this.pageTb.add(new Roo.Toolbar.Fill(), {
32036 handler: function()
32043 onViewOver : function(e, t){
32049 onViewClick : function(doFocus,index){
32053 select: function () {
32054 //Roo.log("SELECT CALLED");
32057 selectByValue : function(xv, scrollIntoView){
32058 var ar = this.getValueArray();
32061 Roo.each(ar, function(v) {
32062 if(v === undefined || v === null){
32065 var r = this.findRecord(this.valueField, v);
32067 sels.push(this.store.indexOf(r))
32071 this.view.select(sels);
32077 onSelect : function(record, index){
32078 // Roo.log("onselect Called");
32079 // this is only called by the clear button now..
32080 this.view.clearSelections();
32081 this.setValue('[]');
32082 if (this.value != this.valueBefore) {
32083 this.fireEvent('change', this, this.value, this.valueBefore);
32084 this.valueBefore = this.value;
32087 getValueArray : function()
32092 //Roo.log(this.value);
32093 if (typeof(this.value) == 'undefined') {
32096 var ar = Roo.decode(this.value);
32097 return ar instanceof Array ? ar : []; //?? valid?
32100 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
32105 expand : function ()
32108 Roo.form.ComboCheck.superclass.expand.call(this);
32109 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
32110 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
32115 collapse : function(){
32116 Roo.form.ComboCheck.superclass.collapse.call(this);
32117 var sl = this.view.getSelectedIndexes();
32118 var st = this.store;
32122 Roo.each(sl, function(i) {
32124 nv.push(r.get(this.valueField));
32126 this.setValue(Roo.encode(nv));
32127 if (this.value != this.valueBefore) {
32129 this.fireEvent('change', this, this.value, this.valueBefore);
32130 this.valueBefore = this.value;
32135 setValue : function(v){
32139 var vals = this.getValueArray();
32141 Roo.each(vals, function(k) {
32142 var r = this.findRecord(this.valueField, k);
32144 tv.push(r.data[this.displayField]);
32145 }else if(this.valueNotFoundText !== undefined){
32146 tv.push( this.valueNotFoundText );
32151 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
32152 this.hiddenField.value = v;
32158 * Ext JS Library 1.1.1
32159 * Copyright(c) 2006-2007, Ext JS, LLC.
32161 * Originally Released Under LGPL - original licence link has changed is not relivant.
32164 * <script type="text/javascript">
32168 * @class Roo.form.Signature
32169 * @extends Roo.form.Field
32173 * @param {Object} config Configuration options
32176 Roo.form.Signature = function(config){
32177 Roo.form.Signature.superclass.constructor.call(this, config);
32179 this.addEvents({// not in used??
32182 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
32183 * @param {Roo.form.Signature} combo This combo box
32188 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
32189 * @param {Roo.form.ComboBox} combo This combo box
32190 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
32196 Roo.extend(Roo.form.Signature, Roo.form.Field, {
32198 * @cfg {Object} labels Label to use when rendering a form.
32202 * confirm : "Confirm"
32207 confirm : "Confirm"
32210 * @cfg {Number} width The signature panel width (defaults to 300)
32214 * @cfg {Number} height The signature panel height (defaults to 100)
32218 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32220 allowBlank : false,
32223 // {Object} signPanel The signature SVG panel element (defaults to {})
32225 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32226 isMouseDown : false,
32227 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32228 isConfirmed : false,
32229 // {String} signatureTmp SVG mapping string (defaults to empty string)
32233 defaultAutoCreate : { // modified by initCompnoent..
32239 onRender : function(ct, position){
32241 Roo.form.Signature.superclass.onRender.call(this, ct, position);
32243 this.wrap = this.el.wrap({
32244 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32247 this.createToolbar(this);
32248 this.signPanel = this.wrap.createChild({
32250 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32254 this.svgID = Roo.id();
32255 this.svgEl = this.signPanel.createChild({
32256 xmlns : 'http://www.w3.org/2000/svg',
32258 id : this.svgID + "-svg",
32260 height: this.height,
32261 viewBox: '0 0 '+this.width+' '+this.height,
32265 id: this.svgID + "-svg-r",
32267 height: this.height,
32272 id: this.svgID + "-svg-l",
32274 y1: (this.height*0.8), // start set the line in 80% of height
32275 x2: this.width, // end
32276 y2: (this.height*0.8), // end set the line in 80% of height
32278 'stroke-width': "1",
32279 'stroke-dasharray': "3",
32280 'shape-rendering': "crispEdges",
32281 'pointer-events': "none"
32285 id: this.svgID + "-svg-p",
32287 'stroke-width': "3",
32289 'pointer-events': 'none'
32294 this.svgBox = this.svgEl.dom.getScreenCTM();
32296 createSVG : function(){
32297 var svg = this.signPanel;
32298 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32301 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32302 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32303 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32304 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32305 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32306 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32307 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32310 isTouchEvent : function(e){
32311 return e.type.match(/^touch/);
32313 getCoords : function (e) {
32314 var pt = this.svgEl.dom.createSVGPoint();
32317 if (this.isTouchEvent(e)) {
32318 pt.x = e.targetTouches[0].clientX;
32319 pt.y = e.targetTouches[0].clientY;
32321 var a = this.svgEl.dom.getScreenCTM();
32322 var b = a.inverse();
32323 var mx = pt.matrixTransform(b);
32324 return mx.x + ',' + mx.y;
32326 //mouse event headler
32327 down : function (e) {
32328 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32329 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32331 this.isMouseDown = true;
32333 e.preventDefault();
32335 move : function (e) {
32336 if (this.isMouseDown) {
32337 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32338 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32341 e.preventDefault();
32343 up : function (e) {
32344 this.isMouseDown = false;
32345 var sp = this.signatureTmp.split(' ');
32348 if(!sp[sp.length-2].match(/^L/)){
32352 this.signatureTmp = sp.join(" ");
32355 if(this.getValue() != this.signatureTmp){
32356 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32357 this.isConfirmed = false;
32359 e.preventDefault();
32363 * Protected method that will not generally be called directly. It
32364 * is called when the editor creates its toolbar. Override this method if you need to
32365 * add custom toolbar buttons.
32366 * @param {HtmlEditor} editor
32368 createToolbar : function(editor){
32369 function btn(id, toggle, handler){
32370 var xid = fid + '-'+ id ;
32374 cls : 'x-btn-icon x-edit-'+id,
32375 enableToggle:toggle !== false,
32376 scope: editor, // was editor...
32377 handler:handler||editor.relayBtnCmd,
32378 clickEvent:'mousedown',
32379 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32385 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32389 cls : ' x-signature-btn x-signature-'+id,
32390 scope: editor, // was editor...
32391 handler: this.reset,
32392 clickEvent:'mousedown',
32393 text: this.labels.clear
32400 cls : ' x-signature-btn x-signature-'+id,
32401 scope: editor, // was editor...
32402 handler: this.confirmHandler,
32403 clickEvent:'mousedown',
32404 text: this.labels.confirm
32411 * when user is clicked confirm then show this image.....
32413 * @return {String} Image Data URI
32415 getImageDataURI : function(){
32416 var svg = this.svgEl.dom.parentNode.innerHTML;
32417 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32422 * @return {Boolean} this.isConfirmed
32424 getConfirmed : function(){
32425 return this.isConfirmed;
32429 * @return {Number} this.width
32431 getWidth : function(){
32436 * @return {Number} this.height
32438 getHeight : function(){
32439 return this.height;
32442 getSignature : function(){
32443 return this.signatureTmp;
32446 reset : function(){
32447 this.signatureTmp = '';
32448 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32449 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32450 this.isConfirmed = false;
32451 Roo.form.Signature.superclass.reset.call(this);
32453 setSignature : function(s){
32454 this.signatureTmp = s;
32455 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32456 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32458 this.isConfirmed = false;
32459 Roo.form.Signature.superclass.reset.call(this);
32462 // Roo.log(this.signPanel.dom.contentWindow.up())
32465 setConfirmed : function(){
32469 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32472 confirmHandler : function(){
32473 if(!this.getSignature()){
32477 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32478 this.setValue(this.getSignature());
32479 this.isConfirmed = true;
32481 this.fireEvent('confirm', this);
32484 // Subclasses should provide the validation implementation by overriding this
32485 validateValue : function(value){
32486 if(this.allowBlank){
32490 if(this.isConfirmed){
32497 * Ext JS Library 1.1.1
32498 * Copyright(c) 2006-2007, Ext JS, LLC.
32500 * Originally Released Under LGPL - original licence link has changed is not relivant.
32503 * <script type="text/javascript">
32508 * @class Roo.form.ComboBox
32509 * @extends Roo.form.TriggerField
32510 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32512 * Create a new ComboBox.
32513 * @param {Object} config Configuration options
32515 Roo.form.Select = function(config){
32516 Roo.form.Select.superclass.constructor.call(this, config);
32520 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32522 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32525 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32526 * rendering into an Roo.Editor, defaults to false)
32529 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32530 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32533 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32536 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32537 * the dropdown list (defaults to undefined, with no header element)
32541 * @cfg {String/Roo.Template} tpl The template to use to render the output
32545 defaultAutoCreate : {tag: "select" },
32547 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32549 listWidth: undefined,
32551 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32552 * mode = 'remote' or 'text' if mode = 'local')
32554 displayField: undefined,
32556 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32557 * mode = 'remote' or 'value' if mode = 'local').
32558 * Note: use of a valueField requires the user make a selection
32559 * in order for a value to be mapped.
32561 valueField: undefined,
32565 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32566 * field's data value (defaults to the underlying DOM element's name)
32568 hiddenName: undefined,
32570 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32574 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32576 selectedClass: 'x-combo-selected',
32578 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
32579 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32580 * which displays a downward arrow icon).
32582 triggerClass : 'x-form-arrow-trigger',
32584 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32588 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32589 * anchor positions (defaults to 'tl-bl')
32591 listAlign: 'tl-bl?',
32593 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32597 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
32598 * query specified by the allQuery config option (defaults to 'query')
32600 triggerAction: 'query',
32602 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32603 * (defaults to 4, does not apply if editable = false)
32607 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32608 * delay (typeAheadDelay) if it matches a known value (defaults to false)
32612 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32613 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32617 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32618 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
32622 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
32623 * when editable = true (defaults to false)
32625 selectOnFocus:false,
32627 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32629 queryParam: 'query',
32631 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
32632 * when mode = 'remote' (defaults to 'Loading...')
32634 loadingText: 'Loading...',
32636 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32640 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32644 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32645 * traditional select (defaults to true)
32649 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32653 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32657 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32658 * listWidth has a higher value)
32662 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32663 * allow the user to set arbitrary text into the field (defaults to false)
32665 forceSelection:false,
32667 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32668 * if typeAhead = true (defaults to 250)
32670 typeAheadDelay : 250,
32672 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32673 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32675 valueNotFoundText : undefined,
32678 * @cfg {String} defaultValue The value displayed after loading the store.
32683 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32685 blockFocus : false,
32688 * @cfg {Boolean} disableClear Disable showing of clear button.
32690 disableClear : false,
32692 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
32694 alwaysQuery : false,
32700 // element that contains real text value.. (when hidden is used..)
32703 onRender : function(ct, position){
32704 Roo.form.Field.prototype.onRender.call(this, ct, position);
32707 this.store.on('beforeload', this.onBeforeLoad, this);
32708 this.store.on('load', this.onLoad, this);
32709 this.store.on('loadexception', this.onLoadException, this);
32710 this.store.load({});
32718 initEvents : function(){
32719 //Roo.form.ComboBox.superclass.initEvents.call(this);
32723 onDestroy : function(){
32726 this.store.un('beforeload', this.onBeforeLoad, this);
32727 this.store.un('load', this.onLoad, this);
32728 this.store.un('loadexception', this.onLoadException, this);
32730 //Roo.form.ComboBox.superclass.onDestroy.call(this);
32734 fireKey : function(e){
32735 if(e.isNavKeyPress() && !this.list.isVisible()){
32736 this.fireEvent("specialkey", this, e);
32741 onResize: function(w, h){
32749 * Allow or prevent the user from directly editing the field text. If false is passed,
32750 * the user will only be able to select from the items defined in the dropdown list. This method
32751 * is the runtime equivalent of setting the 'editable' config option at config time.
32752 * @param {Boolean} value True to allow the user to directly edit the field text
32754 setEditable : function(value){
32759 onBeforeLoad : function(){
32761 Roo.log("Select before load");
32764 this.innerList.update(this.loadingText ?
32765 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32766 //this.restrictHeight();
32767 this.selectedIndex = -1;
32771 onLoad : function(){
32774 var dom = this.el.dom;
32775 dom.innerHTML = '';
32776 var od = dom.ownerDocument;
32778 if (this.emptyText) {
32779 var op = od.createElement('option');
32780 op.setAttribute('value', '');
32781 op.innerHTML = String.format('{0}', this.emptyText);
32782 dom.appendChild(op);
32784 if(this.store.getCount() > 0){
32786 var vf = this.valueField;
32787 var df = this.displayField;
32788 this.store.data.each(function(r) {
32789 // which colmsn to use... testing - cdoe / title..
32790 var op = od.createElement('option');
32791 op.setAttribute('value', r.data[vf]);
32792 op.innerHTML = String.format('{0}', r.data[df]);
32793 dom.appendChild(op);
32795 if (typeof(this.defaultValue != 'undefined')) {
32796 this.setValue(this.defaultValue);
32801 //this.onEmptyResults();
32806 onLoadException : function()
32808 dom.innerHTML = '';
32810 Roo.log("Select on load exception");
32814 Roo.log(this.store.reader.jsonData);
32815 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32816 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32822 onTypeAhead : function(){
32827 onSelect : function(record, index){
32828 Roo.log('on select?');
32830 if(this.fireEvent('beforeselect', this, record, index) !== false){
32831 this.setFromData(index > -1 ? record.data : false);
32833 this.fireEvent('select', this, record, index);
32838 * Returns the currently selected field value or empty string if no value is set.
32839 * @return {String} value The selected value
32841 getValue : function(){
32842 var dom = this.el.dom;
32843 this.value = dom.options[dom.selectedIndex].value;
32849 * Clears any text/value currently set in the field
32851 clearValue : function(){
32853 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32858 * Sets the specified value into the field. If the value finds a match, the corresponding record text
32859 * will be displayed in the field. If the value does not match the data value of an existing item,
32860 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32861 * Otherwise the field will be blank (although the value will still be set).
32862 * @param {String} value The value to match
32864 setValue : function(v){
32865 var d = this.el.dom;
32866 for (var i =0; i < d.options.length;i++) {
32867 if (v == d.options[i].value) {
32868 d.selectedIndex = i;
32876 * @property {Object} the last set data for the element
32881 * Sets the value of the field based on a object which is related to the record format for the store.
32882 * @param {Object} value the value to set as. or false on reset?
32884 setFromData : function(o){
32885 Roo.log('setfrom data?');
32891 reset : function(){
32895 findRecord : function(prop, value){
32900 if(this.store.getCount() > 0){
32901 this.store.each(function(r){
32902 if(r.data[prop] == value){
32912 getName: function()
32914 // returns hidden if it's set..
32915 if (!this.rendered) {return ''};
32916 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
32924 onEmptyResults : function(){
32925 Roo.log('empty results');
32930 * Returns true if the dropdown list is expanded, else false.
32932 isExpanded : function(){
32937 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32938 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32939 * @param {String} value The data value of the item to select
32940 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32941 * selected item if it is not currently in view (defaults to true)
32942 * @return {Boolean} True if the value matched an item in the list, else false
32944 selectByValue : function(v, scrollIntoView){
32945 Roo.log('select By Value');
32948 if(v !== undefined && v !== null){
32949 var r = this.findRecord(this.valueField || this.displayField, v);
32951 this.select(this.store.indexOf(r), scrollIntoView);
32959 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32960 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32961 * @param {Number} index The zero-based index of the list item to select
32962 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32963 * selected item if it is not currently in view (defaults to true)
32965 select : function(index, scrollIntoView){
32966 Roo.log('select ');
32969 this.selectedIndex = index;
32970 this.view.select(index);
32971 if(scrollIntoView !== false){
32972 var el = this.view.getNode(index);
32974 this.innerList.scrollChildIntoView(el, false);
32982 validateBlur : function(){
32989 initQuery : function(){
32990 this.doQuery(this.getRawValue());
32994 doForce : function(){
32995 if(this.el.dom.value.length > 0){
32996 this.el.dom.value =
32997 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
33003 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
33004 * query allowing the query action to be canceled if needed.
33005 * @param {String} query The SQL query to execute
33006 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
33007 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
33008 * saved in the current store (defaults to false)
33010 doQuery : function(q, forceAll){
33012 Roo.log('doQuery?');
33013 if(q === undefined || q === null){
33018 forceAll: forceAll,
33022 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
33026 forceAll = qe.forceAll;
33027 if(forceAll === true || (q.length >= this.minChars)){
33028 if(this.lastQuery != q || this.alwaysQuery){
33029 this.lastQuery = q;
33030 if(this.mode == 'local'){
33031 this.selectedIndex = -1;
33033 this.store.clearFilter();
33035 this.store.filter(this.displayField, q);
33039 this.store.baseParams[this.queryParam] = q;
33041 params: this.getParams(q)
33046 this.selectedIndex = -1;
33053 getParams : function(q){
33055 //p[this.queryParam] = q;
33058 p.limit = this.pageSize;
33064 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
33066 collapse : function(){
33071 collapseIf : function(e){
33076 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
33078 expand : function(){
33086 * @cfg {Boolean} grow
33090 * @cfg {Number} growMin
33094 * @cfg {Number} growMax
33102 setWidth : function()
33106 getResizeEl : function(){
33109 });//<script type="text/javasscript">
33113 * @class Roo.DDView
33114 * A DnD enabled version of Roo.View.
33115 * @param {Element/String} container The Element in which to create the View.
33116 * @param {String} tpl The template string used to create the markup for each element of the View
33117 * @param {Object} config The configuration properties. These include all the config options of
33118 * {@link Roo.View} plus some specific to this class.<br>
33120 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
33121 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
33123 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
33124 .x-view-drag-insert-above {
33125 border-top:1px dotted #3366cc;
33127 .x-view-drag-insert-below {
33128 border-bottom:1px dotted #3366cc;
33134 Roo.DDView = function(container, tpl, config) {
33135 Roo.DDView.superclass.constructor.apply(this, arguments);
33136 this.getEl().setStyle("outline", "0px none");
33137 this.getEl().unselectable();
33138 if (this.dragGroup) {
33139 this.setDraggable(this.dragGroup.split(","));
33141 if (this.dropGroup) {
33142 this.setDroppable(this.dropGroup.split(","));
33144 if (this.deletable) {
33145 this.setDeletable();
33147 this.isDirtyFlag = false;
33153 Roo.extend(Roo.DDView, Roo.View, {
33154 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
33155 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
33156 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
33157 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
33161 reset: Roo.emptyFn,
33163 clearInvalid: Roo.form.Field.prototype.clearInvalid,
33165 validate: function() {
33169 destroy: function() {
33170 this.purgeListeners();
33171 this.getEl.removeAllListeners();
33172 this.getEl().remove();
33173 if (this.dragZone) {
33174 if (this.dragZone.destroy) {
33175 this.dragZone.destroy();
33178 if (this.dropZone) {
33179 if (this.dropZone.destroy) {
33180 this.dropZone.destroy();
33185 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
33186 getName: function() {
33190 /** Loads the View from a JSON string representing the Records to put into the Store. */
33191 setValue: function(v) {
33193 throw "DDView.setValue(). DDView must be constructed with a valid Store";
33196 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33197 this.store.proxy = new Roo.data.MemoryProxy(data);
33201 /** @return {String} a parenthesised list of the ids of the Records in the View. */
33202 getValue: function() {
33204 this.store.each(function(rec) {
33205 result += rec.id + ',';
33207 return result.substr(0, result.length - 1) + ')';
33210 getIds: function() {
33211 var i = 0, result = new Array(this.store.getCount());
33212 this.store.each(function(rec) {
33213 result[i++] = rec.id;
33218 isDirty: function() {
33219 return this.isDirtyFlag;
33223 * Part of the Roo.dd.DropZone interface. If no target node is found, the
33224 * whole Element becomes the target, and this causes the drop gesture to append.
33226 getTargetFromEvent : function(e) {
33227 var target = e.getTarget();
33228 while ((target !== null) && (target.parentNode != this.el.dom)) {
33229 target = target.parentNode;
33232 target = this.el.dom.lastChild || this.el.dom;
33238 * Create the drag data which consists of an object which has the property "ddel" as
33239 * the drag proxy element.
33241 getDragData : function(e) {
33242 var target = this.findItemFromChild(e.getTarget());
33244 this.handleSelection(e);
33245 var selNodes = this.getSelectedNodes();
33248 copy: this.copy || (this.allowCopy && e.ctrlKey),
33252 var selectedIndices = this.getSelectedIndexes();
33253 for (var i = 0; i < selectedIndices.length; i++) {
33254 dragData.records.push(this.store.getAt(selectedIndices[i]));
33256 if (selNodes.length == 1) {
33257 dragData.ddel = target.cloneNode(true); // the div element
33259 var div = document.createElement('div'); // create the multi element drag "ghost"
33260 div.className = 'multi-proxy';
33261 for (var i = 0, len = selNodes.length; i < len; i++) {
33262 div.appendChild(selNodes[i].cloneNode(true));
33264 dragData.ddel = div;
33266 //console.log(dragData)
33267 //console.log(dragData.ddel.innerHTML)
33270 //console.log('nodragData')
33274 /** Specify to which ddGroup items in this DDView may be dragged. */
33275 setDraggable: function(ddGroup) {
33276 if (ddGroup instanceof Array) {
33277 Roo.each(ddGroup, this.setDraggable, this);
33280 if (this.dragZone) {
33281 this.dragZone.addToGroup(ddGroup);
33283 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33284 containerScroll: true,
33288 // Draggability implies selection. DragZone's mousedown selects the element.
33289 if (!this.multiSelect) { this.singleSelect = true; }
33291 // Wire the DragZone's handlers up to methods in *this*
33292 this.dragZone.getDragData = this.getDragData.createDelegate(this);
33296 /** Specify from which ddGroup this DDView accepts drops. */
33297 setDroppable: function(ddGroup) {
33298 if (ddGroup instanceof Array) {
33299 Roo.each(ddGroup, this.setDroppable, this);
33302 if (this.dropZone) {
33303 this.dropZone.addToGroup(ddGroup);
33305 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33306 containerScroll: true,
33310 // Wire the DropZone's handlers up to methods in *this*
33311 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33312 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33313 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33314 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33315 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33319 /** Decide whether to drop above or below a View node. */
33320 getDropPoint : function(e, n, dd){
33321 if (n == this.el.dom) { return "above"; }
33322 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33323 var c = t + (b - t) / 2;
33324 var y = Roo.lib.Event.getPageY(e);
33332 onNodeEnter : function(n, dd, e, data){
33336 onNodeOver : function(n, dd, e, data){
33337 var pt = this.getDropPoint(e, n, dd);
33338 // set the insert point style on the target node
33339 var dragElClass = this.dropNotAllowed;
33342 if (pt == "above"){
33343 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33344 targetElClass = "x-view-drag-insert-above";
33346 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33347 targetElClass = "x-view-drag-insert-below";
33349 if (this.lastInsertClass != targetElClass){
33350 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33351 this.lastInsertClass = targetElClass;
33354 return dragElClass;
33357 onNodeOut : function(n, dd, e, data){
33358 this.removeDropIndicators(n);
33361 onNodeDrop : function(n, dd, e, data){
33362 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33365 var pt = this.getDropPoint(e, n, dd);
33366 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33367 if (pt == "below") { insertAt++; }
33368 for (var i = 0; i < data.records.length; i++) {
33369 var r = data.records[i];
33370 var dup = this.store.getById(r.id);
33371 if (dup && (dd != this.dragZone)) {
33372 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33375 this.store.insert(insertAt++, r.copy());
33377 data.source.isDirtyFlag = true;
33379 this.store.insert(insertAt++, r);
33381 this.isDirtyFlag = true;
33384 this.dragZone.cachedTarget = null;
33388 removeDropIndicators : function(n){
33390 Roo.fly(n).removeClass([
33391 "x-view-drag-insert-above",
33392 "x-view-drag-insert-below"]);
33393 this.lastInsertClass = "_noclass";
33398 * Utility method. Add a delete option to the DDView's context menu.
33399 * @param {String} imageUrl The URL of the "delete" icon image.
33401 setDeletable: function(imageUrl) {
33402 if (!this.singleSelect && !this.multiSelect) {
33403 this.singleSelect = true;
33405 var c = this.getContextMenu();
33406 this.contextMenu.on("itemclick", function(item) {
33409 this.remove(this.getSelectedIndexes());
33413 this.contextMenu.add({
33420 /** Return the context menu for this DDView. */
33421 getContextMenu: function() {
33422 if (!this.contextMenu) {
33423 // Create the View's context menu
33424 this.contextMenu = new Roo.menu.Menu({
33425 id: this.id + "-contextmenu"
33427 this.el.on("contextmenu", this.showContextMenu, this);
33429 return this.contextMenu;
33432 disableContextMenu: function() {
33433 if (this.contextMenu) {
33434 this.el.un("contextmenu", this.showContextMenu, this);
33438 showContextMenu: function(e, item) {
33439 item = this.findItemFromChild(e.getTarget());
33442 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33443 this.contextMenu.showAt(e.getXY());
33448 * Remove {@link Roo.data.Record}s at the specified indices.
33449 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33451 remove: function(selectedIndices) {
33452 selectedIndices = [].concat(selectedIndices);
33453 for (var i = 0; i < selectedIndices.length; i++) {
33454 var rec = this.store.getAt(selectedIndices[i]);
33455 this.store.remove(rec);
33460 * Double click fires the event, but also, if this is draggable, and there is only one other
33461 * related DropZone, it transfers the selected node.
33463 onDblClick : function(e){
33464 var item = this.findItemFromChild(e.getTarget());
33466 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33469 if (this.dragGroup) {
33470 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33471 while (targets.indexOf(this.dropZone) > -1) {
33472 targets.remove(this.dropZone);
33474 if (targets.length == 1) {
33475 this.dragZone.cachedTarget = null;
33476 var el = Roo.get(targets[0].getEl());
33477 var box = el.getBox(true);
33478 targets[0].onNodeDrop(el.dom, {
33480 xy: [box.x, box.y + box.height - 1]
33481 }, null, this.getDragData(e));
33487 handleSelection: function(e) {
33488 this.dragZone.cachedTarget = null;
33489 var item = this.findItemFromChild(e.getTarget());
33491 this.clearSelections(true);
33494 if (item && (this.multiSelect || this.singleSelect)){
33495 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33496 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33497 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33498 this.unselect(item);
33500 this.select(item, this.multiSelect && e.ctrlKey);
33501 this.lastSelection = item;
33506 onItemClick : function(item, index, e){
33507 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33513 unselect : function(nodeInfo, suppressEvent){
33514 var node = this.getNode(nodeInfo);
33515 if(node && this.isSelected(node)){
33516 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33517 Roo.fly(node).removeClass(this.selectedClass);
33518 this.selections.remove(node);
33519 if(!suppressEvent){
33520 this.fireEvent("selectionchange", this, this.selections);
33528 * Ext JS Library 1.1.1
33529 * Copyright(c) 2006-2007, Ext JS, LLC.
33531 * Originally Released Under LGPL - original licence link has changed is not relivant.
33534 * <script type="text/javascript">
33538 * @class Roo.LayoutManager
33539 * @extends Roo.util.Observable
33540 * Base class for layout managers.
33542 Roo.LayoutManager = function(container, config){
33543 Roo.LayoutManager.superclass.constructor.call(this);
33544 this.el = Roo.get(container);
33545 // ie scrollbar fix
33546 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33547 document.body.scroll = "no";
33548 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33549 this.el.position('relative');
33551 this.id = this.el.id;
33552 this.el.addClass("x-layout-container");
33553 /** false to disable window resize monitoring @type Boolean */
33554 this.monitorWindowResize = true;
33559 * Fires when a layout is performed.
33560 * @param {Roo.LayoutManager} this
33564 * @event regionresized
33565 * Fires when the user resizes a region.
33566 * @param {Roo.LayoutRegion} region The resized region
33567 * @param {Number} newSize The new size (width for east/west, height for north/south)
33569 "regionresized" : true,
33571 * @event regioncollapsed
33572 * Fires when a region is collapsed.
33573 * @param {Roo.LayoutRegion} region The collapsed region
33575 "regioncollapsed" : true,
33577 * @event regionexpanded
33578 * Fires when a region is expanded.
33579 * @param {Roo.LayoutRegion} region The expanded region
33581 "regionexpanded" : true
33583 this.updating = false;
33584 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33587 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33589 * Returns true if this layout is currently being updated
33590 * @return {Boolean}
33592 isUpdating : function(){
33593 return this.updating;
33597 * Suspend the LayoutManager from doing auto-layouts while
33598 * making multiple add or remove calls
33600 beginUpdate : function(){
33601 this.updating = true;
33605 * Restore auto-layouts and optionally disable the manager from performing a layout
33606 * @param {Boolean} noLayout true to disable a layout update
33608 endUpdate : function(noLayout){
33609 this.updating = false;
33615 layout: function(){
33619 onRegionResized : function(region, newSize){
33620 this.fireEvent("regionresized", region, newSize);
33624 onRegionCollapsed : function(region){
33625 this.fireEvent("regioncollapsed", region);
33628 onRegionExpanded : function(region){
33629 this.fireEvent("regionexpanded", region);
33633 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33634 * performs box-model adjustments.
33635 * @return {Object} The size as an object {width: (the width), height: (the height)}
33637 getViewSize : function(){
33639 if(this.el.dom != document.body){
33640 size = this.el.getSize();
33642 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33644 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33645 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33650 * Returns the Element this layout is bound to.
33651 * @return {Roo.Element}
33653 getEl : function(){
33658 * Returns the specified region.
33659 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33660 * @return {Roo.LayoutRegion}
33662 getRegion : function(target){
33663 return this.regions[target.toLowerCase()];
33666 onWindowResize : function(){
33667 if(this.monitorWindowResize){
33673 * Ext JS Library 1.1.1
33674 * Copyright(c) 2006-2007, Ext JS, LLC.
33676 * Originally Released Under LGPL - original licence link has changed is not relivant.
33679 * <script type="text/javascript">
33682 * @class Roo.BorderLayout
33683 * @extends Roo.LayoutManager
33684 * @children Roo.ContentPanel
33685 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33686 * please see: <br><br>
33687 * <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>
33688 * <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>
33691 var layout = new Roo.BorderLayout(document.body, {
33725 preferredTabWidth: 150
33730 var CP = Roo.ContentPanel;
33732 layout.beginUpdate();
33733 layout.add("north", new CP("north", "North"));
33734 layout.add("south", new CP("south", {title: "South", closable: true}));
33735 layout.add("west", new CP("west", {title: "West"}));
33736 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33737 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33738 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33739 layout.getRegion("center").showPanel("center1");
33740 layout.endUpdate();
33743 <b>The container the layout is rendered into can be either the body element or any other element.
33744 If it is not the body element, the container needs to either be an absolute positioned element,
33745 or you will need to add "position:relative" to the css of the container. You will also need to specify
33746 the container size if it is not the body element.</b>
33749 * Create a new BorderLayout
33750 * @param {String/HTMLElement/Element} container The container this layout is bound to
33751 * @param {Object} config Configuration options
33753 Roo.BorderLayout = function(container, config){
33754 config = config || {};
33755 Roo.BorderLayout.superclass.constructor.call(this, container, config);
33756 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33757 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33758 var target = this.factory.validRegions[i];
33759 if(config[target]){
33760 this.addRegion(target, config[target]);
33765 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33768 * @cfg {Roo.LayoutRegion} east
33771 * @cfg {Roo.LayoutRegion} west
33774 * @cfg {Roo.LayoutRegion} north
33777 * @cfg {Roo.LayoutRegion} south
33780 * @cfg {Roo.LayoutRegion} center
33783 * Creates and adds a new region if it doesn't already exist.
33784 * @param {String} target The target region key (north, south, east, west or center).
33785 * @param {Object} config The regions config object
33786 * @return {BorderLayoutRegion} The new region
33788 addRegion : function(target, config){
33789 if(!this.regions[target]){
33790 var r = this.factory.create(target, this, config);
33791 this.bindRegion(target, r);
33793 return this.regions[target];
33797 bindRegion : function(name, r){
33798 this.regions[name] = r;
33799 r.on("visibilitychange", this.layout, this);
33800 r.on("paneladded", this.layout, this);
33801 r.on("panelremoved", this.layout, this);
33802 r.on("invalidated", this.layout, this);
33803 r.on("resized", this.onRegionResized, this);
33804 r.on("collapsed", this.onRegionCollapsed, this);
33805 r.on("expanded", this.onRegionExpanded, this);
33809 * Performs a layout update.
33811 layout : function(){
33812 if(this.updating) {
33815 var size = this.getViewSize();
33816 var w = size.width;
33817 var h = size.height;
33822 //var x = 0, y = 0;
33824 var rs = this.regions;
33825 var north = rs["north"];
33826 var south = rs["south"];
33827 var west = rs["west"];
33828 var east = rs["east"];
33829 var center = rs["center"];
33830 //if(this.hideOnLayout){ // not supported anymore
33831 //c.el.setStyle("display", "none");
33833 if(north && north.isVisible()){
33834 var b = north.getBox();
33835 var m = north.getMargins();
33836 b.width = w - (m.left+m.right);
33839 centerY = b.height + b.y + m.bottom;
33840 centerH -= centerY;
33841 north.updateBox(this.safeBox(b));
33843 if(south && south.isVisible()){
33844 var b = south.getBox();
33845 var m = south.getMargins();
33846 b.width = w - (m.left+m.right);
33848 var totalHeight = (b.height + m.top + m.bottom);
33849 b.y = h - totalHeight + m.top;
33850 centerH -= totalHeight;
33851 south.updateBox(this.safeBox(b));
33853 if(west && west.isVisible()){
33854 var b = west.getBox();
33855 var m = west.getMargins();
33856 b.height = centerH - (m.top+m.bottom);
33858 b.y = centerY + m.top;
33859 var totalWidth = (b.width + m.left + m.right);
33860 centerX += totalWidth;
33861 centerW -= totalWidth;
33862 west.updateBox(this.safeBox(b));
33864 if(east && east.isVisible()){
33865 var b = east.getBox();
33866 var m = east.getMargins();
33867 b.height = centerH - (m.top+m.bottom);
33868 var totalWidth = (b.width + m.left + m.right);
33869 b.x = w - totalWidth + m.left;
33870 b.y = centerY + m.top;
33871 centerW -= totalWidth;
33872 east.updateBox(this.safeBox(b));
33875 var m = center.getMargins();
33877 x: centerX + m.left,
33878 y: centerY + m.top,
33879 width: centerW - (m.left+m.right),
33880 height: centerH - (m.top+m.bottom)
33882 //if(this.hideOnLayout){
33883 //center.el.setStyle("display", "block");
33885 center.updateBox(this.safeBox(centerBox));
33888 this.fireEvent("layout", this);
33892 safeBox : function(box){
33893 box.width = Math.max(0, box.width);
33894 box.height = Math.max(0, box.height);
33899 * Adds a ContentPanel (or subclass) to this layout.
33900 * @param {String} target The target region key (north, south, east, west or center).
33901 * @param {Roo.ContentPanel} panel The panel to add
33902 * @return {Roo.ContentPanel} The added panel
33904 add : function(target, panel){
33906 target = target.toLowerCase();
33907 return this.regions[target].add(panel);
33911 * Remove a ContentPanel (or subclass) to this layout.
33912 * @param {String} target The target region key (north, south, east, west or center).
33913 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33914 * @return {Roo.ContentPanel} The removed panel
33916 remove : function(target, panel){
33917 target = target.toLowerCase();
33918 return this.regions[target].remove(panel);
33922 * Searches all regions for a panel with the specified id
33923 * @param {String} panelId
33924 * @return {Roo.ContentPanel} The panel or null if it wasn't found
33926 findPanel : function(panelId){
33927 var rs = this.regions;
33928 for(var target in rs){
33929 if(typeof rs[target] != "function"){
33930 var p = rs[target].getPanel(panelId);
33940 * Searches all regions for a panel with the specified id and activates (shows) it.
33941 * @param {String/ContentPanel} panelId The panels id or the panel itself
33942 * @return {Roo.ContentPanel} The shown panel or null
33944 showPanel : function(panelId) {
33945 var rs = this.regions;
33946 for(var target in rs){
33947 var r = rs[target];
33948 if(typeof r != "function"){
33949 if(r.hasPanel(panelId)){
33950 return r.showPanel(panelId);
33958 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33959 * @param {Roo.state.Provider} provider (optional) An alternate state provider
33961 restoreState : function(provider){
33963 provider = Roo.state.Manager;
33965 var sm = new Roo.LayoutStateManager();
33966 sm.init(this, provider);
33970 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
33971 * object should contain properties for each region to add ContentPanels to, and each property's value should be
33972 * a valid ContentPanel config object. Example:
33974 // Create the main layout
33975 var layout = new Roo.BorderLayout('main-ct', {
33986 // Create and add multiple ContentPanels at once via configs
33989 id: 'source-files',
33991 title:'Ext Source Files',
34004 * @param {Object} regions An object containing ContentPanel configs by region name
34006 batchAdd : function(regions){
34007 this.beginUpdate();
34008 for(var rname in regions){
34009 var lr = this.regions[rname];
34011 this.addTypedPanels(lr, regions[rname]);
34018 addTypedPanels : function(lr, ps){
34019 if(typeof ps == 'string'){
34020 lr.add(new Roo.ContentPanel(ps));
34022 else if(ps instanceof Array){
34023 for(var i =0, len = ps.length; i < len; i++){
34024 this.addTypedPanels(lr, ps[i]);
34027 else if(!ps.events){ // raw config?
34029 delete ps.el; // prevent conflict
34030 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
34032 else { // panel object assumed!
34037 * Adds a xtype elements to the layout.
34041 xtype : 'ContentPanel',
34048 xtype : 'NestedLayoutPanel',
34054 items : [ ... list of content panels or nested layout panels.. ]
34058 * @param {Object} cfg Xtype definition of item to add.
34060 addxtype : function(cfg)
34062 // basically accepts a pannel...
34063 // can accept a layout region..!?!?
34064 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34066 if (!cfg.xtype.match(/Panel$/)) {
34071 if (typeof(cfg.region) == 'undefined') {
34072 Roo.log("Failed to add Panel, region was not set");
34076 var region = cfg.region;
34082 xitems = cfg.items;
34089 case 'ContentPanel': // ContentPanel (el, cfg)
34090 case 'ScrollPanel': // ContentPanel (el, cfg)
34092 if(cfg.autoCreate) {
34093 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34095 var el = this.el.createChild();
34096 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34099 this.add(region, ret);
34103 case 'TreePanel': // our new panel!
34104 cfg.el = this.el.createChild();
34105 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34106 this.add(region, ret);
34109 case 'NestedLayoutPanel':
34110 // create a new Layout (which is a Border Layout...
34111 var el = this.el.createChild();
34112 var clayout = cfg.layout;
34114 clayout.items = clayout.items || [];
34115 // replace this exitems with the clayout ones..
34116 xitems = clayout.items;
34119 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34120 cfg.background = false;
34122 var layout = new Roo.BorderLayout(el, clayout);
34124 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
34125 //console.log('adding nested layout panel ' + cfg.toSource());
34126 this.add(region, ret);
34127 nb = {}; /// find first...
34132 // needs grid and region
34134 //var el = this.getRegion(region).el.createChild();
34135 var el = this.el.createChild();
34136 // create the grid first...
34138 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
34140 if (region == 'center' && this.active ) {
34141 cfg.background = false;
34143 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
34145 this.add(region, ret);
34146 if (cfg.background) {
34147 ret.on('activate', function(gp) {
34148 if (!gp.grid.rendered) {
34163 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34165 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34166 this.add(region, ret);
34169 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
34173 // GridPanel (grid, cfg)
34176 this.beginUpdate();
34180 Roo.each(xitems, function(i) {
34181 region = nb && i.region ? i.region : false;
34183 var add = ret.addxtype(i);
34186 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34187 if (!i.background) {
34188 abn[region] = nb[region] ;
34195 // make the last non-background panel active..
34196 //if (nb) { Roo.log(abn); }
34199 for(var r in abn) {
34200 region = this.getRegion(r);
34202 // tried using nb[r], but it does not work..
34204 region.showPanel(abn[r]);
34215 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34216 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
34217 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34218 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
34221 var CP = Roo.ContentPanel;
34223 var layout = Roo.BorderLayout.create({
34227 panels: [new CP("north", "North")]
34236 panels: [new CP("west", {title: "West"})]
34245 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34254 panels: [new CP("south", {title: "South", closable: true})]
34261 preferredTabWidth: 150,
34263 new CP("center1", {title: "Close Me", closable: true}),
34264 new CP("center2", {title: "Center Panel", closable: false})
34269 layout.getRegion("center").showPanel("center1");
34274 Roo.BorderLayout.create = function(config, targetEl){
34275 var layout = new Roo.BorderLayout(targetEl || document.body, config);
34276 layout.beginUpdate();
34277 var regions = Roo.BorderLayout.RegionFactory.validRegions;
34278 for(var j = 0, jlen = regions.length; j < jlen; j++){
34279 var lr = regions[j];
34280 if(layout.regions[lr] && config[lr].panels){
34281 var r = layout.regions[lr];
34282 var ps = config[lr].panels;
34283 layout.addTypedPanels(r, ps);
34286 layout.endUpdate();
34291 Roo.BorderLayout.RegionFactory = {
34293 validRegions : ["north","south","east","west","center"],
34296 create : function(target, mgr, config){
34297 target = target.toLowerCase();
34298 if(config.lightweight || config.basic){
34299 return new Roo.BasicLayoutRegion(mgr, config, target);
34303 return new Roo.NorthLayoutRegion(mgr, config);
34305 return new Roo.SouthLayoutRegion(mgr, config);
34307 return new Roo.EastLayoutRegion(mgr, config);
34309 return new Roo.WestLayoutRegion(mgr, config);
34311 return new Roo.CenterLayoutRegion(mgr, config);
34313 throw 'Layout region "'+target+'" not supported.';
34317 * Ext JS Library 1.1.1
34318 * Copyright(c) 2006-2007, Ext JS, LLC.
34320 * Originally Released Under LGPL - original licence link has changed is not relivant.
34323 * <script type="text/javascript">
34327 * @class Roo.BasicLayoutRegion
34328 * @extends Roo.util.Observable
34329 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34330 * and does not have a titlebar, tabs or any other features. All it does is size and position
34331 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34333 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34335 this.position = pos;
34338 * @scope Roo.BasicLayoutRegion
34342 * @event beforeremove
34343 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34344 * @param {Roo.LayoutRegion} this
34345 * @param {Roo.ContentPanel} panel The panel
34346 * @param {Object} e The cancel event object
34348 "beforeremove" : true,
34350 * @event invalidated
34351 * Fires when the layout for this region is changed.
34352 * @param {Roo.LayoutRegion} this
34354 "invalidated" : true,
34356 * @event visibilitychange
34357 * Fires when this region is shown or hidden
34358 * @param {Roo.LayoutRegion} this
34359 * @param {Boolean} visibility true or false
34361 "visibilitychange" : true,
34363 * @event paneladded
34364 * Fires when a panel is added.
34365 * @param {Roo.LayoutRegion} this
34366 * @param {Roo.ContentPanel} panel The panel
34368 "paneladded" : true,
34370 * @event panelremoved
34371 * Fires when a panel is removed.
34372 * @param {Roo.LayoutRegion} this
34373 * @param {Roo.ContentPanel} panel The panel
34375 "panelremoved" : true,
34377 * @event beforecollapse
34378 * Fires when this region before collapse.
34379 * @param {Roo.LayoutRegion} this
34381 "beforecollapse" : true,
34384 * Fires when this region is collapsed.
34385 * @param {Roo.LayoutRegion} this
34387 "collapsed" : true,
34390 * Fires when this region is expanded.
34391 * @param {Roo.LayoutRegion} this
34396 * Fires when this region is slid into view.
34397 * @param {Roo.LayoutRegion} this
34399 "slideshow" : true,
34402 * Fires when this region slides out of view.
34403 * @param {Roo.LayoutRegion} this
34405 "slidehide" : true,
34407 * @event panelactivated
34408 * Fires when a panel is activated.
34409 * @param {Roo.LayoutRegion} this
34410 * @param {Roo.ContentPanel} panel The activated panel
34412 "panelactivated" : true,
34415 * Fires when the user resizes this region.
34416 * @param {Roo.LayoutRegion} this
34417 * @param {Number} newSize The new size (width for east/west, height for north/south)
34421 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34422 this.panels = new Roo.util.MixedCollection();
34423 this.panels.getKey = this.getPanelId.createDelegate(this);
34425 this.activePanel = null;
34426 // ensure listeners are added...
34428 if (config.listeners || config.events) {
34429 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34430 listeners : config.listeners || {},
34431 events : config.events || {}
34435 if(skipConfig !== true){
34436 this.applyConfig(config);
34440 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34441 getPanelId : function(p){
34445 applyConfig : function(config){
34446 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34447 this.config = config;
34452 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34453 * the width, for horizontal (north, south) the height.
34454 * @param {Number} newSize The new width or height
34456 resizeTo : function(newSize){
34457 var el = this.el ? this.el :
34458 (this.activePanel ? this.activePanel.getEl() : null);
34460 switch(this.position){
34463 el.setWidth(newSize);
34464 this.fireEvent("resized", this, newSize);
34468 el.setHeight(newSize);
34469 this.fireEvent("resized", this, newSize);
34475 getBox : function(){
34476 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34479 getMargins : function(){
34480 return this.margins;
34483 updateBox : function(box){
34485 var el = this.activePanel.getEl();
34486 el.dom.style.left = box.x + "px";
34487 el.dom.style.top = box.y + "px";
34488 this.activePanel.setSize(box.width, box.height);
34492 * Returns the container element for this region.
34493 * @return {Roo.Element}
34495 getEl : function(){
34496 return this.activePanel;
34500 * Returns true if this region is currently visible.
34501 * @return {Boolean}
34503 isVisible : function(){
34504 return this.activePanel ? true : false;
34507 setActivePanel : function(panel){
34508 panel = this.getPanel(panel);
34509 if(this.activePanel && this.activePanel != panel){
34510 this.activePanel.setActiveState(false);
34511 this.activePanel.getEl().setLeftTop(-10000,-10000);
34513 this.activePanel = panel;
34514 panel.setActiveState(true);
34516 panel.setSize(this.box.width, this.box.height);
34518 this.fireEvent("panelactivated", this, panel);
34519 this.fireEvent("invalidated");
34523 * Show the specified panel.
34524 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34525 * @return {Roo.ContentPanel} The shown panel or null
34527 showPanel : function(panel){
34528 if(panel = this.getPanel(panel)){
34529 this.setActivePanel(panel);
34535 * Get the active panel for this region.
34536 * @return {Roo.ContentPanel} The active panel or null
34538 getActivePanel : function(){
34539 return this.activePanel;
34543 * Add the passed ContentPanel(s)
34544 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34545 * @return {Roo.ContentPanel} The panel added (if only one was added)
34547 add : function(panel){
34548 if(arguments.length > 1){
34549 for(var i = 0, len = arguments.length; i < len; i++) {
34550 this.add(arguments[i]);
34554 if(this.hasPanel(panel)){
34555 this.showPanel(panel);
34558 var el = panel.getEl();
34559 if(el.dom.parentNode != this.mgr.el.dom){
34560 this.mgr.el.dom.appendChild(el.dom);
34562 if(panel.setRegion){
34563 panel.setRegion(this);
34565 this.panels.add(panel);
34566 el.setStyle("position", "absolute");
34567 if(!panel.background){
34568 this.setActivePanel(panel);
34569 if(this.config.initialSize && this.panels.getCount()==1){
34570 this.resizeTo(this.config.initialSize);
34573 this.fireEvent("paneladded", this, panel);
34578 * Returns true if the panel is in this region.
34579 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34580 * @return {Boolean}
34582 hasPanel : function(panel){
34583 if(typeof panel == "object"){ // must be panel obj
34584 panel = panel.getId();
34586 return this.getPanel(panel) ? true : false;
34590 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34591 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34592 * @param {Boolean} preservePanel Overrides the config preservePanel option
34593 * @return {Roo.ContentPanel} The panel that was removed
34595 remove : function(panel, preservePanel){
34596 panel = this.getPanel(panel);
34601 this.fireEvent("beforeremove", this, panel, e);
34602 if(e.cancel === true){
34605 var panelId = panel.getId();
34606 this.panels.removeKey(panelId);
34611 * Returns the panel specified or null if it's not in this region.
34612 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34613 * @return {Roo.ContentPanel}
34615 getPanel : function(id){
34616 if(typeof id == "object"){ // must be panel obj
34619 return this.panels.get(id);
34623 * Returns this regions position (north/south/east/west/center).
34626 getPosition: function(){
34627 return this.position;
34631 * Ext JS Library 1.1.1
34632 * Copyright(c) 2006-2007, Ext JS, LLC.
34634 * Originally Released Under LGPL - original licence link has changed is not relivant.
34637 * <script type="text/javascript">
34641 * @class Roo.LayoutRegion
34642 * @extends Roo.BasicLayoutRegion
34643 * This class represents a region in a layout manager.
34644 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
34645 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
34646 * @cfg {Boolean} floatable False to disable floating (defaults to true)
34647 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34648 * @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})
34649 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
34650 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
34651 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
34652 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
34653 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
34654 * @cfg {String} title The title for the region (overrides panel titles)
34655 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
34656 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34657 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
34658 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34659 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
34660 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34661 * the space available, similar to FireFox 1.5 tabs (defaults to false)
34662 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
34663 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
34664 * @cfg {Boolean} showPin True to show a pin button
34665 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
34666 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
34667 * @cfg {Boolean} disableTabTips True to disable tab tooltips
34668 * @cfg {Number} width For East/West panels
34669 * @cfg {Number} height For North/South panels
34670 * @cfg {Boolean} split To show the splitter
34671 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
34673 Roo.LayoutRegion = function(mgr, config, pos){
34674 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34675 var dh = Roo.DomHelper;
34676 /** This region's container element
34677 * @type Roo.Element */
34678 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34679 /** This region's title element
34680 * @type Roo.Element */
34682 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34683 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
34684 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34686 this.titleEl.enableDisplayMode();
34687 /** This region's title text element
34688 * @type HTMLElement */
34689 this.titleTextEl = this.titleEl.dom.firstChild;
34690 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34691 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34692 this.closeBtn.enableDisplayMode();
34693 this.closeBtn.on("click", this.closeClicked, this);
34694 this.closeBtn.hide();
34696 this.createBody(config);
34697 this.visible = true;
34698 this.collapsed = false;
34700 if(config.hideWhenEmpty){
34702 this.on("paneladded", this.validateVisibility, this);
34703 this.on("panelremoved", this.validateVisibility, this);
34705 this.applyConfig(config);
34708 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34710 createBody : function(){
34711 /** This region's body element
34712 * @type Roo.Element */
34713 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34716 applyConfig : function(c){
34717 if(c.collapsible && this.position != "center" && !this.collapsedEl){
34718 var dh = Roo.DomHelper;
34719 if(c.titlebar !== false){
34720 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34721 this.collapseBtn.on("click", this.collapse, this);
34722 this.collapseBtn.enableDisplayMode();
34724 if(c.showPin === true || this.showPin){
34725 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34726 this.stickBtn.enableDisplayMode();
34727 this.stickBtn.on("click", this.expand, this);
34728 this.stickBtn.hide();
34731 /** This region's collapsed element
34732 * @type Roo.Element */
34733 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34734 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34736 if(c.floatable !== false){
34737 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34738 this.collapsedEl.on("click", this.collapseClick, this);
34741 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34742 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34743 id: "message", unselectable: "on", style:{"float":"left"}});
34744 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34746 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34747 this.expandBtn.on("click", this.expand, this);
34749 if(this.collapseBtn){
34750 this.collapseBtn.setVisible(c.collapsible == true);
34752 this.cmargins = c.cmargins || this.cmargins ||
34753 (this.position == "west" || this.position == "east" ?
34754 {top: 0, left: 2, right:2, bottom: 0} :
34755 {top: 2, left: 0, right:0, bottom: 2});
34756 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34757 this.bottomTabs = c.tabPosition != "top";
34758 this.autoScroll = c.autoScroll || false;
34759 if(this.autoScroll){
34760 this.bodyEl.setStyle("overflow", "auto");
34762 this.bodyEl.setStyle("overflow", "hidden");
34764 //if(c.titlebar !== false){
34765 if((!c.titlebar && !c.title) || c.titlebar === false){
34766 this.titleEl.hide();
34768 this.titleEl.show();
34770 this.titleTextEl.innerHTML = c.title;
34774 this.duration = c.duration || .30;
34775 this.slideDuration = c.slideDuration || .45;
34778 this.collapse(true);
34785 * Returns true if this region is currently visible.
34786 * @return {Boolean}
34788 isVisible : function(){
34789 return this.visible;
34793 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34794 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
34796 setCollapsedTitle : function(title){
34797 title = title || " ";
34798 if(this.collapsedTitleTextEl){
34799 this.collapsedTitleTextEl.innerHTML = title;
34803 getBox : function(){
34805 if(!this.collapsed){
34806 b = this.el.getBox(false, true);
34808 b = this.collapsedEl.getBox(false, true);
34813 getMargins : function(){
34814 return this.collapsed ? this.cmargins : this.margins;
34817 highlight : function(){
34818 this.el.addClass("x-layout-panel-dragover");
34821 unhighlight : function(){
34822 this.el.removeClass("x-layout-panel-dragover");
34825 updateBox : function(box){
34827 if(!this.collapsed){
34828 this.el.dom.style.left = box.x + "px";
34829 this.el.dom.style.top = box.y + "px";
34830 this.updateBody(box.width, box.height);
34832 this.collapsedEl.dom.style.left = box.x + "px";
34833 this.collapsedEl.dom.style.top = box.y + "px";
34834 this.collapsedEl.setSize(box.width, box.height);
34837 this.tabs.autoSizeTabs();
34841 updateBody : function(w, h){
34843 this.el.setWidth(w);
34844 w -= this.el.getBorderWidth("rl");
34845 if(this.config.adjustments){
34846 w += this.config.adjustments[0];
34850 this.el.setHeight(h);
34851 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34852 h -= this.el.getBorderWidth("tb");
34853 if(this.config.adjustments){
34854 h += this.config.adjustments[1];
34856 this.bodyEl.setHeight(h);
34858 h = this.tabs.syncHeight(h);
34861 if(this.panelSize){
34862 w = w !== null ? w : this.panelSize.width;
34863 h = h !== null ? h : this.panelSize.height;
34865 if(this.activePanel){
34866 var el = this.activePanel.getEl();
34867 w = w !== null ? w : el.getWidth();
34868 h = h !== null ? h : el.getHeight();
34869 this.panelSize = {width: w, height: h};
34870 this.activePanel.setSize(w, h);
34872 if(Roo.isIE && this.tabs){
34873 this.tabs.el.repaint();
34878 * Returns the container element for this region.
34879 * @return {Roo.Element}
34881 getEl : function(){
34886 * Hides this region.
34889 if(!this.collapsed){
34890 this.el.dom.style.left = "-2000px";
34893 this.collapsedEl.dom.style.left = "-2000px";
34894 this.collapsedEl.hide();
34896 this.visible = false;
34897 this.fireEvent("visibilitychange", this, false);
34901 * Shows this region if it was previously hidden.
34904 if(!this.collapsed){
34907 this.collapsedEl.show();
34909 this.visible = true;
34910 this.fireEvent("visibilitychange", this, true);
34913 closeClicked : function(){
34914 if(this.activePanel){
34915 this.remove(this.activePanel);
34919 collapseClick : function(e){
34921 e.stopPropagation();
34924 e.stopPropagation();
34930 * Collapses this region.
34931 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34933 collapse : function(skipAnim, skipCheck){
34934 if(this.collapsed) {
34938 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34940 this.collapsed = true;
34942 this.split.el.hide();
34944 if(this.config.animate && skipAnim !== true){
34945 this.fireEvent("invalidated", this);
34946 this.animateCollapse();
34948 this.el.setLocation(-20000,-20000);
34950 this.collapsedEl.show();
34951 this.fireEvent("collapsed", this);
34952 this.fireEvent("invalidated", this);
34958 animateCollapse : function(){
34963 * Expands this region if it was previously collapsed.
34964 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34965 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34967 expand : function(e, skipAnim){
34969 e.stopPropagation();
34971 if(!this.collapsed || this.el.hasActiveFx()) {
34975 this.afterSlideIn();
34978 this.collapsed = false;
34979 if(this.config.animate && skipAnim !== true){
34980 this.animateExpand();
34984 this.split.el.show();
34986 this.collapsedEl.setLocation(-2000,-2000);
34987 this.collapsedEl.hide();
34988 this.fireEvent("invalidated", this);
34989 this.fireEvent("expanded", this);
34993 animateExpand : function(){
34997 initTabs : function()
34999 this.bodyEl.setStyle("overflow", "hidden");
35000 var ts = new Roo.TabPanel(
35003 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35004 disableTooltips: this.config.disableTabTips,
35005 toolbar : this.config.toolbar
35008 if(this.config.hideTabs){
35009 ts.stripWrap.setDisplayed(false);
35012 ts.resizeTabs = this.config.resizeTabs === true;
35013 ts.minTabWidth = this.config.minTabWidth || 40;
35014 ts.maxTabWidth = this.config.maxTabWidth || 250;
35015 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35016 ts.monitorResize = false;
35017 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35018 ts.bodyEl.addClass('x-layout-tabs-body');
35019 this.panels.each(this.initPanelAsTab, this);
35022 initPanelAsTab : function(panel){
35023 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
35024 this.config.closeOnTab && panel.isClosable());
35025 if(panel.tabTip !== undefined){
35026 ti.setTooltip(panel.tabTip);
35028 ti.on("activate", function(){
35029 this.setActivePanel(panel);
35031 if(this.config.closeOnTab){
35032 ti.on("beforeclose", function(t, e){
35034 this.remove(panel);
35040 updatePanelTitle : function(panel, title){
35041 if(this.activePanel == panel){
35042 this.updateTitle(title);
35045 var ti = this.tabs.getTab(panel.getEl().id);
35047 if(panel.tabTip !== undefined){
35048 ti.setTooltip(panel.tabTip);
35053 updateTitle : function(title){
35054 if(this.titleTextEl && !this.config.title){
35055 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35059 setActivePanel : function(panel){
35060 panel = this.getPanel(panel);
35061 if(this.activePanel && this.activePanel != panel){
35062 this.activePanel.setActiveState(false);
35064 this.activePanel = panel;
35065 panel.setActiveState(true);
35066 if(this.panelSize){
35067 panel.setSize(this.panelSize.width, this.panelSize.height);
35070 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35072 this.updateTitle(panel.getTitle());
35074 this.fireEvent("invalidated", this);
35076 this.fireEvent("panelactivated", this, panel);
35080 * Shows the specified panel.
35081 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35082 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35084 showPanel : function(panel)
35086 panel = this.getPanel(panel);
35089 var tab = this.tabs.getTab(panel.getEl().id);
35090 if(tab.isHidden()){
35091 this.tabs.unhideTab(tab.id);
35095 this.setActivePanel(panel);
35102 * Get the active panel for this region.
35103 * @return {Roo.ContentPanel} The active panel or null
35105 getActivePanel : function(){
35106 return this.activePanel;
35109 validateVisibility : function(){
35110 if(this.panels.getCount() < 1){
35111 this.updateTitle(" ");
35112 this.closeBtn.hide();
35115 if(!this.isVisible()){
35122 * Adds the passed ContentPanel(s) to this region.
35123 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35124 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35126 add : function(panel){
35127 if(arguments.length > 1){
35128 for(var i = 0, len = arguments.length; i < len; i++) {
35129 this.add(arguments[i]);
35133 if(this.hasPanel(panel)){
35134 this.showPanel(panel);
35137 panel.setRegion(this);
35138 this.panels.add(panel);
35139 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35140 this.bodyEl.dom.appendChild(panel.getEl().dom);
35141 if(panel.background !== true){
35142 this.setActivePanel(panel);
35144 this.fireEvent("paneladded", this, panel);
35150 this.initPanelAsTab(panel);
35152 if(panel.background !== true){
35153 this.tabs.activate(panel.getEl().id);
35155 this.fireEvent("paneladded", this, panel);
35160 * Hides the tab for the specified panel.
35161 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35163 hidePanel : function(panel){
35164 if(this.tabs && (panel = this.getPanel(panel))){
35165 this.tabs.hideTab(panel.getEl().id);
35170 * Unhides the tab for a previously hidden panel.
35171 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35173 unhidePanel : function(panel){
35174 if(this.tabs && (panel = this.getPanel(panel))){
35175 this.tabs.unhideTab(panel.getEl().id);
35179 clearPanels : function(){
35180 while(this.panels.getCount() > 0){
35181 this.remove(this.panels.first());
35186 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35187 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35188 * @param {Boolean} preservePanel Overrides the config preservePanel option
35189 * @return {Roo.ContentPanel} The panel that was removed
35191 remove : function(panel, preservePanel){
35192 panel = this.getPanel(panel);
35197 this.fireEvent("beforeremove", this, panel, e);
35198 if(e.cancel === true){
35201 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35202 var panelId = panel.getId();
35203 this.panels.removeKey(panelId);
35205 document.body.appendChild(panel.getEl().dom);
35208 this.tabs.removeTab(panel.getEl().id);
35209 }else if (!preservePanel){
35210 this.bodyEl.dom.removeChild(panel.getEl().dom);
35212 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35213 var p = this.panels.first();
35214 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35215 tempEl.appendChild(p.getEl().dom);
35216 this.bodyEl.update("");
35217 this.bodyEl.dom.appendChild(p.getEl().dom);
35219 this.updateTitle(p.getTitle());
35221 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35222 this.setActivePanel(p);
35224 panel.setRegion(null);
35225 if(this.activePanel == panel){
35226 this.activePanel = null;
35228 if(this.config.autoDestroy !== false && preservePanel !== true){
35229 try{panel.destroy();}catch(e){}
35231 this.fireEvent("panelremoved", this, panel);
35236 * Returns the TabPanel component used by this region
35237 * @return {Roo.TabPanel}
35239 getTabs : function(){
35243 createTool : function(parentEl, className){
35244 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35245 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
35246 btn.addClassOnOver("x-layout-tools-button-over");
35251 * Ext JS Library 1.1.1
35252 * Copyright(c) 2006-2007, Ext JS, LLC.
35254 * Originally Released Under LGPL - original licence link has changed is not relivant.
35257 * <script type="text/javascript">
35263 * @class Roo.SplitLayoutRegion
35264 * @extends Roo.LayoutRegion
35265 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35267 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35268 this.cursor = cursor;
35269 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35272 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35273 splitTip : "Drag to resize.",
35274 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35275 useSplitTips : false,
35277 applyConfig : function(config){
35278 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35281 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
35282 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
35283 /** The SplitBar for this region
35284 * @type Roo.SplitBar */
35285 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35286 this.split.on("moved", this.onSplitMove, this);
35287 this.split.useShim = config.useShim === true;
35288 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35289 if(this.useSplitTips){
35290 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35292 if(config.collapsible){
35293 this.split.el.on("dblclick", this.collapse, this);
35296 if(typeof config.minSize != "undefined"){
35297 this.split.minSize = config.minSize;
35299 if(typeof config.maxSize != "undefined"){
35300 this.split.maxSize = config.maxSize;
35302 if(config.hideWhenEmpty || config.hidden || config.collapsed){
35303 this.hideSplitter();
35308 getHMaxSize : function(){
35309 var cmax = this.config.maxSize || 10000;
35310 var center = this.mgr.getRegion("center");
35311 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35314 getVMaxSize : function(){
35315 var cmax = this.config.maxSize || 10000;
35316 var center = this.mgr.getRegion("center");
35317 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35320 onSplitMove : function(split, newSize){
35321 this.fireEvent("resized", this, newSize);
35325 * Returns the {@link Roo.SplitBar} for this region.
35326 * @return {Roo.SplitBar}
35328 getSplitBar : function(){
35333 this.hideSplitter();
35334 Roo.SplitLayoutRegion.superclass.hide.call(this);
35337 hideSplitter : function(){
35339 this.split.el.setLocation(-2000,-2000);
35340 this.split.el.hide();
35346 this.split.el.show();
35348 Roo.SplitLayoutRegion.superclass.show.call(this);
35351 beforeSlide: function(){
35352 if(Roo.isGecko){// firefox overflow auto bug workaround
35353 this.bodyEl.clip();
35355 this.tabs.bodyEl.clip();
35357 if(this.activePanel){
35358 this.activePanel.getEl().clip();
35360 if(this.activePanel.beforeSlide){
35361 this.activePanel.beforeSlide();
35367 afterSlide : function(){
35368 if(Roo.isGecko){// firefox overflow auto bug workaround
35369 this.bodyEl.unclip();
35371 this.tabs.bodyEl.unclip();
35373 if(this.activePanel){
35374 this.activePanel.getEl().unclip();
35375 if(this.activePanel.afterSlide){
35376 this.activePanel.afterSlide();
35382 initAutoHide : function(){
35383 if(this.autoHide !== false){
35384 if(!this.autoHideHd){
35385 var st = new Roo.util.DelayedTask(this.slideIn, this);
35386 this.autoHideHd = {
35387 "mouseout": function(e){
35388 if(!e.within(this.el, true)){
35392 "mouseover" : function(e){
35398 this.el.on(this.autoHideHd);
35402 clearAutoHide : function(){
35403 if(this.autoHide !== false){
35404 this.el.un("mouseout", this.autoHideHd.mouseout);
35405 this.el.un("mouseover", this.autoHideHd.mouseover);
35409 clearMonitor : function(){
35410 Roo.get(document).un("click", this.slideInIf, this);
35413 // these names are backwards but not changed for compat
35414 slideOut : function(){
35415 if(this.isSlid || this.el.hasActiveFx()){
35418 this.isSlid = true;
35419 if(this.collapseBtn){
35420 this.collapseBtn.hide();
35422 this.closeBtnState = this.closeBtn.getStyle('display');
35423 this.closeBtn.hide();
35425 this.stickBtn.show();
35428 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35429 this.beforeSlide();
35430 this.el.setStyle("z-index", 10001);
35431 this.el.slideIn(this.getSlideAnchor(), {
35432 callback: function(){
35434 this.initAutoHide();
35435 Roo.get(document).on("click", this.slideInIf, this);
35436 this.fireEvent("slideshow", this);
35443 afterSlideIn : function(){
35444 this.clearAutoHide();
35445 this.isSlid = false;
35446 this.clearMonitor();
35447 this.el.setStyle("z-index", "");
35448 if(this.collapseBtn){
35449 this.collapseBtn.show();
35451 this.closeBtn.setStyle('display', this.closeBtnState);
35453 this.stickBtn.hide();
35455 this.fireEvent("slidehide", this);
35458 slideIn : function(cb){
35459 if(!this.isSlid || this.el.hasActiveFx()){
35463 this.isSlid = false;
35464 this.beforeSlide();
35465 this.el.slideOut(this.getSlideAnchor(), {
35466 callback: function(){
35467 this.el.setLeftTop(-10000, -10000);
35469 this.afterSlideIn();
35477 slideInIf : function(e){
35478 if(!e.within(this.el)){
35483 animateCollapse : function(){
35484 this.beforeSlide();
35485 this.el.setStyle("z-index", 20000);
35486 var anchor = this.getSlideAnchor();
35487 this.el.slideOut(anchor, {
35488 callback : function(){
35489 this.el.setStyle("z-index", "");
35490 this.collapsedEl.slideIn(anchor, {duration:.3});
35492 this.el.setLocation(-10000,-10000);
35494 this.fireEvent("collapsed", this);
35501 animateExpand : function(){
35502 this.beforeSlide();
35503 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35504 this.el.setStyle("z-index", 20000);
35505 this.collapsedEl.hide({
35508 this.el.slideIn(this.getSlideAnchor(), {
35509 callback : function(){
35510 this.el.setStyle("z-index", "");
35513 this.split.el.show();
35515 this.fireEvent("invalidated", this);
35516 this.fireEvent("expanded", this);
35544 getAnchor : function(){
35545 return this.anchors[this.position];
35548 getCollapseAnchor : function(){
35549 return this.canchors[this.position];
35552 getSlideAnchor : function(){
35553 return this.sanchors[this.position];
35556 getAlignAdj : function(){
35557 var cm = this.cmargins;
35558 switch(this.position){
35574 getExpandAdj : function(){
35575 var c = this.collapsedEl, cm = this.cmargins;
35576 switch(this.position){
35578 return [-(cm.right+c.getWidth()+cm.left), 0];
35581 return [cm.right+c.getWidth()+cm.left, 0];
35584 return [0, -(cm.top+cm.bottom+c.getHeight())];
35587 return [0, cm.top+cm.bottom+c.getHeight()];
35593 * Ext JS Library 1.1.1
35594 * Copyright(c) 2006-2007, Ext JS, LLC.
35596 * Originally Released Under LGPL - original licence link has changed is not relivant.
35599 * <script type="text/javascript">
35602 * These classes are private internal classes
35604 Roo.CenterLayoutRegion = function(mgr, config){
35605 Roo.LayoutRegion.call(this, mgr, config, "center");
35606 this.visible = true;
35607 this.minWidth = config.minWidth || 20;
35608 this.minHeight = config.minHeight || 20;
35611 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35613 // center panel can't be hidden
35617 // center panel can't be hidden
35620 getMinWidth: function(){
35621 return this.minWidth;
35624 getMinHeight: function(){
35625 return this.minHeight;
35630 Roo.NorthLayoutRegion = function(mgr, config){
35631 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35633 this.split.placement = Roo.SplitBar.TOP;
35634 this.split.orientation = Roo.SplitBar.VERTICAL;
35635 this.split.el.addClass("x-layout-split-v");
35637 var size = config.initialSize || config.height;
35638 if(typeof size != "undefined"){
35639 this.el.setHeight(size);
35642 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35643 orientation: Roo.SplitBar.VERTICAL,
35644 getBox : function(){
35645 if(this.collapsed){
35646 return this.collapsedEl.getBox();
35648 var box = this.el.getBox();
35650 box.height += this.split.el.getHeight();
35655 updateBox : function(box){
35656 if(this.split && !this.collapsed){
35657 box.height -= this.split.el.getHeight();
35658 this.split.el.setLeft(box.x);
35659 this.split.el.setTop(box.y+box.height);
35660 this.split.el.setWidth(box.width);
35662 if(this.collapsed){
35663 this.updateBody(box.width, null);
35665 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35669 Roo.SouthLayoutRegion = function(mgr, config){
35670 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35672 this.split.placement = Roo.SplitBar.BOTTOM;
35673 this.split.orientation = Roo.SplitBar.VERTICAL;
35674 this.split.el.addClass("x-layout-split-v");
35676 var size = config.initialSize || config.height;
35677 if(typeof size != "undefined"){
35678 this.el.setHeight(size);
35681 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35682 orientation: Roo.SplitBar.VERTICAL,
35683 getBox : function(){
35684 if(this.collapsed){
35685 return this.collapsedEl.getBox();
35687 var box = this.el.getBox();
35689 var sh = this.split.el.getHeight();
35696 updateBox : function(box){
35697 if(this.split && !this.collapsed){
35698 var sh = this.split.el.getHeight();
35701 this.split.el.setLeft(box.x);
35702 this.split.el.setTop(box.y-sh);
35703 this.split.el.setWidth(box.width);
35705 if(this.collapsed){
35706 this.updateBody(box.width, null);
35708 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35712 Roo.EastLayoutRegion = function(mgr, config){
35713 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35715 this.split.placement = Roo.SplitBar.RIGHT;
35716 this.split.orientation = Roo.SplitBar.HORIZONTAL;
35717 this.split.el.addClass("x-layout-split-h");
35719 var size = config.initialSize || config.width;
35720 if(typeof size != "undefined"){
35721 this.el.setWidth(size);
35724 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35725 orientation: Roo.SplitBar.HORIZONTAL,
35726 getBox : function(){
35727 if(this.collapsed){
35728 return this.collapsedEl.getBox();
35730 var box = this.el.getBox();
35732 var sw = this.split.el.getWidth();
35739 updateBox : function(box){
35740 if(this.split && !this.collapsed){
35741 var sw = this.split.el.getWidth();
35743 this.split.el.setLeft(box.x);
35744 this.split.el.setTop(box.y);
35745 this.split.el.setHeight(box.height);
35748 if(this.collapsed){
35749 this.updateBody(null, box.height);
35751 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35755 Roo.WestLayoutRegion = function(mgr, config){
35756 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35758 this.split.placement = Roo.SplitBar.LEFT;
35759 this.split.orientation = Roo.SplitBar.HORIZONTAL;
35760 this.split.el.addClass("x-layout-split-h");
35762 var size = config.initialSize || config.width;
35763 if(typeof size != "undefined"){
35764 this.el.setWidth(size);
35767 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35768 orientation: Roo.SplitBar.HORIZONTAL,
35769 getBox : function(){
35770 if(this.collapsed){
35771 return this.collapsedEl.getBox();
35773 var box = this.el.getBox();
35775 box.width += this.split.el.getWidth();
35780 updateBox : function(box){
35781 if(this.split && !this.collapsed){
35782 var sw = this.split.el.getWidth();
35784 this.split.el.setLeft(box.x+box.width);
35785 this.split.el.setTop(box.y);
35786 this.split.el.setHeight(box.height);
35788 if(this.collapsed){
35789 this.updateBody(null, box.height);
35791 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35796 * Ext JS Library 1.1.1
35797 * Copyright(c) 2006-2007, Ext JS, LLC.
35799 * Originally Released Under LGPL - original licence link has changed is not relivant.
35802 * <script type="text/javascript">
35807 * Private internal class for reading and applying state
35809 Roo.LayoutStateManager = function(layout){
35810 // default empty state
35819 Roo.LayoutStateManager.prototype = {
35820 init : function(layout, provider){
35821 this.provider = provider;
35822 var state = provider.get(layout.id+"-layout-state");
35824 var wasUpdating = layout.isUpdating();
35826 layout.beginUpdate();
35828 for(var key in state){
35829 if(typeof state[key] != "function"){
35830 var rstate = state[key];
35831 var r = layout.getRegion(key);
35834 r.resizeTo(rstate.size);
35836 if(rstate.collapsed == true){
35839 r.expand(null, true);
35845 layout.endUpdate();
35847 this.state = state;
35849 this.layout = layout;
35850 layout.on("regionresized", this.onRegionResized, this);
35851 layout.on("regioncollapsed", this.onRegionCollapsed, this);
35852 layout.on("regionexpanded", this.onRegionExpanded, this);
35855 storeState : function(){
35856 this.provider.set(this.layout.id+"-layout-state", this.state);
35859 onRegionResized : function(region, newSize){
35860 this.state[region.getPosition()].size = newSize;
35864 onRegionCollapsed : function(region){
35865 this.state[region.getPosition()].collapsed = true;
35869 onRegionExpanded : function(region){
35870 this.state[region.getPosition()].collapsed = false;
35875 * Ext JS Library 1.1.1
35876 * Copyright(c) 2006-2007, Ext JS, LLC.
35878 * Originally Released Under LGPL - original licence link has changed is not relivant.
35881 * <script type="text/javascript">
35884 * @class Roo.ContentPanel
35885 * @extends Roo.util.Observable
35886 * @children Roo.form.Form Roo.JsonView Roo.View
35887 * @parent Roo.BorderLayout Roo.LayoutDialog builder
35888 * A basic ContentPanel element.
35889 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
35890 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
35891 * @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
35892 * @cfg {Boolean} closable True if the panel can be closed/removed
35893 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
35894 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35895 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
35896 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
35897 * @cfg {String} title The title for this panel
35898 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35899 * @cfg {String} url Calls {@link #setUrl} with this value
35900 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35901 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
35902 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
35903 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
35904 * @cfg {String} style Extra style to add to the content panel
35905 * @cfg {Roo.menu.Menu} menu popup menu
35908 * Create a new ContentPanel.
35909 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35910 * @param {String/Object} config A string to set only the title or a config object
35911 * @param {String} content (optional) Set the HTML content for this panel
35912 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35914 Roo.ContentPanel = function(el, config, content){
35918 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35922 if (config && config.parentLayout) {
35923 el = config.parentLayout.el.createChild();
35926 if(el.autoCreate){ // xtype is available if this is called from factory
35930 this.el = Roo.get(el);
35931 if(!this.el && config && config.autoCreate){
35932 if(typeof config.autoCreate == "object"){
35933 if(!config.autoCreate.id){
35934 config.autoCreate.id = config.id||el;
35936 this.el = Roo.DomHelper.append(document.body,
35937 config.autoCreate, true);
35939 this.el = Roo.DomHelper.append(document.body,
35940 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35945 this.closable = false;
35946 this.loaded = false;
35947 this.active = false;
35948 if(typeof config == "string"){
35949 this.title = config;
35951 Roo.apply(this, config);
35954 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35955 this.wrapEl = this.el.wrap();
35956 this.toolbar.container = this.el.insertSibling(false, 'before');
35957 this.toolbar = new Roo.Toolbar(this.toolbar);
35960 // xtype created footer. - not sure if will work as we normally have to render first..
35961 if (this.footer && !this.footer.el && this.footer.xtype) {
35962 if (!this.wrapEl) {
35963 this.wrapEl = this.el.wrap();
35966 this.footer.container = this.wrapEl.createChild();
35968 this.footer = Roo.factory(this.footer, Roo);
35973 this.resizeEl = Roo.get(this.resizeEl, true);
35975 this.resizeEl = this.el;
35977 // handle view.xtype
35985 * Fires when this panel is activated.
35986 * @param {Roo.ContentPanel} this
35990 * @event deactivate
35991 * Fires when this panel is activated.
35992 * @param {Roo.ContentPanel} this
35994 "deactivate" : true,
35998 * Fires when this panel is resized if fitToFrame is true.
35999 * @param {Roo.ContentPanel} this
36000 * @param {Number} width The width after any component adjustments
36001 * @param {Number} height The height after any component adjustments
36007 * Fires when this tab is created
36008 * @param {Roo.ContentPanel} this
36018 if(this.autoScroll){
36019 this.resizeEl.setStyle("overflow", "auto");
36021 // fix randome scrolling
36022 this.el.on('scroll', function() {
36023 Roo.log('fix random scolling');
36024 this.scrollTo('top',0);
36027 content = content || this.content;
36029 this.setContent(content);
36031 if(config && config.url){
36032 this.setUrl(this.url, this.params, this.loadOnce);
36037 Roo.ContentPanel.superclass.constructor.call(this);
36039 if (this.view && typeof(this.view.xtype) != 'undefined') {
36040 this.view.el = this.el.appendChild(document.createElement("div"));
36041 this.view = Roo.factory(this.view);
36042 this.view.render && this.view.render(false, '');
36046 this.fireEvent('render', this);
36049 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
36051 setRegion : function(region){
36052 this.region = region;
36054 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
36056 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
36061 * Returns the toolbar for this Panel if one was configured.
36062 * @return {Roo.Toolbar}
36064 getToolbar : function(){
36065 return this.toolbar;
36068 setActiveState : function(active){
36069 this.active = active;
36071 this.fireEvent("deactivate", this);
36073 this.fireEvent("activate", this);
36077 * Updates this panel's element
36078 * @param {String} content The new content
36079 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36081 setContent : function(content, loadScripts){
36082 this.el.update(content, loadScripts);
36085 ignoreResize : function(w, h){
36086 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36089 this.lastSize = {width: w, height: h};
36094 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36095 * @return {Roo.UpdateManager} The UpdateManager
36097 getUpdateManager : function(){
36098 return this.el.getUpdateManager();
36101 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36102 * @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:
36105 url: "your-url.php",
36106 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36107 callback: yourFunction,
36108 scope: yourObject, //(optional scope)
36111 text: "Loading...",
36116 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36117 * 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.
36118 * @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}
36119 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36120 * @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.
36121 * @return {Roo.ContentPanel} this
36124 var um = this.el.getUpdateManager();
36125 um.update.apply(um, arguments);
36131 * 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.
36132 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36133 * @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)
36134 * @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)
36135 * @return {Roo.UpdateManager} The UpdateManager
36137 setUrl : function(url, params, loadOnce){
36138 if(this.refreshDelegate){
36139 this.removeListener("activate", this.refreshDelegate);
36141 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36142 this.on("activate", this.refreshDelegate);
36143 return this.el.getUpdateManager();
36146 _handleRefresh : function(url, params, loadOnce){
36147 if(!loadOnce || !this.loaded){
36148 var updater = this.el.getUpdateManager();
36149 updater.update(url, params, this._setLoaded.createDelegate(this));
36153 _setLoaded : function(){
36154 this.loaded = true;
36158 * Returns this panel's id
36161 getId : function(){
36166 * Returns this panel's element - used by regiosn to add.
36167 * @return {Roo.Element}
36169 getEl : function(){
36170 return this.wrapEl || this.el;
36173 adjustForComponents : function(width, height)
36175 //Roo.log('adjustForComponents ');
36176 if(this.resizeEl != this.el){
36177 width -= this.el.getFrameWidth('lr');
36178 height -= this.el.getFrameWidth('tb');
36181 var te = this.toolbar.getEl();
36182 height -= te.getHeight();
36183 te.setWidth(width);
36186 var te = this.footer.getEl();
36187 //Roo.log("footer:" + te.getHeight());
36189 height -= te.getHeight();
36190 te.setWidth(width);
36194 if(this.adjustments){
36195 width += this.adjustments[0];
36196 height += this.adjustments[1];
36198 return {"width": width, "height": height};
36201 setSize : function(width, height){
36202 if(this.fitToFrame && !this.ignoreResize(width, height)){
36203 if(this.fitContainer && this.resizeEl != this.el){
36204 this.el.setSize(width, height);
36206 var size = this.adjustForComponents(width, height);
36207 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36208 this.fireEvent('resize', this, size.width, size.height);
36213 * Returns this panel's title
36216 getTitle : function(){
36221 * Set this panel's title
36222 * @param {String} title
36224 setTitle : function(title){
36225 this.title = title;
36227 this.region.updatePanelTitle(this, title);
36232 * Returns true is this panel was configured to be closable
36233 * @return {Boolean}
36235 isClosable : function(){
36236 return this.closable;
36239 beforeSlide : function(){
36241 this.resizeEl.clip();
36244 afterSlide : function(){
36246 this.resizeEl.unclip();
36250 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36251 * Will fail silently if the {@link #setUrl} method has not been called.
36252 * This does not activate the panel, just updates its content.
36254 refresh : function(){
36255 if(this.refreshDelegate){
36256 this.loaded = false;
36257 this.refreshDelegate();
36262 * Destroys this panel
36264 destroy : function(){
36265 this.el.removeAllListeners();
36266 var tempEl = document.createElement("span");
36267 tempEl.appendChild(this.el.dom);
36268 tempEl.innerHTML = "";
36274 * form - if the content panel contains a form - this is a reference to it.
36275 * @type {Roo.form.Form}
36279 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36280 * This contains a reference to it.
36286 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36296 * @param {Object} cfg Xtype definition of item to add.
36299 addxtype : function(cfg) {
36301 if (cfg.xtype.match(/^Form$/)) {
36304 //if (this.footer) {
36305 // el = this.footer.container.insertSibling(false, 'before');
36307 el = this.el.createChild();
36310 this.form = new Roo.form.Form(cfg);
36313 if ( this.form.allItems.length) {
36314 this.form.render(el.dom);
36318 // should only have one of theses..
36319 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36320 // views.. should not be just added - used named prop 'view''
36322 cfg.el = this.el.appendChild(document.createElement("div"));
36325 var ret = new Roo.factory(cfg);
36327 ret.render && ret.render(false, ''); // render blank..
36347 * @class Roo.GridPanel
36348 * @extends Roo.ContentPanel
36349 * @parent Roo.BorderLayout Roo.LayoutDialog builder
36351 * Create a new GridPanel.
36352 * @cfg {Roo.grid.Grid} grid The grid for this panel
36354 Roo.GridPanel = function(grid, config){
36356 // universal ctor...
36357 if (typeof(grid.grid) != 'undefined') {
36359 grid = config.grid;
36361 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36362 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36364 this.wrapper.dom.appendChild(grid.getGridEl().dom);
36366 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36369 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36371 // xtype created footer. - not sure if will work as we normally have to render first..
36372 if (this.footer && !this.footer.el && this.footer.xtype) {
36374 this.footer.container = this.grid.getView().getFooterPanel(true);
36375 this.footer.dataSource = this.grid.dataSource;
36376 this.footer = Roo.factory(this.footer, Roo);
36380 grid.monitorWindowResize = false; // turn off autosizing
36381 grid.autoHeight = false;
36382 grid.autoWidth = false;
36384 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36387 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36388 getId : function(){
36389 return this.grid.id;
36393 * Returns the grid for this panel
36394 * @return {Roo.grid.Grid}
36396 getGrid : function(){
36400 setSize : function(width, height){
36401 if(!this.ignoreResize(width, height)){
36402 var grid = this.grid;
36403 var size = this.adjustForComponents(width, height);
36404 grid.getGridEl().setSize(size.width, size.height);
36409 beforeSlide : function(){
36410 this.grid.getView().scroller.clip();
36413 afterSlide : function(){
36414 this.grid.getView().scroller.unclip();
36417 destroy : function(){
36418 this.grid.destroy();
36420 Roo.GridPanel.superclass.destroy.call(this);
36426 * @class Roo.NestedLayoutPanel
36427 * @extends Roo.ContentPanel
36428 * @parent Roo.BorderLayout Roo.LayoutDialog builder
36429 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
36433 * Create a new NestedLayoutPanel.
36436 * @param {Roo.BorderLayout} layout [required] The layout for this panel
36437 * @param {String/Object} config A string to set only the title or a config object
36439 Roo.NestedLayoutPanel = function(layout, config)
36441 // construct with only one argument..
36442 /* FIXME - implement nicer consturctors
36443 if (layout.layout) {
36445 layout = config.layout;
36446 delete config.layout;
36448 if (layout.xtype && !layout.getEl) {
36449 // then layout needs constructing..
36450 layout = Roo.factory(layout, Roo);
36455 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36457 layout.monitorWindowResize = false; // turn off autosizing
36458 this.layout = layout;
36459 this.layout.getEl().addClass("x-layout-nested-layout");
36466 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36470 setSize : function(width, height){
36471 if(!this.ignoreResize(width, height)){
36472 var size = this.adjustForComponents(width, height);
36473 var el = this.layout.getEl();
36474 el.setSize(size.width, size.height);
36475 var touch = el.dom.offsetWidth;
36476 this.layout.layout();
36477 // ie requires a double layout on the first pass
36478 if(Roo.isIE && !this.initialized){
36479 this.initialized = true;
36480 this.layout.layout();
36485 // activate all subpanels if not currently active..
36487 setActiveState : function(active){
36488 this.active = active;
36490 this.fireEvent("deactivate", this);
36494 this.fireEvent("activate", this);
36495 // not sure if this should happen before or after..
36496 if (!this.layout) {
36497 return; // should not happen..
36500 for (var r in this.layout.regions) {
36501 reg = this.layout.getRegion(r);
36502 if (reg.getActivePanel()) {
36503 //reg.showPanel(reg.getActivePanel()); // force it to activate..
36504 reg.setActivePanel(reg.getActivePanel());
36507 if (!reg.panels.length) {
36510 reg.showPanel(reg.getPanel(0));
36519 * Returns the nested BorderLayout for this panel
36520 * @return {Roo.BorderLayout}
36522 getLayout : function(){
36523 return this.layout;
36527 * Adds a xtype elements to the layout of the nested panel
36531 xtype : 'ContentPanel',
36538 xtype : 'NestedLayoutPanel',
36544 items : [ ... list of content panels or nested layout panels.. ]
36548 * @param {Object} cfg Xtype definition of item to add.
36550 addxtype : function(cfg) {
36551 return this.layout.addxtype(cfg);
36556 Roo.ScrollPanel = function(el, config, content){
36557 config = config || {};
36558 config.fitToFrame = true;
36559 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36561 this.el.dom.style.overflow = "hidden";
36562 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36563 this.el.removeClass("x-layout-inactive-content");
36564 this.el.on("mousewheel", this.onWheel, this);
36566 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
36567 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
36568 up.unselectable(); down.unselectable();
36569 up.on("click", this.scrollUp, this);
36570 down.on("click", this.scrollDown, this);
36571 up.addClassOnOver("x-scroller-btn-over");
36572 down.addClassOnOver("x-scroller-btn-over");
36573 up.addClassOnClick("x-scroller-btn-click");
36574 down.addClassOnClick("x-scroller-btn-click");
36575 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36577 this.resizeEl = this.el;
36578 this.el = wrap; this.up = up; this.down = down;
36581 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36583 wheelIncrement : 5,
36584 scrollUp : function(){
36585 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36588 scrollDown : function(){
36589 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36592 afterScroll : function(){
36593 var el = this.resizeEl;
36594 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36595 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36596 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36599 setSize : function(){
36600 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36601 this.afterScroll();
36604 onWheel : function(e){
36605 var d = e.getWheelDelta();
36606 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36607 this.afterScroll();
36611 setContent : function(content, loadScripts){
36612 this.resizeEl.update(content, loadScripts);
36620 * @class Roo.TreePanel
36621 * @extends Roo.ContentPanel
36622 * @parent Roo.BorderLayout Roo.LayoutDialog builder
36623 * Treepanel component
36626 * Create a new TreePanel. - defaults to fit/scoll contents.
36627 * @param {String/Object} config A string to set only the panel's title, or a config object
36629 Roo.TreePanel = function(config){
36630 var el = config.el;
36631 var tree = config.tree;
36632 delete config.tree;
36633 delete config.el; // hopefull!
36635 // wrapper for IE7 strict & safari scroll issue
36637 var treeEl = el.createChild();
36638 config.resizeEl = treeEl;
36642 Roo.TreePanel.superclass.constructor.call(this, el, config);
36645 this.tree = new Roo.tree.TreePanel(treeEl , tree);
36646 //console.log(tree);
36647 this.on('activate', function()
36649 if (this.tree.rendered) {
36652 //console.log('render tree');
36653 this.tree.render();
36655 // this should not be needed.. - it's actually the 'el' that resizes?
36656 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36658 //this.on('resize', function (cp, w, h) {
36659 // this.tree.innerCt.setWidth(w);
36660 // this.tree.innerCt.setHeight(h);
36661 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
36668 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
36672 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36679 * Ext JS Library 1.1.1
36680 * Copyright(c) 2006-2007, Ext JS, LLC.
36682 * Originally Released Under LGPL - original licence link has changed is not relivant.
36685 * <script type="text/javascript">
36690 * @class Roo.ReaderLayout
36691 * @extends Roo.BorderLayout
36692 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
36693 * center region containing two nested regions (a top one for a list view and one for item preview below),
36694 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36695 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36696 * expedites the setup of the overall layout and regions for this common application style.
36699 var reader = new Roo.ReaderLayout();
36700 var CP = Roo.ContentPanel; // shortcut for adding
36702 reader.beginUpdate();
36703 reader.add("north", new CP("north", "North"));
36704 reader.add("west", new CP("west", {title: "West"}));
36705 reader.add("east", new CP("east", {title: "East"}));
36707 reader.regions.listView.add(new CP("listView", "List"));
36708 reader.regions.preview.add(new CP("preview", "Preview"));
36709 reader.endUpdate();
36712 * Create a new ReaderLayout
36713 * @param {Object} config Configuration options
36714 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36715 * document.body if omitted)
36717 Roo.ReaderLayout = function(config, renderTo){
36718 var c = config || {size:{}};
36719 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36720 north: c.north !== false ? Roo.apply({
36724 }, c.north) : false,
36725 west: c.west !== false ? Roo.apply({
36733 margins:{left:5,right:0,bottom:5,top:5},
36734 cmargins:{left:5,right:5,bottom:5,top:5}
36735 }, c.west) : false,
36736 east: c.east !== false ? Roo.apply({
36744 margins:{left:0,right:5,bottom:5,top:5},
36745 cmargins:{left:5,right:5,bottom:5,top:5}
36746 }, c.east) : false,
36747 center: Roo.apply({
36748 tabPosition: 'top',
36752 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36756 this.el.addClass('x-reader');
36758 this.beginUpdate();
36760 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36761 south: c.preview !== false ? Roo.apply({
36768 cmargins:{top:5,left:0, right:0, bottom:0}
36769 }, c.preview) : false,
36770 center: Roo.apply({
36776 this.add('center', new Roo.NestedLayoutPanel(inner,
36777 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36781 this.regions.preview = inner.getRegion('south');
36782 this.regions.listView = inner.getRegion('center');
36785 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36787 * Ext JS Library 1.1.1
36788 * Copyright(c) 2006-2007, Ext JS, LLC.
36790 * Originally Released Under LGPL - original licence link has changed is not relivant.
36793 * <script type="text/javascript">
36797 * @class Roo.grid.Grid
36798 * @extends Roo.util.Observable
36799 * This class represents the primary interface of a component based grid control.
36800 * <br><br>Usage:<pre><code>
36801 var grid = new Roo.grid.Grid("my-container-id", {
36804 selModel: mySelectionModel,
36805 autoSizeColumns: true,
36806 monitorWindowResize: false,
36807 trackMouseOver: true
36812 * <b>Common Problems:</b><br/>
36813 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36814 * element will correct this<br/>
36815 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36816 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36817 * are unpredictable.<br/>
36818 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36819 * grid to calculate dimensions/offsets.<br/>
36821 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36822 * The container MUST have some type of size defined for the grid to fill. The container will be
36823 * automatically set to position relative if it isn't already.
36824 * @param {Object} config A config object that sets properties on this grid.
36826 Roo.grid.Grid = function(container, config){
36827 // initialize the container
36828 this.container = Roo.get(container);
36829 this.container.update("");
36830 this.container.setStyle("overflow", "hidden");
36831 this.container.addClass('x-grid-container');
36833 this.id = this.container.id;
36835 Roo.apply(this, config);
36836 // check and correct shorthanded configs
36838 this.dataSource = this.ds;
36842 this.colModel = this.cm;
36846 this.selModel = this.sm;
36850 if (this.selModel) {
36851 this.selModel = Roo.factory(this.selModel, Roo.grid);
36852 this.sm = this.selModel;
36853 this.sm.xmodule = this.xmodule || false;
36855 if (typeof(this.colModel.config) == 'undefined') {
36856 this.colModel = new Roo.grid.ColumnModel(this.colModel);
36857 this.cm = this.colModel;
36858 this.cm.xmodule = this.xmodule || false;
36860 if (this.dataSource) {
36861 this.dataSource= Roo.factory(this.dataSource, Roo.data);
36862 this.ds = this.dataSource;
36863 this.ds.xmodule = this.xmodule || false;
36870 this.container.setWidth(this.width);
36874 this.container.setHeight(this.height);
36881 * The raw click event for the entire grid.
36882 * @param {Roo.EventObject} e
36887 * The raw dblclick event for the entire grid.
36888 * @param {Roo.EventObject} e
36892 * @event contextmenu
36893 * The raw contextmenu event for the entire grid.
36894 * @param {Roo.EventObject} e
36896 "contextmenu" : true,
36899 * The raw mousedown event for the entire grid.
36900 * @param {Roo.EventObject} e
36902 "mousedown" : true,
36905 * The raw mouseup event for the entire grid.
36906 * @param {Roo.EventObject} e
36911 * The raw mouseover event for the entire grid.
36912 * @param {Roo.EventObject} e
36914 "mouseover" : true,
36917 * The raw mouseout event for the entire grid.
36918 * @param {Roo.EventObject} e
36923 * The raw keypress event for the entire grid.
36924 * @param {Roo.EventObject} e
36929 * The raw keydown event for the entire grid.
36930 * @param {Roo.EventObject} e
36938 * Fires when a cell is clicked
36939 * @param {Grid} this
36940 * @param {Number} rowIndex
36941 * @param {Number} columnIndex
36942 * @param {Roo.EventObject} e
36944 "cellclick" : true,
36946 * @event celldblclick
36947 * Fires when a cell is double clicked
36948 * @param {Grid} this
36949 * @param {Number} rowIndex
36950 * @param {Number} columnIndex
36951 * @param {Roo.EventObject} e
36953 "celldblclick" : true,
36956 * Fires when a row is clicked
36957 * @param {Grid} this
36958 * @param {Number} rowIndex
36959 * @param {Roo.EventObject} e
36963 * @event rowdblclick
36964 * Fires when a row is double clicked
36965 * @param {Grid} this
36966 * @param {Number} rowIndex
36967 * @param {Roo.EventObject} e
36969 "rowdblclick" : true,
36971 * @event headerclick
36972 * Fires when a header is clicked
36973 * @param {Grid} this
36974 * @param {Number} columnIndex
36975 * @param {Roo.EventObject} e
36977 "headerclick" : true,
36979 * @event headerdblclick
36980 * Fires when a header cell is double clicked
36981 * @param {Grid} this
36982 * @param {Number} columnIndex
36983 * @param {Roo.EventObject} e
36985 "headerdblclick" : true,
36987 * @event rowcontextmenu
36988 * Fires when a row is right clicked
36989 * @param {Grid} this
36990 * @param {Number} rowIndex
36991 * @param {Roo.EventObject} e
36993 "rowcontextmenu" : true,
36995 * @event cellcontextmenu
36996 * Fires when a cell is right clicked
36997 * @param {Grid} this
36998 * @param {Number} rowIndex
36999 * @param {Number} cellIndex
37000 * @param {Roo.EventObject} e
37002 "cellcontextmenu" : true,
37004 * @event headercontextmenu
37005 * Fires when a header is right clicked
37006 * @param {Grid} this
37007 * @param {Number} columnIndex
37008 * @param {Roo.EventObject} e
37010 "headercontextmenu" : true,
37012 * @event bodyscroll
37013 * Fires when the body element is scrolled
37014 * @param {Number} scrollLeft
37015 * @param {Number} scrollTop
37017 "bodyscroll" : true,
37019 * @event columnresize
37020 * Fires when the user resizes a column
37021 * @param {Number} columnIndex
37022 * @param {Number} newSize
37024 "columnresize" : true,
37026 * @event columnmove
37027 * Fires when the user moves a column
37028 * @param {Number} oldIndex
37029 * @param {Number} newIndex
37031 "columnmove" : true,
37034 * Fires when row(s) start being dragged
37035 * @param {Grid} this
37036 * @param {Roo.GridDD} dd The drag drop object
37037 * @param {event} e The raw browser event
37039 "startdrag" : true,
37042 * Fires when a drag operation is complete
37043 * @param {Grid} this
37044 * @param {Roo.GridDD} dd The drag drop object
37045 * @param {event} e The raw browser event
37050 * Fires when dragged row(s) are dropped on a valid DD target
37051 * @param {Grid} this
37052 * @param {Roo.GridDD} dd The drag drop object
37053 * @param {String} targetId The target drag drop object
37054 * @param {event} e The raw browser event
37059 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37060 * @param {Grid} this
37061 * @param {Roo.GridDD} dd The drag drop object
37062 * @param {String} targetId The target drag drop object
37063 * @param {event} e The raw browser event
37068 * Fires when the dragged row(s) first cross another DD target while being dragged
37069 * @param {Grid} this
37070 * @param {Roo.GridDD} dd The drag drop object
37071 * @param {String} targetId The target drag drop object
37072 * @param {event} e The raw browser event
37074 "dragenter" : true,
37077 * Fires when the dragged row(s) leave another DD target while being dragged
37078 * @param {Grid} this
37079 * @param {Roo.GridDD} dd The drag drop object
37080 * @param {String} targetId The target drag drop object
37081 * @param {event} e The raw browser event
37086 * Fires when a row is rendered, so you can change add a style to it.
37087 * @param {GridView} gridview The grid view
37088 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37094 * Fires when the grid is rendered
37095 * @param {Grid} grid
37100 Roo.grid.Grid.superclass.constructor.call(this);
37102 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
37105 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
37108 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
37111 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
37114 * @cfg {Roo.data.Store} ds The data store for the grid
37117 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
37120 * @cfg {String} ddGroup - drag drop group.
37123 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
37127 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
37129 minColumnWidth : 25,
37132 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
37133 * <b>on initial render.</b> It is more efficient to explicitly size the columns
37134 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
37136 autoSizeColumns : false,
37139 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
37141 autoSizeHeaders : true,
37144 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
37146 monitorWindowResize : true,
37149 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
37150 * rows measured to get a columns size. Default is 0 (all rows).
37152 maxRowsToMeasure : 0,
37155 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
37157 trackMouseOver : true,
37160 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
37163 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
37167 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
37169 enableDragDrop : false,
37172 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
37174 enableColumnMove : true,
37177 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
37179 enableColumnHide : true,
37182 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
37184 enableRowHeightSync : false,
37187 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
37192 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
37194 autoHeight : false,
37197 * @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.
37199 autoExpandColumn : false,
37202 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37205 autoExpandMin : 50,
37208 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37210 autoExpandMax : 1000,
37213 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37218 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37222 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37226 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37228 sortColMenu : false,
37234 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37235 * of a fixed width. Default is false.
37238 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37243 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37244 * %0 is replaced with the number of selected rows.
37246 ddText : "{0} selected row{1}",
37250 * Called once after all setup has been completed and the grid is ready to be rendered.
37251 * @return {Roo.grid.Grid} this
37253 render : function()
37255 var c = this.container;
37256 // try to detect autoHeight/width mode
37257 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37258 this.autoHeight = true;
37260 var view = this.getView();
37263 c.on("click", this.onClick, this);
37264 c.on("dblclick", this.onDblClick, this);
37265 c.on("contextmenu", this.onContextMenu, this);
37266 c.on("keydown", this.onKeyDown, this);
37268 c.on("touchstart", this.onTouchStart, this);
37271 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37273 this.getSelectionModel().init(this);
37278 this.loadMask = new Roo.LoadMask(this.container,
37279 Roo.apply({store:this.dataSource}, this.loadMask));
37283 if (this.toolbar && this.toolbar.xtype) {
37284 this.toolbar.container = this.getView().getHeaderPanel(true);
37285 this.toolbar = new Roo.Toolbar(this.toolbar);
37287 if (this.footer && this.footer.xtype) {
37288 this.footer.dataSource = this.getDataSource();
37289 this.footer.container = this.getView().getFooterPanel(true);
37290 this.footer = Roo.factory(this.footer, Roo);
37292 if (this.dropTarget && this.dropTarget.xtype) {
37293 delete this.dropTarget.xtype;
37294 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37298 this.rendered = true;
37299 this.fireEvent('render', this);
37304 * Reconfigures the grid to use a different Store and Column Model.
37305 * The View will be bound to the new objects and refreshed.
37306 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37307 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37309 reconfigure : function(dataSource, colModel){
37311 this.loadMask.destroy();
37312 this.loadMask = new Roo.LoadMask(this.container,
37313 Roo.apply({store:dataSource}, this.loadMask));
37315 this.view.bind(dataSource, colModel);
37316 this.dataSource = dataSource;
37317 this.colModel = colModel;
37318 this.view.refresh(true);
37322 * Add's a column, default at the end..
37324 * @param {int} position to add (default end)
37325 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
37327 addColumns : function(pos, ar)
37330 for (var i =0;i< ar.length;i++) {
37332 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37333 this.cm.lookup[cfg.id] = cfg;
37337 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37338 pos = this.cm.config.length; //this.cm.config.push(cfg);
37340 pos = Math.max(0,pos);
37343 this.cm.config.splice.apply(this.cm.config, ar);
37347 this.view.generateRules(this.cm);
37348 this.view.refresh(true);
37356 onKeyDown : function(e){
37357 this.fireEvent("keydown", e);
37361 * Destroy this grid.
37362 * @param {Boolean} removeEl True to remove the element
37364 destroy : function(removeEl, keepListeners){
37366 this.loadMask.destroy();
37368 var c = this.container;
37369 c.removeAllListeners();
37370 this.view.destroy();
37371 this.colModel.purgeListeners();
37372 if(!keepListeners){
37373 this.purgeListeners();
37376 if(removeEl === true){
37382 processEvent : function(name, e){
37383 // does this fire select???
37384 //Roo.log('grid:processEvent ' + name);
37386 if (name != 'touchstart' ) {
37387 this.fireEvent(name, e);
37390 var t = e.getTarget();
37392 var header = v.findHeaderIndex(t);
37393 if(header !== false){
37394 var ename = name == 'touchstart' ? 'click' : name;
37396 this.fireEvent("header" + ename, this, header, e);
37398 var row = v.findRowIndex(t);
37399 var cell = v.findCellIndex(t);
37400 if (name == 'touchstart') {
37401 // first touch is always a click.
37402 // hopefull this happens after selection is updated.?
37405 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37406 var cs = this.selModel.getSelectedCell();
37407 if (row == cs[0] && cell == cs[1]){
37411 if (typeof(this.selModel.getSelections) != 'undefined') {
37412 var cs = this.selModel.getSelections();
37413 var ds = this.dataSource;
37414 if (cs.length == 1 && ds.getAt(row) == cs[0]){
37425 this.fireEvent("row" + name, this, row, e);
37426 if(cell !== false){
37427 this.fireEvent("cell" + name, this, row, cell, e);
37434 onClick : function(e){
37435 this.processEvent("click", e);
37438 onTouchStart : function(e){
37439 this.processEvent("touchstart", e);
37443 onContextMenu : function(e, t){
37444 this.processEvent("contextmenu", e);
37448 onDblClick : function(e){
37449 this.processEvent("dblclick", e);
37453 walkCells : function(row, col, step, fn, scope){
37454 var cm = this.colModel, clen = cm.getColumnCount();
37455 var ds = this.dataSource, rlen = ds.getCount(), first = true;
37467 if(fn.call(scope || this, row, col, cm) === true){
37485 if(fn.call(scope || this, row, col, cm) === true){
37497 getSelections : function(){
37498 return this.selModel.getSelections();
37502 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37503 * but if manual update is required this method will initiate it.
37505 autoSize : function(){
37507 this.view.layout();
37508 if(this.view.adjustForScroll){
37509 this.view.adjustForScroll();
37515 * Returns the grid's underlying element.
37516 * @return {Element} The element
37518 getGridEl : function(){
37519 return this.container;
37522 // private for compatibility, overridden by editor grid
37523 stopEditing : function(){},
37526 * Returns the grid's SelectionModel.
37527 * @return {SelectionModel}
37529 getSelectionModel : function(){
37530 if(!this.selModel){
37531 this.selModel = new Roo.grid.RowSelectionModel();
37533 return this.selModel;
37537 * Returns the grid's DataSource.
37538 * @return {DataSource}
37540 getDataSource : function(){
37541 return this.dataSource;
37545 * Returns the grid's ColumnModel.
37546 * @return {ColumnModel}
37548 getColumnModel : function(){
37549 return this.colModel;
37553 * Returns the grid's GridView object.
37554 * @return {GridView}
37556 getView : function(){
37558 this.view = new Roo.grid.GridView(this.viewConfig);
37559 this.relayEvents(this.view, [
37560 "beforerowremoved", "beforerowsinserted",
37561 "beforerefresh", "rowremoved",
37562 "rowsinserted", "rowupdated" ,"refresh"
37568 * Called to get grid's drag proxy text, by default returns this.ddText.
37569 * Override this to put something different in the dragged text.
37572 getDragDropText : function(){
37573 var count = this.selModel.getCount();
37574 return String.format(this.ddText, count, count == 1 ? '' : 's');
37579 * Ext JS Library 1.1.1
37580 * Copyright(c) 2006-2007, Ext JS, LLC.
37582 * Originally Released Under LGPL - original licence link has changed is not relivant.
37585 * <script type="text/javascript">
37588 * @class Roo.grid.AbstractGridView
37589 * @extends Roo.util.Observable
37591 * Abstract base class for grid Views
37594 Roo.grid.AbstractGridView = function(){
37598 "beforerowremoved" : true,
37599 "beforerowsinserted" : true,
37600 "beforerefresh" : true,
37601 "rowremoved" : true,
37602 "rowsinserted" : true,
37603 "rowupdated" : true,
37606 Roo.grid.AbstractGridView.superclass.constructor.call(this);
37609 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37610 rowClass : "x-grid-row",
37611 cellClass : "x-grid-cell",
37612 tdClass : "x-grid-td",
37613 hdClass : "x-grid-hd",
37614 splitClass : "x-grid-hd-split",
37616 init: function(grid){
37618 var cid = this.grid.getGridEl().id;
37619 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37620 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37621 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37622 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37625 getColumnRenderers : function(){
37626 var renderers = [];
37627 var cm = this.grid.colModel;
37628 var colCount = cm.getColumnCount();
37629 for(var i = 0; i < colCount; i++){
37630 renderers[i] = cm.getRenderer(i);
37635 getColumnIds : function(){
37637 var cm = this.grid.colModel;
37638 var colCount = cm.getColumnCount();
37639 for(var i = 0; i < colCount; i++){
37640 ids[i] = cm.getColumnId(i);
37645 getDataIndexes : function(){
37646 if(!this.indexMap){
37647 this.indexMap = this.buildIndexMap();
37649 return this.indexMap.colToData;
37652 getColumnIndexByDataIndex : function(dataIndex){
37653 if(!this.indexMap){
37654 this.indexMap = this.buildIndexMap();
37656 return this.indexMap.dataToCol[dataIndex];
37660 * Set a css style for a column dynamically.
37661 * @param {Number} colIndex The index of the column
37662 * @param {String} name The css property name
37663 * @param {String} value The css value
37665 setCSSStyle : function(colIndex, name, value){
37666 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37667 Roo.util.CSS.updateRule(selector, name, value);
37670 generateRules : function(cm){
37671 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37672 Roo.util.CSS.removeStyleSheet(rulesId);
37673 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37674 var cid = cm.getColumnId(i);
37675 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37676 this.tdSelector, cid, " {\n}\n",
37677 this.hdSelector, cid, " {\n}\n",
37678 this.splitSelector, cid, " {\n}\n");
37680 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37684 * Ext JS Library 1.1.1
37685 * Copyright(c) 2006-2007, Ext JS, LLC.
37687 * Originally Released Under LGPL - original licence link has changed is not relivant.
37690 * <script type="text/javascript">
37694 // This is a support class used internally by the Grid components
37695 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37697 this.view = grid.getView();
37698 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37699 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37701 this.setHandleElId(Roo.id(hd));
37702 this.setOuterHandleElId(Roo.id(hd2));
37704 this.scroll = false;
37706 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37708 getDragData : function(e){
37709 var t = Roo.lib.Event.getTarget(e);
37710 var h = this.view.findHeaderCell(t);
37712 return {ddel: h.firstChild, header:h};
37717 onInitDrag : function(e){
37718 this.view.headersDisabled = true;
37719 var clone = this.dragData.ddel.cloneNode(true);
37720 clone.id = Roo.id();
37721 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37722 this.proxy.update(clone);
37726 afterValidDrop : function(){
37728 setTimeout(function(){
37729 v.headersDisabled = false;
37733 afterInvalidDrop : function(){
37735 setTimeout(function(){
37736 v.headersDisabled = false;
37742 * Ext JS Library 1.1.1
37743 * Copyright(c) 2006-2007, Ext JS, LLC.
37745 * Originally Released Under LGPL - original licence link has changed is not relivant.
37748 * <script type="text/javascript">
37751 // This is a support class used internally by the Grid components
37752 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37754 this.view = grid.getView();
37755 // split the proxies so they don't interfere with mouse events
37756 this.proxyTop = Roo.DomHelper.append(document.body, {
37757 cls:"col-move-top", html:" "
37759 this.proxyBottom = Roo.DomHelper.append(document.body, {
37760 cls:"col-move-bottom", html:" "
37762 this.proxyTop.hide = this.proxyBottom.hide = function(){
37763 this.setLeftTop(-100,-100);
37764 this.setStyle("visibility", "hidden");
37766 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37767 // temporarily disabled
37768 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37769 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37771 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37772 proxyOffsets : [-4, -9],
37773 fly: Roo.Element.fly,
37775 getTargetFromEvent : function(e){
37776 var t = Roo.lib.Event.getTarget(e);
37777 var cindex = this.view.findCellIndex(t);
37778 if(cindex !== false){
37779 return this.view.getHeaderCell(cindex);
37784 nextVisible : function(h){
37785 var v = this.view, cm = this.grid.colModel;
37788 if(!cm.isHidden(v.getCellIndex(h))){
37796 prevVisible : function(h){
37797 var v = this.view, cm = this.grid.colModel;
37800 if(!cm.isHidden(v.getCellIndex(h))){
37808 positionIndicator : function(h, n, e){
37809 var x = Roo.lib.Event.getPageX(e);
37810 var r = Roo.lib.Dom.getRegion(n.firstChild);
37811 var px, pt, py = r.top + this.proxyOffsets[1];
37812 if((r.right - x) <= (r.right-r.left)/2){
37813 px = r.right+this.view.borderWidth;
37819 var oldIndex = this.view.getCellIndex(h);
37820 var newIndex = this.view.getCellIndex(n);
37822 if(this.grid.colModel.isFixed(newIndex)){
37826 var locked = this.grid.colModel.isLocked(newIndex);
37831 if(oldIndex < newIndex){
37834 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37837 px += this.proxyOffsets[0];
37838 this.proxyTop.setLeftTop(px, py);
37839 this.proxyTop.show();
37840 if(!this.bottomOffset){
37841 this.bottomOffset = this.view.mainHd.getHeight();
37843 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37844 this.proxyBottom.show();
37848 onNodeEnter : function(n, dd, e, data){
37849 if(data.header != n){
37850 this.positionIndicator(data.header, n, e);
37854 onNodeOver : function(n, dd, e, data){
37855 var result = false;
37856 if(data.header != n){
37857 result = this.positionIndicator(data.header, n, e);
37860 this.proxyTop.hide();
37861 this.proxyBottom.hide();
37863 return result ? this.dropAllowed : this.dropNotAllowed;
37866 onNodeOut : function(n, dd, e, data){
37867 this.proxyTop.hide();
37868 this.proxyBottom.hide();
37871 onNodeDrop : function(n, dd, e, data){
37872 var h = data.header;
37874 var cm = this.grid.colModel;
37875 var x = Roo.lib.Event.getPageX(e);
37876 var r = Roo.lib.Dom.getRegion(n.firstChild);
37877 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37878 var oldIndex = this.view.getCellIndex(h);
37879 var newIndex = this.view.getCellIndex(n);
37880 var locked = cm.isLocked(newIndex);
37884 if(oldIndex < newIndex){
37887 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37890 cm.setLocked(oldIndex, locked, true);
37891 cm.moveColumn(oldIndex, newIndex);
37892 this.grid.fireEvent("columnmove", oldIndex, newIndex);
37900 * Ext JS Library 1.1.1
37901 * Copyright(c) 2006-2007, Ext JS, LLC.
37903 * Originally Released Under LGPL - original licence link has changed is not relivant.
37906 * <script type="text/javascript">
37910 * @class Roo.grid.GridView
37911 * @extends Roo.util.Observable
37914 * @param {Object} config
37916 Roo.grid.GridView = function(config){
37917 Roo.grid.GridView.superclass.constructor.call(this);
37920 Roo.apply(this, config);
37923 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37925 unselectable : 'unselectable="on"',
37926 unselectableCls : 'x-unselectable',
37929 rowClass : "x-grid-row",
37931 cellClass : "x-grid-col",
37933 tdClass : "x-grid-td",
37935 hdClass : "x-grid-hd",
37937 splitClass : "x-grid-split",
37939 sortClasses : ["sort-asc", "sort-desc"],
37941 enableMoveAnim : false,
37945 dh : Roo.DomHelper,
37947 fly : Roo.Element.fly,
37949 css : Roo.util.CSS,
37955 scrollIncrement : 22,
37957 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37959 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37961 bind : function(ds, cm){
37963 this.ds.un("load", this.onLoad, this);
37964 this.ds.un("datachanged", this.onDataChange, this);
37965 this.ds.un("add", this.onAdd, this);
37966 this.ds.un("remove", this.onRemove, this);
37967 this.ds.un("update", this.onUpdate, this);
37968 this.ds.un("clear", this.onClear, this);
37971 ds.on("load", this.onLoad, this);
37972 ds.on("datachanged", this.onDataChange, this);
37973 ds.on("add", this.onAdd, this);
37974 ds.on("remove", this.onRemove, this);
37975 ds.on("update", this.onUpdate, this);
37976 ds.on("clear", this.onClear, this);
37981 this.cm.un("widthchange", this.onColWidthChange, this);
37982 this.cm.un("headerchange", this.onHeaderChange, this);
37983 this.cm.un("hiddenchange", this.onHiddenChange, this);
37984 this.cm.un("columnmoved", this.onColumnMove, this);
37985 this.cm.un("columnlockchange", this.onColumnLock, this);
37988 this.generateRules(cm);
37989 cm.on("widthchange", this.onColWidthChange, this);
37990 cm.on("headerchange", this.onHeaderChange, this);
37991 cm.on("hiddenchange", this.onHiddenChange, this);
37992 cm.on("columnmoved", this.onColumnMove, this);
37993 cm.on("columnlockchange", this.onColumnLock, this);
37998 init: function(grid){
37999 Roo.grid.GridView.superclass.init.call(this, grid);
38001 this.bind(grid.dataSource, grid.colModel);
38003 grid.on("headerclick", this.handleHeaderClick, this);
38005 if(grid.trackMouseOver){
38006 grid.on("mouseover", this.onRowOver, this);
38007 grid.on("mouseout", this.onRowOut, this);
38009 grid.cancelTextSelection = function(){};
38010 this.gridId = grid.id;
38012 var tpls = this.templates || {};
38015 tpls.master = new Roo.Template(
38016 '<div class="x-grid" hidefocus="true">',
38017 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
38018 '<div class="x-grid-topbar"></div>',
38019 '<div class="x-grid-scroller"><div></div></div>',
38020 '<div class="x-grid-locked">',
38021 '<div class="x-grid-header">{lockedHeader}</div>',
38022 '<div class="x-grid-body">{lockedBody}</div>',
38024 '<div class="x-grid-viewport">',
38025 '<div class="x-grid-header">{header}</div>',
38026 '<div class="x-grid-body">{body}</div>',
38028 '<div class="x-grid-bottombar"></div>',
38030 '<div class="x-grid-resize-proxy"> </div>',
38033 tpls.master.disableformats = true;
38037 tpls.header = new Roo.Template(
38038 '<table border="0" cellspacing="0" cellpadding="0">',
38039 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
38042 tpls.header.disableformats = true;
38044 tpls.header.compile();
38047 tpls.hcell = new Roo.Template(
38048 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
38049 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
38052 tpls.hcell.disableFormats = true;
38054 tpls.hcell.compile();
38057 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
38058 this.unselectableCls + '" ' + this.unselectable +'> </div>');
38059 tpls.hsplit.disableFormats = true;
38061 tpls.hsplit.compile();
38064 tpls.body = new Roo.Template(
38065 '<table border="0" cellspacing="0" cellpadding="0">',
38066 "<tbody>{rows}</tbody>",
38069 tpls.body.disableFormats = true;
38071 tpls.body.compile();
38074 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
38075 tpls.row.disableFormats = true;
38077 tpls.row.compile();
38080 tpls.cell = new Roo.Template(
38081 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
38082 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
38083 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
38086 tpls.cell.disableFormats = true;
38088 tpls.cell.compile();
38090 this.templates = tpls;
38093 // remap these for backwards compat
38094 onColWidthChange : function(){
38095 this.updateColumns.apply(this, arguments);
38097 onHeaderChange : function(){
38098 this.updateHeaders.apply(this, arguments);
38100 onHiddenChange : function(){
38101 this.handleHiddenChange.apply(this, arguments);
38103 onColumnMove : function(){
38104 this.handleColumnMove.apply(this, arguments);
38106 onColumnLock : function(){
38107 this.handleLockChange.apply(this, arguments);
38110 onDataChange : function(){
38112 this.updateHeaderSortState();
38115 onClear : function(){
38119 onUpdate : function(ds, record){
38120 this.refreshRow(record);
38123 refreshRow : function(record){
38124 var ds = this.ds, index;
38125 if(typeof record == 'number'){
38127 record = ds.getAt(index);
38129 index = ds.indexOf(record);
38131 this.insertRows(ds, index, index, true);
38132 this.onRemove(ds, record, index+1, true);
38133 this.syncRowHeights(index, index);
38135 this.fireEvent("rowupdated", this, index, record);
38138 onAdd : function(ds, records, index){
38139 this.insertRows(ds, index, index + (records.length-1));
38142 onRemove : function(ds, record, index, isUpdate){
38143 if(isUpdate !== true){
38144 this.fireEvent("beforerowremoved", this, index, record);
38146 var bt = this.getBodyTable(), lt = this.getLockedTable();
38147 if(bt.rows[index]){
38148 bt.firstChild.removeChild(bt.rows[index]);
38150 if(lt.rows[index]){
38151 lt.firstChild.removeChild(lt.rows[index]);
38153 if(isUpdate !== true){
38154 this.stripeRows(index);
38155 this.syncRowHeights(index, index);
38157 this.fireEvent("rowremoved", this, index, record);
38161 onLoad : function(){
38162 this.scrollToTop();
38166 * Scrolls the grid to the top
38168 scrollToTop : function(){
38170 this.scroller.dom.scrollTop = 0;
38176 * Gets a panel in the header of the grid that can be used for toolbars etc.
38177 * After modifying the contents of this panel a call to grid.autoSize() may be
38178 * required to register any changes in size.
38179 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
38180 * @return Roo.Element
38182 getHeaderPanel : function(doShow){
38184 this.headerPanel.show();
38186 return this.headerPanel;
38190 * Gets a panel in the footer of the grid that can be used for toolbars etc.
38191 * After modifying the contents of this panel a call to grid.autoSize() may be
38192 * required to register any changes in size.
38193 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
38194 * @return Roo.Element
38196 getFooterPanel : function(doShow){
38198 this.footerPanel.show();
38200 return this.footerPanel;
38203 initElements : function(){
38204 var E = Roo.Element;
38205 var el = this.grid.getGridEl().dom.firstChild;
38206 var cs = el.childNodes;
38208 this.el = new E(el);
38210 this.focusEl = new E(el.firstChild);
38211 this.focusEl.swallowEvent("click", true);
38213 this.headerPanel = new E(cs[1]);
38214 this.headerPanel.enableDisplayMode("block");
38216 this.scroller = new E(cs[2]);
38217 this.scrollSizer = new E(this.scroller.dom.firstChild);
38219 this.lockedWrap = new E(cs[3]);
38220 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38221 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38223 this.mainWrap = new E(cs[4]);
38224 this.mainHd = new E(this.mainWrap.dom.firstChild);
38225 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38227 this.footerPanel = new E(cs[5]);
38228 this.footerPanel.enableDisplayMode("block");
38230 this.resizeProxy = new E(cs[6]);
38232 this.headerSelector = String.format(
38233 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38234 this.lockedHd.id, this.mainHd.id
38237 this.splitterSelector = String.format(
38238 '#{0} div.x-grid-split, #{1} div.x-grid-split',
38239 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38242 idToCssName : function(s)
38244 return s.replace(/[^a-z0-9]+/ig, '-');
38247 getHeaderCell : function(index){
38248 return Roo.DomQuery.select(this.headerSelector)[index];
38251 getHeaderCellMeasure : function(index){
38252 return this.getHeaderCell(index).firstChild;
38255 getHeaderCellText : function(index){
38256 return this.getHeaderCell(index).firstChild.firstChild;
38259 getLockedTable : function(){
38260 return this.lockedBody.dom.firstChild;
38263 getBodyTable : function(){
38264 return this.mainBody.dom.firstChild;
38267 getLockedRow : function(index){
38268 return this.getLockedTable().rows[index];
38271 getRow : function(index){
38272 return this.getBodyTable().rows[index];
38275 getRowComposite : function(index){
38277 this.rowEl = new Roo.CompositeElementLite();
38279 var els = [], lrow, mrow;
38280 if(lrow = this.getLockedRow(index)){
38283 if(mrow = this.getRow(index)){
38286 this.rowEl.elements = els;
38290 * Gets the 'td' of the cell
38292 * @param {Integer} rowIndex row to select
38293 * @param {Integer} colIndex column to select
38297 getCell : function(rowIndex, colIndex){
38298 var locked = this.cm.getLockedCount();
38300 if(colIndex < locked){
38301 source = this.lockedBody.dom.firstChild;
38303 source = this.mainBody.dom.firstChild;
38304 colIndex -= locked;
38306 return source.rows[rowIndex].childNodes[colIndex];
38309 getCellText : function(rowIndex, colIndex){
38310 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38313 getCellBox : function(cell){
38314 var b = this.fly(cell).getBox();
38315 if(Roo.isOpera){ // opera fails to report the Y
38316 b.y = cell.offsetTop + this.mainBody.getY();
38321 getCellIndex : function(cell){
38322 var id = String(cell.className).match(this.cellRE);
38324 return parseInt(id[1], 10);
38329 findHeaderIndex : function(n){
38330 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38331 return r ? this.getCellIndex(r) : false;
38334 findHeaderCell : function(n){
38335 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38336 return r ? r : false;
38339 findRowIndex : function(n){
38343 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38344 return r ? r.rowIndex : false;
38347 findCellIndex : function(node){
38348 var stop = this.el.dom;
38349 while(node && node != stop){
38350 if(this.findRE.test(node.className)){
38351 return this.getCellIndex(node);
38353 node = node.parentNode;
38358 getColumnId : function(index){
38359 return this.cm.getColumnId(index);
38362 getSplitters : function()
38364 if(this.splitterSelector){
38365 return Roo.DomQuery.select(this.splitterSelector);
38371 getSplitter : function(index){
38372 return this.getSplitters()[index];
38375 onRowOver : function(e, t){
38377 if((row = this.findRowIndex(t)) !== false){
38378 this.getRowComposite(row).addClass("x-grid-row-over");
38382 onRowOut : function(e, t){
38384 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38385 this.getRowComposite(row).removeClass("x-grid-row-over");
38389 renderHeaders : function(){
38391 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38392 var cb = [], lb = [], sb = [], lsb = [], p = {};
38393 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38394 p.cellId = "x-grid-hd-0-" + i;
38395 p.splitId = "x-grid-csplit-0-" + i;
38396 p.id = cm.getColumnId(i);
38397 p.value = cm.getColumnHeader(i) || "";
38398 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
38399 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38400 if(!cm.isLocked(i)){
38401 cb[cb.length] = ct.apply(p);
38402 sb[sb.length] = st.apply(p);
38404 lb[lb.length] = ct.apply(p);
38405 lsb[lsb.length] = st.apply(p);
38408 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38409 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38412 updateHeaders : function(){
38413 var html = this.renderHeaders();
38414 this.lockedHd.update(html[0]);
38415 this.mainHd.update(html[1]);
38419 * Focuses the specified row.
38420 * @param {Number} row The row index
38422 focusRow : function(row)
38424 //Roo.log('GridView.focusRow');
38425 var x = this.scroller.dom.scrollLeft;
38426 this.focusCell(row, 0, false);
38427 this.scroller.dom.scrollLeft = x;
38431 * Focuses the specified cell.
38432 * @param {Number} row The row index
38433 * @param {Number} col The column index
38434 * @param {Boolean} hscroll false to disable horizontal scrolling
38436 focusCell : function(row, col, hscroll)
38438 //Roo.log('GridView.focusCell');
38439 var el = this.ensureVisible(row, col, hscroll);
38440 this.focusEl.alignTo(el, "tl-tl");
38442 this.focusEl.focus();
38444 this.focusEl.focus.defer(1, this.focusEl);
38449 * Scrolls the specified cell into view
38450 * @param {Number} row The row index
38451 * @param {Number} col The column index
38452 * @param {Boolean} hscroll false to disable horizontal scrolling
38454 ensureVisible : function(row, col, hscroll)
38456 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38457 //return null; //disable for testing.
38458 if(typeof row != "number"){
38459 row = row.rowIndex;
38461 if(row < 0 && row >= this.ds.getCount()){
38464 col = (col !== undefined ? col : 0);
38465 var cm = this.grid.colModel;
38466 while(cm.isHidden(col)){
38470 var el = this.getCell(row, col);
38474 var c = this.scroller.dom;
38476 var ctop = parseInt(el.offsetTop, 10);
38477 var cleft = parseInt(el.offsetLeft, 10);
38478 var cbot = ctop + el.offsetHeight;
38479 var cright = cleft + el.offsetWidth;
38481 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38482 var stop = parseInt(c.scrollTop, 10);
38483 var sleft = parseInt(c.scrollLeft, 10);
38484 var sbot = stop + ch;
38485 var sright = sleft + c.clientWidth;
38487 Roo.log('GridView.ensureVisible:' +
38489 ' c.clientHeight:' + c.clientHeight +
38490 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38498 c.scrollTop = ctop;
38499 //Roo.log("set scrolltop to ctop DISABLE?");
38500 }else if(cbot > sbot){
38501 //Roo.log("set scrolltop to cbot-ch");
38502 c.scrollTop = cbot-ch;
38505 if(hscroll !== false){
38507 c.scrollLeft = cleft;
38508 }else if(cright > sright){
38509 c.scrollLeft = cright-c.clientWidth;
38516 updateColumns : function(){
38517 this.grid.stopEditing();
38518 var cm = this.grid.colModel, colIds = this.getColumnIds();
38519 //var totalWidth = cm.getTotalWidth();
38521 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38522 //if(cm.isHidden(i)) continue;
38523 var w = cm.getColumnWidth(i);
38524 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38525 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38527 this.updateSplitters();
38530 generateRules : function(cm){
38531 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38532 Roo.util.CSS.removeStyleSheet(rulesId);
38533 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38534 var cid = cm.getColumnId(i);
38536 if(cm.config[i].align){
38537 align = 'text-align:'+cm.config[i].align+';';
38540 if(cm.isHidden(i)){
38541 hidden = 'display:none;';
38543 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38545 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38546 this.hdSelector, cid, " {\n", align, width, "}\n",
38547 this.tdSelector, cid, " {\n",hidden,"\n}\n",
38548 this.splitSelector, cid, " {\n", hidden , "\n}\n");
38550 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38553 updateSplitters : function(){
38554 var cm = this.cm, s = this.getSplitters();
38555 if(s){ // splitters not created yet
38556 var pos = 0, locked = true;
38557 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38558 if(cm.isHidden(i)) {
38561 var w = cm.getColumnWidth(i); // make sure it's a number
38562 if(!cm.isLocked(i) && locked){
38567 s[i].style.left = (pos-this.splitOffset) + "px";
38572 handleHiddenChange : function(colModel, colIndex, hidden){
38574 this.hideColumn(colIndex);
38576 this.unhideColumn(colIndex);
38580 hideColumn : function(colIndex){
38581 var cid = this.getColumnId(colIndex);
38582 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38583 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38585 this.updateHeaders();
38587 this.updateSplitters();
38591 unhideColumn : function(colIndex){
38592 var cid = this.getColumnId(colIndex);
38593 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38594 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38597 this.updateHeaders();
38599 this.updateSplitters();
38603 insertRows : function(dm, firstRow, lastRow, isUpdate){
38604 if(firstRow == 0 && lastRow == dm.getCount()-1){
38608 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38610 var s = this.getScrollState();
38611 var markup = this.renderRows(firstRow, lastRow);
38612 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38613 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38614 this.restoreScroll(s);
38616 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38617 this.syncRowHeights(firstRow, lastRow);
38618 this.stripeRows(firstRow);
38624 bufferRows : function(markup, target, index){
38625 var before = null, trows = target.rows, tbody = target.tBodies[0];
38626 if(index < trows.length){
38627 before = trows[index];
38629 var b = document.createElement("div");
38630 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38631 var rows = b.firstChild.rows;
38632 for(var i = 0, len = rows.length; i < len; i++){
38634 tbody.insertBefore(rows[0], before);
38636 tbody.appendChild(rows[0]);
38643 deleteRows : function(dm, firstRow, lastRow){
38644 if(dm.getRowCount()<1){
38645 this.fireEvent("beforerefresh", this);
38646 this.mainBody.update("");
38647 this.lockedBody.update("");
38648 this.fireEvent("refresh", this);
38650 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38651 var bt = this.getBodyTable();
38652 var tbody = bt.firstChild;
38653 var rows = bt.rows;
38654 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38655 tbody.removeChild(rows[firstRow]);
38657 this.stripeRows(firstRow);
38658 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38662 updateRows : function(dataSource, firstRow, lastRow){
38663 var s = this.getScrollState();
38665 this.restoreScroll(s);
38668 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38672 this.updateHeaderSortState();
38675 getScrollState : function(){
38677 var sb = this.scroller.dom;
38678 return {left: sb.scrollLeft, top: sb.scrollTop};
38681 stripeRows : function(startRow){
38682 if(!this.grid.stripeRows || this.ds.getCount() < 1){
38685 startRow = startRow || 0;
38686 var rows = this.getBodyTable().rows;
38687 var lrows = this.getLockedTable().rows;
38688 var cls = ' x-grid-row-alt ';
38689 for(var i = startRow, len = rows.length; i < len; i++){
38690 var row = rows[i], lrow = lrows[i];
38691 var isAlt = ((i+1) % 2 == 0);
38692 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38693 if(isAlt == hasAlt){
38697 row.className += " x-grid-row-alt";
38699 row.className = row.className.replace("x-grid-row-alt", "");
38702 lrow.className = row.className;
38707 restoreScroll : function(state){
38708 //Roo.log('GridView.restoreScroll');
38709 var sb = this.scroller.dom;
38710 sb.scrollLeft = state.left;
38711 sb.scrollTop = state.top;
38715 syncScroll : function(){
38716 //Roo.log('GridView.syncScroll');
38717 var sb = this.scroller.dom;
38718 var sh = this.mainHd.dom;
38719 var bs = this.mainBody.dom;
38720 var lv = this.lockedBody.dom;
38721 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38722 lv.scrollTop = bs.scrollTop = sb.scrollTop;
38725 handleScroll : function(e){
38727 var sb = this.scroller.dom;
38728 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38732 handleWheel : function(e){
38733 var d = e.getWheelDelta();
38734 this.scroller.dom.scrollTop -= d*22;
38735 // set this here to prevent jumpy scrolling on large tables
38736 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38740 renderRows : function(startRow, endRow){
38741 // pull in all the crap needed to render rows
38742 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38743 var colCount = cm.getColumnCount();
38745 if(ds.getCount() < 1){
38749 // build a map for all the columns
38751 for(var i = 0; i < colCount; i++){
38752 var name = cm.getDataIndex(i);
38754 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38755 renderer : cm.getRenderer(i),
38756 id : cm.getColumnId(i),
38757 locked : cm.isLocked(i),
38758 has_editor : cm.isCellEditable(i)
38762 startRow = startRow || 0;
38763 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38765 // records to render
38766 var rs = ds.getRange(startRow, endRow);
38768 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38771 // As much as I hate to duplicate code, this was branched because FireFox really hates
38772 // [].join("") on strings. The performance difference was substantial enough to
38773 // branch this function
38774 doRender : Roo.isGecko ?
38775 function(cs, rs, ds, startRow, colCount, stripe){
38776 var ts = this.templates, ct = ts.cell, rt = ts.row;
38778 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38780 var hasListener = this.grid.hasListener('rowclass');
38782 for(var j = 0, len = rs.length; j < len; j++){
38783 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38784 for(var i = 0; i < colCount; i++){
38786 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38788 p.css = p.attr = "";
38789 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38790 if(p.value == undefined || p.value === "") {
38791 p.value = " ";
38794 p.css += ' x-grid-editable-cell';
38796 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38797 p.css += ' x-grid-dirty-cell';
38799 var markup = ct.apply(p);
38807 if(stripe && ((rowIndex+1) % 2 == 0)){
38808 alt.push("x-grid-row-alt")
38811 alt.push( " x-grid-dirty-row");
38814 if(this.getRowClass){
38815 alt.push(this.getRowClass(r, rowIndex));
38821 rowIndex : rowIndex,
38824 this.grid.fireEvent('rowclass', this, rowcfg);
38825 alt.push(rowcfg.rowClass);
38827 rp.alt = alt.join(" ");
38828 lbuf+= rt.apply(rp);
38830 buf+= rt.apply(rp);
38832 return [lbuf, buf];
38834 function(cs, rs, ds, startRow, colCount, stripe){
38835 var ts = this.templates, ct = ts.cell, rt = ts.row;
38837 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38838 var hasListener = this.grid.hasListener('rowclass');
38841 for(var j = 0, len = rs.length; j < len; j++){
38842 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38843 for(var i = 0; i < colCount; i++){
38845 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38847 p.css = p.attr = "";
38848 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38849 if(p.value == undefined || p.value === "") {
38850 p.value = " ";
38854 p.css += ' x-grid-editable-cell';
38856 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38857 p.css += ' x-grid-dirty-cell'
38860 var markup = ct.apply(p);
38862 cb[cb.length] = markup;
38864 lcb[lcb.length] = markup;
38868 if(stripe && ((rowIndex+1) % 2 == 0)){
38869 alt.push( "x-grid-row-alt");
38872 alt.push(" x-grid-dirty-row");
38875 if(this.getRowClass){
38876 alt.push( this.getRowClass(r, rowIndex));
38882 rowIndex : rowIndex,
38885 this.grid.fireEvent('rowclass', this, rowcfg);
38886 alt.push(rowcfg.rowClass);
38889 rp.alt = alt.join(" ");
38890 rp.cells = lcb.join("");
38891 lbuf[lbuf.length] = rt.apply(rp);
38892 rp.cells = cb.join("");
38893 buf[buf.length] = rt.apply(rp);
38895 return [lbuf.join(""), buf.join("")];
38898 renderBody : function(){
38899 var markup = this.renderRows();
38900 var bt = this.templates.body;
38901 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38905 * Refreshes the grid
38906 * @param {Boolean} headersToo
38908 refresh : function(headersToo){
38909 this.fireEvent("beforerefresh", this);
38910 this.grid.stopEditing();
38911 var result = this.renderBody();
38912 this.lockedBody.update(result[0]);
38913 this.mainBody.update(result[1]);
38914 if(headersToo === true){
38915 this.updateHeaders();
38916 this.updateColumns();
38917 this.updateSplitters();
38918 this.updateHeaderSortState();
38920 this.syncRowHeights();
38922 this.fireEvent("refresh", this);
38925 handleColumnMove : function(cm, oldIndex, newIndex){
38926 this.indexMap = null;
38927 var s = this.getScrollState();
38928 this.refresh(true);
38929 this.restoreScroll(s);
38930 this.afterMove(newIndex);
38933 afterMove : function(colIndex){
38934 if(this.enableMoveAnim && Roo.enableFx){
38935 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38937 // if multisort - fix sortOrder, and reload..
38938 if (this.grid.dataSource.multiSort) {
38939 // the we can call sort again..
38940 var dm = this.grid.dataSource;
38941 var cm = this.grid.colModel;
38943 for(var i = 0; i < cm.config.length; i++ ) {
38945 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38946 continue; // dont' bother, it's not in sort list or being set.
38949 so.push(cm.config[i].dataIndex);
38952 dm.load(dm.lastOptions);
38959 updateCell : function(dm, rowIndex, dataIndex){
38960 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38961 if(typeof colIndex == "undefined"){ // not present in grid
38964 var cm = this.grid.colModel;
38965 var cell = this.getCell(rowIndex, colIndex);
38966 var cellText = this.getCellText(rowIndex, colIndex);
38969 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38970 id : cm.getColumnId(colIndex),
38971 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38973 var renderer = cm.getRenderer(colIndex);
38974 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38975 if(typeof val == "undefined" || val === "") {
38978 cellText.innerHTML = val;
38979 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38980 this.syncRowHeights(rowIndex, rowIndex);
38983 calcColumnWidth : function(colIndex, maxRowsToMeasure){
38985 if(this.grid.autoSizeHeaders){
38986 var h = this.getHeaderCellMeasure(colIndex);
38987 maxWidth = Math.max(maxWidth, h.scrollWidth);
38990 if(this.cm.isLocked(colIndex)){
38991 tb = this.getLockedTable();
38994 tb = this.getBodyTable();
38995 index = colIndex - this.cm.getLockedCount();
38998 var rows = tb.rows;
38999 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
39000 for(var i = 0; i < stopIndex; i++){
39001 var cell = rows[i].childNodes[index].firstChild;
39002 maxWidth = Math.max(maxWidth, cell.scrollWidth);
39005 return maxWidth + /*margin for error in IE*/ 5;
39008 * Autofit a column to its content.
39009 * @param {Number} colIndex
39010 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
39012 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
39013 if(this.cm.isHidden(colIndex)){
39014 return; // can't calc a hidden column
39017 var cid = this.cm.getColumnId(colIndex);
39018 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
39019 if(this.grid.autoSizeHeaders){
39020 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
39023 var newWidth = this.calcColumnWidth(colIndex);
39024 this.cm.setColumnWidth(colIndex,
39025 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
39026 if(!suppressEvent){
39027 this.grid.fireEvent("columnresize", colIndex, newWidth);
39032 * Autofits all columns to their content and then expands to fit any extra space in the grid
39034 autoSizeColumns : function(){
39035 var cm = this.grid.colModel;
39036 var colCount = cm.getColumnCount();
39037 for(var i = 0; i < colCount; i++){
39038 this.autoSizeColumn(i, true, true);
39040 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
39043 this.updateColumns();
39049 * Autofits all columns to the grid's width proportionate with their current size
39050 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
39052 fitColumns : function(reserveScrollSpace){
39053 var cm = this.grid.colModel;
39054 var colCount = cm.getColumnCount();
39058 for (i = 0; i < colCount; i++){
39059 if(!cm.isHidden(i) && !cm.isFixed(i)){
39060 w = cm.getColumnWidth(i);
39066 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
39067 if(reserveScrollSpace){
39070 var frac = (avail - cm.getTotalWidth())/width;
39071 while (cols.length){
39074 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
39076 this.updateColumns();
39080 onRowSelect : function(rowIndex){
39081 var row = this.getRowComposite(rowIndex);
39082 row.addClass("x-grid-row-selected");
39085 onRowDeselect : function(rowIndex){
39086 var row = this.getRowComposite(rowIndex);
39087 row.removeClass("x-grid-row-selected");
39090 onCellSelect : function(row, col){
39091 var cell = this.getCell(row, col);
39093 Roo.fly(cell).addClass("x-grid-cell-selected");
39097 onCellDeselect : function(row, col){
39098 var cell = this.getCell(row, col);
39100 Roo.fly(cell).removeClass("x-grid-cell-selected");
39104 updateHeaderSortState : function(){
39106 // sort state can be single { field: xxx, direction : yyy}
39107 // or { xxx=>ASC , yyy : DESC ..... }
39110 if (!this.ds.multiSort) {
39111 var state = this.ds.getSortState();
39115 mstate[state.field] = state.direction;
39116 // FIXME... - this is not used here.. but might be elsewhere..
39117 this.sortState = state;
39120 mstate = this.ds.sortToggle;
39122 //remove existing sort classes..
39124 var sc = this.sortClasses;
39125 var hds = this.el.select(this.headerSelector).removeClass(sc);
39127 for(var f in mstate) {
39129 var sortColumn = this.cm.findColumnIndex(f);
39131 if(sortColumn != -1){
39132 var sortDir = mstate[f];
39133 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
39142 handleHeaderClick : function(g, index,e){
39144 Roo.log("header click");
39147 // touch events on header are handled by context
39148 this.handleHdCtx(g,index,e);
39153 if(this.headersDisabled){
39156 var dm = g.dataSource, cm = g.colModel;
39157 if(!cm.isSortable(index)){
39162 if (dm.multiSort) {
39163 // update the sortOrder
39165 for(var i = 0; i < cm.config.length; i++ ) {
39167 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
39168 continue; // dont' bother, it's not in sort list or being set.
39171 so.push(cm.config[i].dataIndex);
39177 dm.sort(cm.getDataIndex(index));
39181 destroy : function(){
39183 this.colMenu.removeAll();
39184 Roo.menu.MenuMgr.unregister(this.colMenu);
39185 this.colMenu.getEl().remove();
39186 delete this.colMenu;
39189 this.hmenu.removeAll();
39190 Roo.menu.MenuMgr.unregister(this.hmenu);
39191 this.hmenu.getEl().remove();
39194 if(this.grid.enableColumnMove){
39195 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39197 for(var dd in dds){
39198 if(!dds[dd].config.isTarget && dds[dd].dragElId){
39199 var elid = dds[dd].dragElId;
39201 Roo.get(elid).remove();
39202 } else if(dds[dd].config.isTarget){
39203 dds[dd].proxyTop.remove();
39204 dds[dd].proxyBottom.remove();
39207 if(Roo.dd.DDM.locationCache[dd]){
39208 delete Roo.dd.DDM.locationCache[dd];
39211 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39214 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39215 this.bind(null, null);
39216 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39219 handleLockChange : function(){
39220 this.refresh(true);
39223 onDenyColumnLock : function(){
39227 onDenyColumnHide : function(){
39231 handleHdMenuClick : function(item){
39232 var index = this.hdCtxIndex;
39233 var cm = this.cm, ds = this.ds;
39236 ds.sort(cm.getDataIndex(index), "ASC");
39239 ds.sort(cm.getDataIndex(index), "DESC");
39242 var lc = cm.getLockedCount();
39243 if(cm.getColumnCount(true) <= lc+1){
39244 this.onDenyColumnLock();
39248 cm.setLocked(index, true, true);
39249 cm.moveColumn(index, lc);
39250 this.grid.fireEvent("columnmove", index, lc);
39252 cm.setLocked(index, true);
39256 var lc = cm.getLockedCount();
39257 if((lc-1) != index){
39258 cm.setLocked(index, false, true);
39259 cm.moveColumn(index, lc-1);
39260 this.grid.fireEvent("columnmove", index, lc-1);
39262 cm.setLocked(index, false);
39265 case 'wider': // used to expand cols on touch..
39267 var cw = cm.getColumnWidth(index);
39268 cw += (item.id == 'wider' ? 1 : -1) * 50;
39269 cw = Math.max(0, cw);
39270 cw = Math.min(cw,4000);
39271 cm.setColumnWidth(index, cw);
39275 index = cm.getIndexById(item.id.substr(4));
39277 if(item.checked && cm.getColumnCount(true) <= 1){
39278 this.onDenyColumnHide();
39281 cm.setHidden(index, item.checked);
39287 beforeColMenuShow : function(){
39288 var cm = this.cm, colCount = cm.getColumnCount();
39289 this.colMenu.removeAll();
39292 for(var i = 0; i < colCount; i++){
39294 id: "col-"+cm.getColumnId(i),
39295 text: cm.getColumnHeader(i),
39296 checked: !cm.isHidden(i),
39301 if (this.grid.sortColMenu) {
39302 items.sort(function(a,b) {
39303 if (a.text == b.text) {
39306 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39310 for(var i = 0; i < colCount; i++){
39311 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39315 handleHdCtx : function(g, index, e){
39317 var hd = this.getHeaderCell(index);
39318 this.hdCtxIndex = index;
39319 var ms = this.hmenu.items, cm = this.cm;
39320 ms.get("asc").setDisabled(!cm.isSortable(index));
39321 ms.get("desc").setDisabled(!cm.isSortable(index));
39322 if(this.grid.enableColLock !== false){
39323 ms.get("lock").setDisabled(cm.isLocked(index));
39324 ms.get("unlock").setDisabled(!cm.isLocked(index));
39326 this.hmenu.show(hd, "tl-bl");
39329 handleHdOver : function(e){
39330 var hd = this.findHeaderCell(e.getTarget());
39331 if(hd && !this.headersDisabled){
39332 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39333 this.fly(hd).addClass("x-grid-hd-over");
39338 handleHdOut : function(e){
39339 var hd = this.findHeaderCell(e.getTarget());
39341 this.fly(hd).removeClass("x-grid-hd-over");
39345 handleSplitDblClick : function(e, t){
39346 var i = this.getCellIndex(t);
39347 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39348 this.autoSizeColumn(i, true);
39353 render : function(){
39356 var colCount = cm.getColumnCount();
39358 if(this.grid.monitorWindowResize === true){
39359 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39361 var header = this.renderHeaders();
39362 var body = this.templates.body.apply({rows:""});
39363 var html = this.templates.master.apply({
39366 lockedHeader: header[0],
39370 //this.updateColumns();
39372 this.grid.getGridEl().dom.innerHTML = html;
39374 this.initElements();
39376 // a kludge to fix the random scolling effect in webkit
39377 this.el.on("scroll", function() {
39378 this.el.dom.scrollTop=0; // hopefully not recursive..
39381 this.scroller.on("scroll", this.handleScroll, this);
39382 this.lockedBody.on("mousewheel", this.handleWheel, this);
39383 this.mainBody.on("mousewheel", this.handleWheel, this);
39385 this.mainHd.on("mouseover", this.handleHdOver, this);
39386 this.mainHd.on("mouseout", this.handleHdOut, this);
39387 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39388 {delegate: "."+this.splitClass});
39390 this.lockedHd.on("mouseover", this.handleHdOver, this);
39391 this.lockedHd.on("mouseout", this.handleHdOut, this);
39392 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39393 {delegate: "."+this.splitClass});
39395 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39396 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39399 this.updateSplitters();
39401 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39402 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39403 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39406 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39407 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39409 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39410 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39412 if(this.grid.enableColLock !== false){
39413 this.hmenu.add('-',
39414 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39415 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39419 this.hmenu.add('-',
39420 {id:"wider", text: this.columnsWiderText},
39421 {id:"narrow", text: this.columnsNarrowText }
39427 if(this.grid.enableColumnHide !== false){
39429 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39430 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39431 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39433 this.hmenu.add('-',
39434 {id:"columns", text: this.columnsText, menu: this.colMenu}
39437 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39439 this.grid.on("headercontextmenu", this.handleHdCtx, this);
39442 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39443 this.dd = new Roo.grid.GridDragZone(this.grid, {
39444 ddGroup : this.grid.ddGroup || 'GridDD'
39450 for(var i = 0; i < colCount; i++){
39451 if(cm.isHidden(i)){
39452 this.hideColumn(i);
39454 if(cm.config[i].align){
39455 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39456 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39460 this.updateHeaderSortState();
39462 this.beforeInitialResize();
39465 // two part rendering gives faster view to the user
39466 this.renderPhase2.defer(1, this);
39469 renderPhase2 : function(){
39470 // render the rows now
39472 if(this.grid.autoSizeColumns){
39473 this.autoSizeColumns();
39477 beforeInitialResize : function(){
39481 onColumnSplitterMoved : function(i, w){
39482 this.userResized = true;
39483 var cm = this.grid.colModel;
39484 cm.setColumnWidth(i, w, true);
39485 var cid = cm.getColumnId(i);
39486 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39487 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39488 this.updateSplitters();
39490 this.grid.fireEvent("columnresize", i, w);
39493 syncRowHeights : function(startIndex, endIndex){
39494 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39495 startIndex = startIndex || 0;
39496 var mrows = this.getBodyTable().rows;
39497 var lrows = this.getLockedTable().rows;
39498 var len = mrows.length-1;
39499 endIndex = Math.min(endIndex || len, len);
39500 for(var i = startIndex; i <= endIndex; i++){
39501 var m = mrows[i], l = lrows[i];
39502 var h = Math.max(m.offsetHeight, l.offsetHeight);
39503 m.style.height = l.style.height = h + "px";
39508 layout : function(initialRender, is2ndPass)
39511 var auto = g.autoHeight;
39512 var scrollOffset = 16;
39513 var c = g.getGridEl(), cm = this.cm,
39514 expandCol = g.autoExpandColumn,
39516 //c.beginMeasure();
39518 if(!c.dom.offsetWidth){ // display:none?
39520 this.lockedWrap.show();
39521 this.mainWrap.show();
39526 var hasLock = this.cm.isLocked(0);
39528 var tbh = this.headerPanel.getHeight();
39529 var bbh = this.footerPanel.getHeight();
39532 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39533 var newHeight = ch + c.getBorderWidth("tb");
39535 newHeight = Math.min(g.maxHeight, newHeight);
39537 c.setHeight(newHeight);
39541 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39544 var s = this.scroller;
39546 var csize = c.getSize(true);
39548 this.el.setSize(csize.width, csize.height);
39550 this.headerPanel.setWidth(csize.width);
39551 this.footerPanel.setWidth(csize.width);
39553 var hdHeight = this.mainHd.getHeight();
39554 var vw = csize.width;
39555 var vh = csize.height - (tbh + bbh);
39559 var bt = this.getBodyTable();
39561 if(cm.getLockedCount() == cm.config.length){
39562 bt = this.getLockedTable();
39565 var ltWidth = hasLock ?
39566 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39568 var scrollHeight = bt.offsetHeight;
39569 var scrollWidth = ltWidth + bt.offsetWidth;
39570 var vscroll = false, hscroll = false;
39572 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39574 var lw = this.lockedWrap, mw = this.mainWrap;
39575 var lb = this.lockedBody, mb = this.mainBody;
39577 setTimeout(function(){
39578 var t = s.dom.offsetTop;
39579 var w = s.dom.clientWidth,
39580 h = s.dom.clientHeight;
39583 lw.setSize(ltWidth, h);
39585 mw.setLeftTop(ltWidth, t);
39586 mw.setSize(w-ltWidth, h);
39588 lb.setHeight(h-hdHeight);
39589 mb.setHeight(h-hdHeight);
39591 if(is2ndPass !== true && !gv.userResized && expandCol){
39592 // high speed resize without full column calculation
39594 var ci = cm.getIndexById(expandCol);
39596 ci = cm.findColumnIndex(expandCol);
39598 ci = Math.max(0, ci); // make sure it's got at least the first col.
39599 var expandId = cm.getColumnId(ci);
39600 var tw = cm.getTotalWidth(false);
39601 var currentWidth = cm.getColumnWidth(ci);
39602 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39603 if(currentWidth != cw){
39604 cm.setColumnWidth(ci, cw, true);
39605 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39606 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39607 gv.updateSplitters();
39608 gv.layout(false, true);
39620 onWindowResize : function(){
39621 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39627 appendFooter : function(parentEl){
39631 sortAscText : "Sort Ascending",
39632 sortDescText : "Sort Descending",
39633 lockText : "Lock Column",
39634 unlockText : "Unlock Column",
39635 columnsText : "Columns",
39637 columnsWiderText : "Wider",
39638 columnsNarrowText : "Thinner"
39642 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39643 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39644 this.proxy.el.addClass('x-grid3-col-dd');
39647 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39648 handleMouseDown : function(e){
39652 callHandleMouseDown : function(e){
39653 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39658 * Ext JS Library 1.1.1
39659 * Copyright(c) 2006-2007, Ext JS, LLC.
39661 * Originally Released Under LGPL - original licence link has changed is not relivant.
39664 * <script type="text/javascript">
39667 * @extends Roo.dd.DDProxy
39668 * @class Roo.grid.SplitDragZone
39669 * Support for Column Header resizing
39671 * @param {Object} config
39674 // This is a support class used internally by the Grid components
39675 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39677 this.view = grid.getView();
39678 this.proxy = this.view.resizeProxy;
39679 Roo.grid.SplitDragZone.superclass.constructor.call(
39682 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39684 dragElId : Roo.id(this.proxy.dom),
39689 this.setHandleElId(Roo.id(hd));
39690 if (hd2 !== false) {
39691 this.setOuterHandleElId(Roo.id(hd2));
39694 this.scroll = false;
39696 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39697 fly: Roo.Element.fly,
39699 b4StartDrag : function(x, y){
39700 this.view.headersDisabled = true;
39701 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39702 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39704 this.proxy.setHeight(h);
39706 // for old system colWidth really stored the actual width?
39707 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39708 // which in reality did not work.. - it worked only for fixed sizes
39709 // for resizable we need to use actual sizes.
39710 var w = this.cm.getColumnWidth(this.cellIndex);
39711 if (!this.view.mainWrap) {
39713 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39718 // this was w-this.grid.minColumnWidth;
39719 // doesnt really make sense? - w = thie curren width or the rendered one?
39720 var minw = Math.max(w-this.grid.minColumnWidth, 0);
39721 this.resetConstraints();
39722 this.setXConstraint(minw, 1000);
39723 this.setYConstraint(0, 0);
39724 this.minX = x - minw;
39725 this.maxX = x + 1000;
39727 if (!this.view.mainWrap) { // this is Bootstrap code..
39728 this.getDragEl().style.display='block';
39731 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39735 handleMouseDown : function(e){
39736 ev = Roo.EventObject.setEvent(e);
39737 var t = this.fly(ev.getTarget());
39738 if(t.hasClass("x-grid-split")){
39739 this.cellIndex = this.view.getCellIndex(t.dom);
39740 this.split = t.dom;
39741 this.cm = this.grid.colModel;
39742 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39743 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39748 endDrag : function(e){
39749 this.view.headersDisabled = false;
39750 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39751 var diff = endX - this.startPos;
39753 var w = this.cm.getColumnWidth(this.cellIndex);
39754 if (!this.view.mainWrap) {
39757 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39760 autoOffset : function(){
39761 this.setDelta(0,0);
39765 * Ext JS Library 1.1.1
39766 * Copyright(c) 2006-2007, Ext JS, LLC.
39768 * Originally Released Under LGPL - original licence link has changed is not relivant.
39771 * <script type="text/javascript">
39775 // This is a support class used internally by the Grid components
39776 Roo.grid.GridDragZone = function(grid, config){
39777 this.view = grid.getView();
39778 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39779 if(this.view.lockedBody){
39780 this.setHandleElId(Roo.id(this.view.mainBody.dom));
39781 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39783 this.scroll = false;
39785 this.ddel = document.createElement('div');
39786 this.ddel.className = 'x-grid-dd-wrap';
39789 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39790 ddGroup : "GridDD",
39792 getDragData : function(e){
39793 var t = Roo.lib.Event.getTarget(e);
39794 var rowIndex = this.view.findRowIndex(t);
39795 var sm = this.grid.selModel;
39797 //Roo.log(rowIndex);
39799 if (sm.getSelectedCell) {
39800 // cell selection..
39801 if (!sm.getSelectedCell()) {
39804 if (rowIndex != sm.getSelectedCell()[0]) {
39809 if (sm.getSelections && sm.getSelections().length < 1) {
39814 // before it used to all dragging of unseleted... - now we dont do that.
39815 if(rowIndex !== false){
39820 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39822 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39825 if (e.hasModifier()){
39826 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39829 Roo.log("getDragData");
39834 rowIndex: rowIndex,
39835 selections: sm.getSelections ? sm.getSelections() : (
39836 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39843 onInitDrag : function(e){
39844 var data = this.dragData;
39845 this.ddel.innerHTML = this.grid.getDragDropText();
39846 this.proxy.update(this.ddel);
39847 // fire start drag?
39850 afterRepair : function(){
39851 this.dragging = false;
39854 getRepairXY : function(e, data){
39858 onEndDrag : function(data, e){
39862 onValidDrop : function(dd, e, id){
39867 beforeInvalidDrop : function(e, id){
39872 * Ext JS Library 1.1.1
39873 * Copyright(c) 2006-2007, Ext JS, LLC.
39875 * Originally Released Under LGPL - original licence link has changed is not relivant.
39878 * <script type="text/javascript">
39883 * @class Roo.grid.ColumnModel
39884 * @extends Roo.util.Observable
39885 * This is the default implementation of a ColumnModel used by the Grid. It defines
39886 * the columns in the grid.
39889 var colModel = new Roo.grid.ColumnModel([
39890 {header: "Ticker", width: 60, sortable: true, locked: true},
39891 {header: "Company Name", width: 150, sortable: true},
39892 {header: "Market Cap.", width: 100, sortable: true},
39893 {header: "$ Sales", width: 100, sortable: true, renderer: money},
39894 {header: "Employees", width: 100, sortable: true, resizable: false}
39899 * The config options listed for this class are options which may appear in each
39900 * individual column definition.
39901 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39903 * @param {Object} config An Array of column config objects. See this class's
39904 * config objects for details.
39906 Roo.grid.ColumnModel = function(config){
39908 * The config passed into the constructor
39910 this.config = []; //config;
39913 // if no id, create one
39914 // if the column does not have a dataIndex mapping,
39915 // map it to the order it is in the config
39916 for(var i = 0, len = config.length; i < len; i++){
39917 this.addColumn(config[i]);
39922 * The width of columns which have no width specified (defaults to 100)
39925 this.defaultWidth = 100;
39928 * Default sortable of columns which have no sortable specified (defaults to false)
39931 this.defaultSortable = false;
39935 * @event widthchange
39936 * Fires when the width of a column changes.
39937 * @param {ColumnModel} this
39938 * @param {Number} columnIndex The column index
39939 * @param {Number} newWidth The new width
39941 "widthchange": true,
39943 * @event headerchange
39944 * Fires when the text of a header changes.
39945 * @param {ColumnModel} this
39946 * @param {Number} columnIndex The column index
39947 * @param {Number} newText The new header text
39949 "headerchange": true,
39951 * @event hiddenchange
39952 * Fires when a column is hidden or "unhidden".
39953 * @param {ColumnModel} this
39954 * @param {Number} columnIndex The column index
39955 * @param {Boolean} hidden true if hidden, false otherwise
39957 "hiddenchange": true,
39959 * @event columnmoved
39960 * Fires when a column is moved.
39961 * @param {ColumnModel} this
39962 * @param {Number} oldIndex
39963 * @param {Number} newIndex
39965 "columnmoved" : true,
39967 * @event columlockchange
39968 * Fires when a column's locked state is changed
39969 * @param {ColumnModel} this
39970 * @param {Number} colIndex
39971 * @param {Boolean} locked true if locked
39973 "columnlockchange" : true
39975 Roo.grid.ColumnModel.superclass.constructor.call(this);
39977 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39979 * @cfg {String} header [required] The header text to display in the Grid view.
39982 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39985 * @cfg {String} smHeader Header at Bootsrap Small width
39988 * @cfg {String} mdHeader Header at Bootsrap Medium width
39991 * @cfg {String} lgHeader Header at Bootsrap Large width
39994 * @cfg {String} xlHeader Header at Bootsrap extra Large width
39997 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
39998 * {@link Roo.data.Record} definition from which to draw the column's value. If not
39999 * specified, the column's index is used as an index into the Record's data Array.
40002 * @cfg {Number} width The initial width in pixels of the column. Using this
40003 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
40006 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
40007 * Defaults to the value of the {@link #defaultSortable} property.
40008 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
40011 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
40014 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
40017 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
40020 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
40023 * @cfg {Function} renderer A function used to generate HTML markup for a cell
40024 * given the cell's data value. See {@link #setRenderer}. If not specified, the
40025 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
40026 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
40029 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
40032 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
40035 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
40038 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
40041 * @cfg {String} tooltip mouse over tooltip text
40044 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
40047 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
40050 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
40053 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
40056 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
40059 * Returns the id of the column at the specified index.
40060 * @param {Number} index The column index
40061 * @return {String} the id
40063 getColumnId : function(index){
40064 return this.config[index].id;
40068 * Returns the column for a specified id.
40069 * @param {String} id The column id
40070 * @return {Object} the column
40072 getColumnById : function(id){
40073 return this.lookup[id];
40078 * Returns the column Object for a specified dataIndex.
40079 * @param {String} dataIndex The column dataIndex
40080 * @return {Object|Boolean} the column or false if not found
40082 getColumnByDataIndex: function(dataIndex){
40083 var index = this.findColumnIndex(dataIndex);
40084 return index > -1 ? this.config[index] : false;
40088 * Returns the index for a specified column id.
40089 * @param {String} id The column id
40090 * @return {Number} the index, or -1 if not found
40092 getIndexById : function(id){
40093 for(var i = 0, len = this.config.length; i < len; i++){
40094 if(this.config[i].id == id){
40102 * Returns the index for a specified column dataIndex.
40103 * @param {String} dataIndex The column dataIndex
40104 * @return {Number} the index, or -1 if not found
40107 findColumnIndex : function(dataIndex){
40108 for(var i = 0, len = this.config.length; i < len; i++){
40109 if(this.config[i].dataIndex == dataIndex){
40117 moveColumn : function(oldIndex, newIndex){
40118 var c = this.config[oldIndex];
40119 this.config.splice(oldIndex, 1);
40120 this.config.splice(newIndex, 0, c);
40121 this.dataMap = null;
40122 this.fireEvent("columnmoved", this, oldIndex, newIndex);
40125 isLocked : function(colIndex){
40126 return this.config[colIndex].locked === true;
40129 setLocked : function(colIndex, value, suppressEvent){
40130 if(this.isLocked(colIndex) == value){
40133 this.config[colIndex].locked = value;
40134 if(!suppressEvent){
40135 this.fireEvent("columnlockchange", this, colIndex, value);
40139 getTotalLockedWidth : function(){
40140 var totalWidth = 0;
40141 for(var i = 0; i < this.config.length; i++){
40142 if(this.isLocked(i) && !this.isHidden(i)){
40143 this.totalWidth += this.getColumnWidth(i);
40149 getLockedCount : function(){
40150 for(var i = 0, len = this.config.length; i < len; i++){
40151 if(!this.isLocked(i)){
40156 return this.config.length;
40160 * Returns the number of columns.
40163 getColumnCount : function(visibleOnly){
40164 if(visibleOnly === true){
40166 for(var i = 0, len = this.config.length; i < len; i++){
40167 if(!this.isHidden(i)){
40173 return this.config.length;
40177 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
40178 * @param {Function} fn
40179 * @param {Object} scope (optional)
40180 * @return {Array} result
40182 getColumnsBy : function(fn, scope){
40184 for(var i = 0, len = this.config.length; i < len; i++){
40185 var c = this.config[i];
40186 if(fn.call(scope||this, c, i) === true){
40194 * Returns true if the specified column is sortable.
40195 * @param {Number} col The column index
40196 * @return {Boolean}
40198 isSortable : function(col){
40199 if(typeof this.config[col].sortable == "undefined"){
40200 return this.defaultSortable;
40202 return this.config[col].sortable;
40206 * Returns the rendering (formatting) function defined for the column.
40207 * @param {Number} col The column index.
40208 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40210 getRenderer : function(col){
40211 if(!this.config[col].renderer){
40212 return Roo.grid.ColumnModel.defaultRenderer;
40214 return this.config[col].renderer;
40218 * Sets the rendering (formatting) function for a column.
40219 * @param {Number} col The column index
40220 * @param {Function} fn The function to use to process the cell's raw data
40221 * to return HTML markup for the grid view. The render function is called with
40222 * the following parameters:<ul>
40223 * <li>Data value.</li>
40224 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40225 * <li>css A CSS style string to apply to the table cell.</li>
40226 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40227 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40228 * <li>Row index</li>
40229 * <li>Column index</li>
40230 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40232 setRenderer : function(col, fn){
40233 this.config[col].renderer = fn;
40237 * Returns the width for the specified column.
40238 * @param {Number} col The column index
40239 * @param (optional) {String} gridSize bootstrap width size.
40242 getColumnWidth : function(col, gridSize)
40244 var cfg = this.config[col];
40246 if (typeof(gridSize) == 'undefined') {
40247 return cfg.width * 1 || this.defaultWidth;
40249 if (gridSize === false) { // if we set it..
40250 return cfg.width || false;
40252 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40254 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40255 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40258 return cfg[ sizes[i] ];
40265 * Sets the width for a column.
40266 * @param {Number} col The column index
40267 * @param {Number} width The new width
40269 setColumnWidth : function(col, width, suppressEvent){
40270 this.config[col].width = width;
40271 this.totalWidth = null;
40272 if(!suppressEvent){
40273 this.fireEvent("widthchange", this, col, width);
40278 * Returns the total width of all columns.
40279 * @param {Boolean} includeHidden True to include hidden column widths
40282 getTotalWidth : function(includeHidden){
40283 if(!this.totalWidth){
40284 this.totalWidth = 0;
40285 for(var i = 0, len = this.config.length; i < len; i++){
40286 if(includeHidden || !this.isHidden(i)){
40287 this.totalWidth += this.getColumnWidth(i);
40291 return this.totalWidth;
40295 * Returns the header for the specified column.
40296 * @param {Number} col The column index
40299 getColumnHeader : function(col){
40300 return this.config[col].header;
40304 * Sets the header for a column.
40305 * @param {Number} col The column index
40306 * @param {String} header The new header
40308 setColumnHeader : function(col, header){
40309 this.config[col].header = header;
40310 this.fireEvent("headerchange", this, col, header);
40314 * Returns the tooltip for the specified column.
40315 * @param {Number} col The column index
40318 getColumnTooltip : function(col){
40319 return this.config[col].tooltip;
40322 * Sets the tooltip for a column.
40323 * @param {Number} col The column index
40324 * @param {String} tooltip The new tooltip
40326 setColumnTooltip : function(col, tooltip){
40327 this.config[col].tooltip = tooltip;
40331 * Returns the dataIndex for the specified column.
40332 * @param {Number} col The column index
40335 getDataIndex : function(col){
40336 return this.config[col].dataIndex;
40340 * Sets the dataIndex for a column.
40341 * @param {Number} col The column index
40342 * @param {Number} dataIndex The new dataIndex
40344 setDataIndex : function(col, dataIndex){
40345 this.config[col].dataIndex = dataIndex;
40351 * Returns true if the cell is editable.
40352 * @param {Number} colIndex The column index
40353 * @param {Number} rowIndex The row index - this is nto actually used..?
40354 * @return {Boolean}
40356 isCellEditable : function(colIndex, rowIndex){
40357 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40361 * Returns the editor defined for the cell/column.
40362 * return false or null to disable editing.
40363 * @param {Number} colIndex The column index
40364 * @param {Number} rowIndex The row index
40367 getCellEditor : function(colIndex, rowIndex){
40368 return this.config[colIndex].editor;
40372 * Sets if a column is editable.
40373 * @param {Number} col The column index
40374 * @param {Boolean} editable True if the column is editable
40376 setEditable : function(col, editable){
40377 this.config[col].editable = editable;
40382 * Returns true if the column is hidden.
40383 * @param {Number} colIndex The column index
40384 * @return {Boolean}
40386 isHidden : function(colIndex){
40387 return this.config[colIndex].hidden;
40392 * Returns true if the column width cannot be changed
40394 isFixed : function(colIndex){
40395 return this.config[colIndex].fixed;
40399 * Returns true if the column can be resized
40400 * @return {Boolean}
40402 isResizable : function(colIndex){
40403 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40406 * Sets if a column is hidden.
40407 * @param {Number} colIndex The column index
40408 * @param {Boolean} hidden True if the column is hidden
40410 setHidden : function(colIndex, hidden){
40411 this.config[colIndex].hidden = hidden;
40412 this.totalWidth = null;
40413 this.fireEvent("hiddenchange", this, colIndex, hidden);
40417 * Sets the editor for a column.
40418 * @param {Number} col The column index
40419 * @param {Object} editor The editor object
40421 setEditor : function(col, editor){
40422 this.config[col].editor = editor;
40425 * Add a column (experimental...) - defaults to adding to the end..
40426 * @param {Object} config
40428 addColumn : function(c)
40431 var i = this.config.length;
40432 this.config[i] = c;
40434 if(typeof c.dataIndex == "undefined"){
40437 if(typeof c.renderer == "string"){
40438 c.renderer = Roo.util.Format[c.renderer];
40440 if(typeof c.id == "undefined"){
40443 if(c.editor && c.editor.xtype){
40444 c.editor = Roo.factory(c.editor, Roo.grid);
40446 if(c.editor && c.editor.isFormField){
40447 c.editor = new Roo.grid.GridEditor(c.editor);
40449 this.lookup[c.id] = c;
40454 Roo.grid.ColumnModel.defaultRenderer = function(value)
40456 if(typeof value == "object") {
40459 if(typeof value == "string" && value.length < 1){
40463 return String.format("{0}", value);
40466 // Alias for backwards compatibility
40467 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40470 * Ext JS Library 1.1.1
40471 * Copyright(c) 2006-2007, Ext JS, LLC.
40473 * Originally Released Under LGPL - original licence link has changed is not relivant.
40476 * <script type="text/javascript">
40480 * @class Roo.grid.AbstractSelectionModel
40481 * @extends Roo.util.Observable
40483 * Abstract base class for grid SelectionModels. It provides the interface that should be
40484 * implemented by descendant classes. This class should not be directly instantiated.
40487 Roo.grid.AbstractSelectionModel = function(){
40488 this.locked = false;
40489 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40492 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
40493 /** @ignore Called by the grid automatically. Do not call directly. */
40494 init : function(grid){
40500 * Locks the selections.
40503 this.locked = true;
40507 * Unlocks the selections.
40509 unlock : function(){
40510 this.locked = false;
40514 * Returns true if the selections are locked.
40515 * @return {Boolean}
40517 isLocked : function(){
40518 return this.locked;
40522 * Ext JS Library 1.1.1
40523 * Copyright(c) 2006-2007, Ext JS, LLC.
40525 * Originally Released Under LGPL - original licence link has changed is not relivant.
40528 * <script type="text/javascript">
40531 * @extends Roo.grid.AbstractSelectionModel
40532 * @class Roo.grid.RowSelectionModel
40533 * The default SelectionModel used by {@link Roo.grid.Grid}.
40534 * It supports multiple selections and keyboard selection/navigation.
40536 * @param {Object} config
40538 Roo.grid.RowSelectionModel = function(config){
40539 Roo.apply(this, config);
40540 this.selections = new Roo.util.MixedCollection(false, function(o){
40545 this.lastActive = false;
40549 * @event selectionchange
40550 * Fires when the selection changes
40551 * @param {SelectionModel} this
40553 "selectionchange" : true,
40555 * @event afterselectionchange
40556 * Fires after the selection changes (eg. by key press or clicking)
40557 * @param {SelectionModel} this
40559 "afterselectionchange" : true,
40561 * @event beforerowselect
40562 * Fires when a row is selected being selected, return false to cancel.
40563 * @param {SelectionModel} this
40564 * @param {Number} rowIndex The selected index
40565 * @param {Boolean} keepExisting False if other selections will be cleared
40567 "beforerowselect" : true,
40570 * Fires when a row is selected.
40571 * @param {SelectionModel} this
40572 * @param {Number} rowIndex The selected index
40573 * @param {Roo.data.Record} r The record
40575 "rowselect" : true,
40577 * @event rowdeselect
40578 * Fires when a row is deselected.
40579 * @param {SelectionModel} this
40580 * @param {Number} rowIndex The selected index
40582 "rowdeselect" : true
40584 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40585 this.locked = false;
40588 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
40590 * @cfg {Boolean} singleSelect
40591 * True to allow selection of only one row at a time (defaults to false)
40593 singleSelect : false,
40596 initEvents : function(){
40598 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40599 this.grid.on("mousedown", this.handleMouseDown, this);
40600 }else{ // allow click to work like normal
40601 this.grid.on("rowclick", this.handleDragableRowClick, this);
40603 // bootstrap does not have a view..
40604 var view = this.grid.view ? this.grid.view : this.grid;
40605 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40606 "up" : function(e){
40608 this.selectPrevious(e.shiftKey);
40609 }else if(this.last !== false && this.lastActive !== false){
40610 var last = this.last;
40611 this.selectRange(this.last, this.lastActive-1);
40612 view.focusRow(this.lastActive);
40613 if(last !== false){
40617 this.selectFirstRow();
40619 this.fireEvent("afterselectionchange", this);
40621 "down" : function(e){
40623 this.selectNext(e.shiftKey);
40624 }else if(this.last !== false && this.lastActive !== false){
40625 var last = this.last;
40626 this.selectRange(this.last, this.lastActive+1);
40627 view.focusRow(this.lastActive);
40628 if(last !== false){
40632 this.selectFirstRow();
40634 this.fireEvent("afterselectionchange", this);
40640 view.on("refresh", this.onRefresh, this);
40641 view.on("rowupdated", this.onRowUpdated, this);
40642 view.on("rowremoved", this.onRemove, this);
40646 onRefresh : function(){
40647 var ds = this.grid.ds, i, v = this.grid.view;
40648 var s = this.selections;
40649 s.each(function(r){
40650 if((i = ds.indexOfId(r.id)) != -1){
40652 s.add(ds.getAt(i)); // updating the selection relate data
40660 onRemove : function(v, index, r){
40661 this.selections.remove(r);
40665 onRowUpdated : function(v, index, r){
40666 if(this.isSelected(r)){
40667 v.onRowSelect(index);
40673 * @param {Array} records The records to select
40674 * @param {Boolean} keepExisting (optional) True to keep existing selections
40676 selectRecords : function(records, keepExisting){
40678 this.clearSelections();
40680 var ds = this.grid.ds;
40681 for(var i = 0, len = records.length; i < len; i++){
40682 this.selectRow(ds.indexOf(records[i]), true);
40687 * Gets the number of selected rows.
40690 getCount : function(){
40691 return this.selections.length;
40695 * Selects the first row in the grid.
40697 selectFirstRow : function(){
40702 * Select the last row.
40703 * @param {Boolean} keepExisting (optional) True to keep existing selections
40705 selectLastRow : function(keepExisting){
40706 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40710 * Selects the row immediately following the last selected row.
40711 * @param {Boolean} keepExisting (optional) True to keep existing selections
40713 selectNext : function(keepExisting){
40714 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40715 this.selectRow(this.last+1, keepExisting);
40716 var view = this.grid.view ? this.grid.view : this.grid;
40717 view.focusRow(this.last);
40722 * Selects the row that precedes the last selected row.
40723 * @param {Boolean} keepExisting (optional) True to keep existing selections
40725 selectPrevious : function(keepExisting){
40727 this.selectRow(this.last-1, keepExisting);
40728 var view = this.grid.view ? this.grid.view : this.grid;
40729 view.focusRow(this.last);
40734 * Returns the selected records
40735 * @return {Array} Array of selected records
40737 getSelections : function(){
40738 return [].concat(this.selections.items);
40742 * Returns the first selected record.
40745 getSelected : function(){
40746 return this.selections.itemAt(0);
40751 * Clears all selections.
40753 clearSelections : function(fast){
40758 var ds = this.grid.ds;
40759 var s = this.selections;
40760 s.each(function(r){
40761 this.deselectRow(ds.indexOfId(r.id));
40765 this.selections.clear();
40772 * Selects all rows.
40774 selectAll : function(){
40778 this.selections.clear();
40779 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40780 this.selectRow(i, true);
40785 * Returns True if there is a selection.
40786 * @return {Boolean}
40788 hasSelection : function(){
40789 return this.selections.length > 0;
40793 * Returns True if the specified row is selected.
40794 * @param {Number/Record} record The record or index of the record to check
40795 * @return {Boolean}
40797 isSelected : function(index){
40798 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40799 return (r && this.selections.key(r.id) ? true : false);
40803 * Returns True if the specified record id is selected.
40804 * @param {String} id The id of record to check
40805 * @return {Boolean}
40807 isIdSelected : function(id){
40808 return (this.selections.key(id) ? true : false);
40812 handleMouseDown : function(e, t)
40814 var view = this.grid.view ? this.grid.view : this.grid;
40816 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40819 if(e.shiftKey && this.last !== false){
40820 var last = this.last;
40821 this.selectRange(last, rowIndex, e.ctrlKey);
40822 this.last = last; // reset the last
40823 view.focusRow(rowIndex);
40825 var isSelected = this.isSelected(rowIndex);
40826 if(e.button !== 0 && isSelected){
40827 view.focusRow(rowIndex);
40828 }else if(e.ctrlKey && isSelected){
40829 this.deselectRow(rowIndex);
40830 }else if(!isSelected){
40831 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40832 view.focusRow(rowIndex);
40835 this.fireEvent("afterselectionchange", this);
40838 handleDragableRowClick : function(grid, rowIndex, e)
40840 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40841 this.selectRow(rowIndex, false);
40842 var view = this.grid.view ? this.grid.view : this.grid;
40843 view.focusRow(rowIndex);
40844 this.fireEvent("afterselectionchange", this);
40849 * Selects multiple rows.
40850 * @param {Array} rows Array of the indexes of the row to select
40851 * @param {Boolean} keepExisting (optional) True to keep existing selections
40853 selectRows : function(rows, keepExisting){
40855 this.clearSelections();
40857 for(var i = 0, len = rows.length; i < len; i++){
40858 this.selectRow(rows[i], true);
40863 * Selects a range of rows. All rows in between startRow and endRow are also selected.
40864 * @param {Number} startRow The index of the first row in the range
40865 * @param {Number} endRow The index of the last row in the range
40866 * @param {Boolean} keepExisting (optional) True to retain existing selections
40868 selectRange : function(startRow, endRow, keepExisting){
40873 this.clearSelections();
40875 if(startRow <= endRow){
40876 for(var i = startRow; i <= endRow; i++){
40877 this.selectRow(i, true);
40880 for(var i = startRow; i >= endRow; i--){
40881 this.selectRow(i, true);
40887 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40888 * @param {Number} startRow The index of the first row in the range
40889 * @param {Number} endRow The index of the last row in the range
40891 deselectRange : function(startRow, endRow, preventViewNotify){
40895 for(var i = startRow; i <= endRow; i++){
40896 this.deselectRow(i, preventViewNotify);
40902 * @param {Number} row The index of the row to select
40903 * @param {Boolean} keepExisting (optional) True to keep existing selections
40905 selectRow : function(index, keepExisting, preventViewNotify){
40906 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40909 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40910 if(!keepExisting || this.singleSelect){
40911 this.clearSelections();
40913 var r = this.grid.ds.getAt(index);
40914 this.selections.add(r);
40915 this.last = this.lastActive = index;
40916 if(!preventViewNotify){
40917 var view = this.grid.view ? this.grid.view : this.grid;
40918 view.onRowSelect(index);
40920 this.fireEvent("rowselect", this, index, r);
40921 this.fireEvent("selectionchange", this);
40927 * @param {Number} row The index of the row to deselect
40929 deselectRow : function(index, preventViewNotify){
40933 if(this.last == index){
40936 if(this.lastActive == index){
40937 this.lastActive = false;
40939 var r = this.grid.ds.getAt(index);
40940 this.selections.remove(r);
40941 if(!preventViewNotify){
40942 var view = this.grid.view ? this.grid.view : this.grid;
40943 view.onRowDeselect(index);
40945 this.fireEvent("rowdeselect", this, index);
40946 this.fireEvent("selectionchange", this);
40950 restoreLast : function(){
40952 this.last = this._last;
40957 acceptsNav : function(row, col, cm){
40958 return !cm.isHidden(col) && cm.isCellEditable(col, row);
40962 onEditorKey : function(field, e){
40963 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40968 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40970 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40972 }else if(k == e.ENTER && !e.ctrlKey){
40976 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40978 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40980 }else if(k == e.ESC){
40984 g.startEditing(newCell[0], newCell[1]);
40989 * Ext JS Library 1.1.1
40990 * Copyright(c) 2006-2007, Ext JS, LLC.
40992 * Originally Released Under LGPL - original licence link has changed is not relivant.
40995 * <script type="text/javascript">
40998 * @class Roo.grid.CellSelectionModel
40999 * @extends Roo.grid.AbstractSelectionModel
41000 * This class provides the basic implementation for cell selection in a grid.
41002 * @param {Object} config The object containing the configuration of this model.
41003 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
41005 Roo.grid.CellSelectionModel = function(config){
41006 Roo.apply(this, config);
41008 this.selection = null;
41012 * @event beforerowselect
41013 * Fires before a cell is selected.
41014 * @param {SelectionModel} this
41015 * @param {Number} rowIndex The selected row index
41016 * @param {Number} colIndex The selected cell index
41018 "beforecellselect" : true,
41020 * @event cellselect
41021 * Fires when a cell is selected.
41022 * @param {SelectionModel} this
41023 * @param {Number} rowIndex The selected row index
41024 * @param {Number} colIndex The selected cell index
41026 "cellselect" : true,
41028 * @event selectionchange
41029 * Fires when the active selection changes.
41030 * @param {SelectionModel} this
41031 * @param {Object} selection null for no selection or an object (o) with two properties
41033 <li>o.record: the record object for the row the selection is in</li>
41034 <li>o.cell: An array of [rowIndex, columnIndex]</li>
41037 "selectionchange" : true,
41040 * Fires when the tab (or enter) was pressed on the last editable cell
41041 * You can use this to trigger add new row.
41042 * @param {SelectionModel} this
41046 * @event beforeeditnext
41047 * Fires before the next editable sell is made active
41048 * You can use this to skip to another cell or fire the tabend
41049 * if you set cell to false
41050 * @param {Object} eventdata object : { cell : [ row, col ] }
41052 "beforeeditnext" : true
41054 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
41057 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
41059 enter_is_tab: false,
41062 initEvents : function(){
41063 this.grid.on("mousedown", this.handleMouseDown, this);
41064 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
41065 var view = this.grid.view;
41066 view.on("refresh", this.onViewChange, this);
41067 view.on("rowupdated", this.onRowUpdated, this);
41068 view.on("beforerowremoved", this.clearSelections, this);
41069 view.on("beforerowsinserted", this.clearSelections, this);
41070 if(this.grid.isEditor){
41071 this.grid.on("beforeedit", this.beforeEdit, this);
41076 beforeEdit : function(e){
41077 this.select(e.row, e.column, false, true, e.record);
41081 onRowUpdated : function(v, index, r){
41082 if(this.selection && this.selection.record == r){
41083 v.onCellSelect(index, this.selection.cell[1]);
41088 onViewChange : function(){
41089 this.clearSelections(true);
41093 * Returns the currently selected cell,.
41094 * @return {Array} The selected cell (row, column) or null if none selected.
41096 getSelectedCell : function(){
41097 return this.selection ? this.selection.cell : null;
41101 * Clears all selections.
41102 * @param {Boolean} true to prevent the gridview from being notified about the change.
41104 clearSelections : function(preventNotify){
41105 var s = this.selection;
41107 if(preventNotify !== true){
41108 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
41110 this.selection = null;
41111 this.fireEvent("selectionchange", this, null);
41116 * Returns true if there is a selection.
41117 * @return {Boolean}
41119 hasSelection : function(){
41120 return this.selection ? true : false;
41124 handleMouseDown : function(e, t){
41125 var v = this.grid.getView();
41126 if(this.isLocked()){
41129 var row = v.findRowIndex(t);
41130 var cell = v.findCellIndex(t);
41131 if(row !== false && cell !== false){
41132 this.select(row, cell);
41138 * @param {Number} rowIndex
41139 * @param {Number} collIndex
41141 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
41142 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
41143 this.clearSelections();
41144 r = r || this.grid.dataSource.getAt(rowIndex);
41147 cell : [rowIndex, colIndex]
41149 if(!preventViewNotify){
41150 var v = this.grid.getView();
41151 v.onCellSelect(rowIndex, colIndex);
41152 if(preventFocus !== true){
41153 v.focusCell(rowIndex, colIndex);
41156 this.fireEvent("cellselect", this, rowIndex, colIndex);
41157 this.fireEvent("selectionchange", this, this.selection);
41162 isSelectable : function(rowIndex, colIndex, cm){
41163 return !cm.isHidden(colIndex);
41167 handleKeyDown : function(e){
41168 //Roo.log('Cell Sel Model handleKeyDown');
41169 if(!e.isNavKeyPress()){
41172 var g = this.grid, s = this.selection;
41175 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
41177 this.select(cell[0], cell[1]);
41182 var walk = function(row, col, step){
41183 return g.walkCells(row, col, step, sm.isSelectable, sm);
41185 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
41192 // handled by onEditorKey
41193 if (g.isEditor && g.editing) {
41197 newCell = walk(r, c-1, -1);
41199 newCell = walk(r, c+1, 1);
41204 newCell = walk(r+1, c, 1);
41208 newCell = walk(r-1, c, -1);
41212 newCell = walk(r, c+1, 1);
41216 newCell = walk(r, c-1, -1);
41221 if(g.isEditor && !g.editing){
41222 g.startEditing(r, c);
41231 this.select(newCell[0], newCell[1]);
41237 acceptsNav : function(row, col, cm){
41238 return !cm.isHidden(col) && cm.isCellEditable(col, row);
41242 * @param {Number} field (not used) - as it's normally used as a listener
41243 * @param {Number} e - event - fake it by using
41245 * var e = Roo.EventObjectImpl.prototype;
41246 * e.keyCode = e.TAB
41250 onEditorKey : function(field, e){
41252 var k = e.getKey(),
41255 ed = g.activeEditor,
41257 ///Roo.log('onEditorKey' + k);
41260 if (this.enter_is_tab && k == e.ENTER) {
41266 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41268 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41274 } else if(k == e.ENTER && !e.ctrlKey){
41277 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41279 } else if(k == e.ESC){
41284 var ecall = { cell : newCell, forward : forward };
41285 this.fireEvent('beforeeditnext', ecall );
41286 newCell = ecall.cell;
41287 forward = ecall.forward;
41291 //Roo.log('next cell after edit');
41292 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41293 } else if (forward) {
41294 // tabbed past last
41295 this.fireEvent.defer(100, this, ['tabend',this]);
41300 * Ext JS Library 1.1.1
41301 * Copyright(c) 2006-2007, Ext JS, LLC.
41303 * Originally Released Under LGPL - original licence link has changed is not relivant.
41306 * <script type="text/javascript">
41310 * @class Roo.grid.EditorGrid
41311 * @extends Roo.grid.Grid
41312 * Class for creating and editable grid.
41313 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41314 * The container MUST have some type of size defined for the grid to fill. The container will be
41315 * automatically set to position relative if it isn't already.
41316 * @param {Object} dataSource The data model to bind to
41317 * @param {Object} colModel The column model with info about this grid's columns
41319 Roo.grid.EditorGrid = function(container, config){
41320 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41321 this.getGridEl().addClass("xedit-grid");
41323 if(!this.selModel){
41324 this.selModel = new Roo.grid.CellSelectionModel();
41327 this.activeEditor = null;
41331 * @event beforeedit
41332 * Fires before cell editing is triggered. The edit event object has the following properties <br />
41333 * <ul style="padding:5px;padding-left:16px;">
41334 * <li>grid - This grid</li>
41335 * <li>record - The record being edited</li>
41336 * <li>field - The field name being edited</li>
41337 * <li>value - The value for the field being edited.</li>
41338 * <li>row - The grid row index</li>
41339 * <li>column - The grid column index</li>
41340 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41342 * @param {Object} e An edit event (see above for description)
41344 "beforeedit" : true,
41347 * Fires after a cell is edited. <br />
41348 * <ul style="padding:5px;padding-left:16px;">
41349 * <li>grid - This grid</li>
41350 * <li>record - The record being edited</li>
41351 * <li>field - The field name being edited</li>
41352 * <li>value - The value being set</li>
41353 * <li>originalValue - The original value for the field, before the edit.</li>
41354 * <li>row - The grid row index</li>
41355 * <li>column - The grid column index</li>
41357 * @param {Object} e An edit event (see above for description)
41359 "afteredit" : true,
41361 * @event validateedit
41362 * Fires after a cell is edited, but before the value is set in the record.
41363 * You can use this to modify the value being set in the field, Return false
41364 * to cancel the change. The edit event object has the following properties <br />
41365 * <ul style="padding:5px;padding-left:16px;">
41366 * <li>editor - This editor</li>
41367 * <li>grid - This grid</li>
41368 * <li>record - The record being edited</li>
41369 * <li>field - The field name being edited</li>
41370 * <li>value - The value being set</li>
41371 * <li>originalValue - The original value for the field, before the edit.</li>
41372 * <li>row - The grid row index</li>
41373 * <li>column - The grid column index</li>
41374 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41376 * @param {Object} e An edit event (see above for description)
41378 "validateedit" : true
41380 this.on("bodyscroll", this.stopEditing, this);
41381 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
41384 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41386 * @cfg {Number} clicksToEdit
41387 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41394 trackMouseOver: false, // causes very odd FF errors
41396 onCellDblClick : function(g, row, col){
41397 this.startEditing(row, col);
41400 onEditComplete : function(ed, value, startValue){
41401 this.editing = false;
41402 this.activeEditor = null;
41403 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41405 var field = this.colModel.getDataIndex(ed.col);
41410 originalValue: startValue,
41417 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41420 if(String(value) !== String(startValue)){
41422 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41423 r.set(field, e.value);
41424 // if we are dealing with a combo box..
41425 // then we also set the 'name' colum to be the displayField
41426 if (ed.field.displayField && ed.field.name) {
41427 r.set(ed.field.name, ed.field.el.dom.value);
41430 delete e.cancel; //?? why!!!
41431 this.fireEvent("afteredit", e);
41434 this.fireEvent("afteredit", e); // always fire it!
41436 this.view.focusCell(ed.row, ed.col);
41440 * Starts editing the specified for the specified row/column
41441 * @param {Number} rowIndex
41442 * @param {Number} colIndex
41444 startEditing : function(row, col){
41445 this.stopEditing();
41446 if(this.colModel.isCellEditable(col, row)){
41447 this.view.ensureVisible(row, col, true);
41449 var r = this.dataSource.getAt(row);
41450 var field = this.colModel.getDataIndex(col);
41451 var cell = Roo.get(this.view.getCell(row,col));
41456 value: r.data[field],
41461 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41462 this.editing = true;
41463 var ed = this.colModel.getCellEditor(col, row);
41469 ed.render(ed.parentEl || document.body);
41475 (function(){ // complex but required for focus issues in safari, ie and opera
41479 ed.on("complete", this.onEditComplete, this, {single: true});
41480 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
41481 this.activeEditor = ed;
41482 var v = r.data[field];
41483 ed.startEdit(this.view.getCell(row, col), v);
41484 // combo's with 'displayField and name set
41485 if (ed.field.displayField && ed.field.name) {
41486 ed.field.el.dom.value = r.data[ed.field.name];
41490 }).defer(50, this);
41496 * Stops any active editing
41498 stopEditing : function(){
41499 if(this.activeEditor){
41500 this.activeEditor.completeEdit();
41502 this.activeEditor = null;
41506 * Called to get grid's drag proxy text, by default returns this.ddText.
41509 getDragDropText : function(){
41510 var count = this.selModel.getSelectedCell() ? 1 : 0;
41511 return String.format(this.ddText, count, count == 1 ? '' : 's');
41516 * Ext JS Library 1.1.1
41517 * Copyright(c) 2006-2007, Ext JS, LLC.
41519 * Originally Released Under LGPL - original licence link has changed is not relivant.
41522 * <script type="text/javascript">
41525 // private - not really -- you end up using it !
41526 // This is a support class used internally by the Grid components
41529 * @class Roo.grid.GridEditor
41530 * @extends Roo.Editor
41531 * Class for creating and editable grid elements.
41532 * @param {Object} config any settings (must include field)
41534 Roo.grid.GridEditor = function(field, config){
41535 if (!config && field.field) {
41537 field = Roo.factory(config.field, Roo.form);
41539 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41540 field.monitorTab = false;
41543 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41546 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41549 alignment: "tl-tl",
41552 cls: "x-small-editor x-grid-editor",
41557 * Ext JS Library 1.1.1
41558 * Copyright(c) 2006-2007, Ext JS, LLC.
41560 * Originally Released Under LGPL - original licence link has changed is not relivant.
41563 * <script type="text/javascript">
41568 Roo.grid.PropertyRecord = Roo.data.Record.create([
41569 {name:'name',type:'string'}, 'value'
41573 Roo.grid.PropertyStore = function(grid, source){
41575 this.store = new Roo.data.Store({
41576 recordType : Roo.grid.PropertyRecord
41578 this.store.on('update', this.onUpdate, this);
41580 this.setSource(source);
41582 Roo.grid.PropertyStore.superclass.constructor.call(this);
41587 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41588 setSource : function(o){
41590 this.store.removeAll();
41593 if(this.isEditableValue(o[k])){
41594 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41597 this.store.loadRecords({records: data}, {}, true);
41600 onUpdate : function(ds, record, type){
41601 if(type == Roo.data.Record.EDIT){
41602 var v = record.data['value'];
41603 var oldValue = record.modified['value'];
41604 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41605 this.source[record.id] = v;
41607 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41614 getProperty : function(row){
41615 return this.store.getAt(row);
41618 isEditableValue: function(val){
41619 if(val && val instanceof Date){
41621 }else if(typeof val == 'object' || typeof val == 'function'){
41627 setValue : function(prop, value){
41628 this.source[prop] = value;
41629 this.store.getById(prop).set('value', value);
41632 getSource : function(){
41633 return this.source;
41637 Roo.grid.PropertyColumnModel = function(grid, store){
41640 g.PropertyColumnModel.superclass.constructor.call(this, [
41641 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41642 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41644 this.store = store;
41645 this.bselect = Roo.DomHelper.append(document.body, {
41646 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41647 {tag: 'option', value: 'true', html: 'true'},
41648 {tag: 'option', value: 'false', html: 'false'}
41651 Roo.id(this.bselect);
41654 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41655 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41656 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41657 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41658 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41660 this.renderCellDelegate = this.renderCell.createDelegate(this);
41661 this.renderPropDelegate = this.renderProp.createDelegate(this);
41664 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41668 valueText : 'Value',
41670 dateFormat : 'm/j/Y',
41673 renderDate : function(dateVal){
41674 return dateVal.dateFormat(this.dateFormat);
41677 renderBool : function(bVal){
41678 return bVal ? 'true' : 'false';
41681 isCellEditable : function(colIndex, rowIndex){
41682 return colIndex == 1;
41685 getRenderer : function(col){
41687 this.renderCellDelegate : this.renderPropDelegate;
41690 renderProp : function(v){
41691 return this.getPropertyName(v);
41694 renderCell : function(val){
41696 if(val instanceof Date){
41697 rv = this.renderDate(val);
41698 }else if(typeof val == 'boolean'){
41699 rv = this.renderBool(val);
41701 return Roo.util.Format.htmlEncode(rv);
41704 getPropertyName : function(name){
41705 var pn = this.grid.propertyNames;
41706 return pn && pn[name] ? pn[name] : name;
41709 getCellEditor : function(colIndex, rowIndex){
41710 var p = this.store.getProperty(rowIndex);
41711 var n = p.data['name'], val = p.data['value'];
41713 if(typeof(this.grid.customEditors[n]) == 'string'){
41714 return this.editors[this.grid.customEditors[n]];
41716 if(typeof(this.grid.customEditors[n]) != 'undefined'){
41717 return this.grid.customEditors[n];
41719 if(val instanceof Date){
41720 return this.editors['date'];
41721 }else if(typeof val == 'number'){
41722 return this.editors['number'];
41723 }else if(typeof val == 'boolean'){
41724 return this.editors['boolean'];
41726 return this.editors['string'];
41732 * @class Roo.grid.PropertyGrid
41733 * @extends Roo.grid.EditorGrid
41734 * This class represents the interface of a component based property grid control.
41735 * <br><br>Usage:<pre><code>
41736 var grid = new Roo.grid.PropertyGrid("my-container-id", {
41744 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41745 * The container MUST have some type of size defined for the grid to fill. The container will be
41746 * automatically set to position relative if it isn't already.
41747 * @param {Object} config A config object that sets properties on this grid.
41749 Roo.grid.PropertyGrid = function(container, config){
41750 config = config || {};
41751 var store = new Roo.grid.PropertyStore(this);
41752 this.store = store;
41753 var cm = new Roo.grid.PropertyColumnModel(this, store);
41754 store.store.sort('name', 'ASC');
41755 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41758 enableColLock:false,
41759 enableColumnMove:false,
41761 trackMouseOver: false,
41764 this.getGridEl().addClass('x-props-grid');
41765 this.lastEditRow = null;
41766 this.on('columnresize', this.onColumnResize, this);
41769 * @event beforepropertychange
41770 * Fires before a property changes (return false to stop?)
41771 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41772 * @param {String} id Record Id
41773 * @param {String} newval New Value
41774 * @param {String} oldval Old Value
41776 "beforepropertychange": true,
41778 * @event propertychange
41779 * Fires after a property changes
41780 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41781 * @param {String} id Record Id
41782 * @param {String} newval New Value
41783 * @param {String} oldval Old Value
41785 "propertychange": true
41787 this.customEditors = this.customEditors || {};
41789 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41792 * @cfg {Object} customEditors map of colnames=> custom editors.
41793 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41794 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41795 * false disables editing of the field.
41799 * @cfg {Object} propertyNames map of property Names to their displayed value
41802 render : function(){
41803 Roo.grid.PropertyGrid.superclass.render.call(this);
41804 this.autoSize.defer(100, this);
41807 autoSize : function(){
41808 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41810 this.view.fitColumns();
41814 onColumnResize : function(){
41815 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41819 * Sets the data for the Grid
41820 * accepts a Key => Value object of all the elements avaiable.
41821 * @param {Object} data to appear in grid.
41823 setSource : function(source){
41824 this.store.setSource(source);
41828 * Gets all the data from the grid.
41829 * @return {Object} data data stored in grid
41831 getSource : function(){
41832 return this.store.getSource();
41841 * @class Roo.grid.Calendar
41842 * @extends Roo.grid.Grid
41843 * This class extends the Grid to provide a calendar widget
41844 * <br><br>Usage:<pre><code>
41845 var grid = new Roo.grid.Calendar("my-container-id", {
41848 selModel: mySelectionModel,
41849 autoSizeColumns: true,
41850 monitorWindowResize: false,
41851 trackMouseOver: true
41852 eventstore : real data store..
41858 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41859 * The container MUST have some type of size defined for the grid to fill. The container will be
41860 * automatically set to position relative if it isn't already.
41861 * @param {Object} config A config object that sets properties on this grid.
41863 Roo.grid.Calendar = function(container, config){
41864 // initialize the container
41865 this.container = Roo.get(container);
41866 this.container.update("");
41867 this.container.setStyle("overflow", "hidden");
41868 this.container.addClass('x-grid-container');
41870 this.id = this.container.id;
41872 Roo.apply(this, config);
41873 // check and correct shorthanded configs
41877 for (var r = 0;r < 6;r++) {
41880 for (var c =0;c < 7;c++) {
41884 if (this.eventStore) {
41885 this.eventStore= Roo.factory(this.eventStore, Roo.data);
41886 this.eventStore.on('load',this.onLoad, this);
41887 this.eventStore.on('beforeload',this.clearEvents, this);
41891 this.dataSource = new Roo.data.Store({
41892 proxy: new Roo.data.MemoryProxy(rows),
41893 reader: new Roo.data.ArrayReader({}, [
41894 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41897 this.dataSource.load();
41898 this.ds = this.dataSource;
41899 this.ds.xmodule = this.xmodule || false;
41902 var cellRender = function(v,x,r)
41904 return String.format(
41905 '<div class="fc-day fc-widget-content"><div>' +
41906 '<div class="fc-event-container"></div>' +
41907 '<div class="fc-day-number">{0}</div>'+
41909 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41910 '</div></div>', v);
41915 this.colModel = new Roo.grid.ColumnModel( [
41917 xtype: 'ColumnModel',
41919 dataIndex : 'weekday0',
41921 renderer : cellRender
41924 xtype: 'ColumnModel',
41926 dataIndex : 'weekday1',
41928 renderer : cellRender
41931 xtype: 'ColumnModel',
41933 dataIndex : 'weekday2',
41934 header : 'Tuesday',
41935 renderer : cellRender
41938 xtype: 'ColumnModel',
41940 dataIndex : 'weekday3',
41941 header : 'Wednesday',
41942 renderer : cellRender
41945 xtype: 'ColumnModel',
41947 dataIndex : 'weekday4',
41948 header : 'Thursday',
41949 renderer : cellRender
41952 xtype: 'ColumnModel',
41954 dataIndex : 'weekday5',
41956 renderer : cellRender
41959 xtype: 'ColumnModel',
41961 dataIndex : 'weekday6',
41962 header : 'Saturday',
41963 renderer : cellRender
41966 this.cm = this.colModel;
41967 this.cm.xmodule = this.xmodule || false;
41971 //this.selModel = new Roo.grid.CellSelectionModel();
41972 //this.sm = this.selModel;
41973 //this.selModel.init(this);
41977 this.container.setWidth(this.width);
41981 this.container.setHeight(this.height);
41988 * The raw click event for the entire grid.
41989 * @param {Roo.EventObject} e
41994 * The raw dblclick event for the entire grid.
41995 * @param {Roo.EventObject} e
41999 * @event contextmenu
42000 * The raw contextmenu event for the entire grid.
42001 * @param {Roo.EventObject} e
42003 "contextmenu" : true,
42006 * The raw mousedown event for the entire grid.
42007 * @param {Roo.EventObject} e
42009 "mousedown" : true,
42012 * The raw mouseup event for the entire grid.
42013 * @param {Roo.EventObject} e
42018 * The raw mouseover event for the entire grid.
42019 * @param {Roo.EventObject} e
42021 "mouseover" : true,
42024 * The raw mouseout event for the entire grid.
42025 * @param {Roo.EventObject} e
42030 * The raw keypress event for the entire grid.
42031 * @param {Roo.EventObject} e
42036 * The raw keydown event for the entire grid.
42037 * @param {Roo.EventObject} e
42045 * Fires when a cell is clicked
42046 * @param {Grid} this
42047 * @param {Number} rowIndex
42048 * @param {Number} columnIndex
42049 * @param {Roo.EventObject} e
42051 "cellclick" : true,
42053 * @event celldblclick
42054 * Fires when a cell is double clicked
42055 * @param {Grid} this
42056 * @param {Number} rowIndex
42057 * @param {Number} columnIndex
42058 * @param {Roo.EventObject} e
42060 "celldblclick" : true,
42063 * Fires when a row is clicked
42064 * @param {Grid} this
42065 * @param {Number} rowIndex
42066 * @param {Roo.EventObject} e
42070 * @event rowdblclick
42071 * Fires when a row is double clicked
42072 * @param {Grid} this
42073 * @param {Number} rowIndex
42074 * @param {Roo.EventObject} e
42076 "rowdblclick" : true,
42078 * @event headerclick
42079 * Fires when a header is clicked
42080 * @param {Grid} this
42081 * @param {Number} columnIndex
42082 * @param {Roo.EventObject} e
42084 "headerclick" : true,
42086 * @event headerdblclick
42087 * Fires when a header cell is double clicked
42088 * @param {Grid} this
42089 * @param {Number} columnIndex
42090 * @param {Roo.EventObject} e
42092 "headerdblclick" : true,
42094 * @event rowcontextmenu
42095 * Fires when a row is right clicked
42096 * @param {Grid} this
42097 * @param {Number} rowIndex
42098 * @param {Roo.EventObject} e
42100 "rowcontextmenu" : true,
42102 * @event cellcontextmenu
42103 * Fires when a cell is right clicked
42104 * @param {Grid} this
42105 * @param {Number} rowIndex
42106 * @param {Number} cellIndex
42107 * @param {Roo.EventObject} e
42109 "cellcontextmenu" : true,
42111 * @event headercontextmenu
42112 * Fires when a header is right clicked
42113 * @param {Grid} this
42114 * @param {Number} columnIndex
42115 * @param {Roo.EventObject} e
42117 "headercontextmenu" : true,
42119 * @event bodyscroll
42120 * Fires when the body element is scrolled
42121 * @param {Number} scrollLeft
42122 * @param {Number} scrollTop
42124 "bodyscroll" : true,
42126 * @event columnresize
42127 * Fires when the user resizes a column
42128 * @param {Number} columnIndex
42129 * @param {Number} newSize
42131 "columnresize" : true,
42133 * @event columnmove
42134 * Fires when the user moves a column
42135 * @param {Number} oldIndex
42136 * @param {Number} newIndex
42138 "columnmove" : true,
42141 * Fires when row(s) start being dragged
42142 * @param {Grid} this
42143 * @param {Roo.GridDD} dd The drag drop object
42144 * @param {event} e The raw browser event
42146 "startdrag" : true,
42149 * Fires when a drag operation is complete
42150 * @param {Grid} this
42151 * @param {Roo.GridDD} dd The drag drop object
42152 * @param {event} e The raw browser event
42157 * Fires when dragged row(s) are dropped on a valid DD target
42158 * @param {Grid} this
42159 * @param {Roo.GridDD} dd The drag drop object
42160 * @param {String} targetId The target drag drop object
42161 * @param {event} e The raw browser event
42166 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
42167 * @param {Grid} this
42168 * @param {Roo.GridDD} dd The drag drop object
42169 * @param {String} targetId The target drag drop object
42170 * @param {event} e The raw browser event
42175 * Fires when the dragged row(s) first cross another DD target while being dragged
42176 * @param {Grid} this
42177 * @param {Roo.GridDD} dd The drag drop object
42178 * @param {String} targetId The target drag drop object
42179 * @param {event} e The raw browser event
42181 "dragenter" : true,
42184 * Fires when the dragged row(s) leave another DD target while being dragged
42185 * @param {Grid} this
42186 * @param {Roo.GridDD} dd The drag drop object
42187 * @param {String} targetId The target drag drop object
42188 * @param {event} e The raw browser event
42193 * Fires when a row is rendered, so you can change add a style to it.
42194 * @param {GridView} gridview The grid view
42195 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
42201 * Fires when the grid is rendered
42202 * @param {Grid} grid
42207 * Fires when a date is selected
42208 * @param {DatePicker} this
42209 * @param {Date} date The selected date
42213 * @event monthchange
42214 * Fires when the displayed month changes
42215 * @param {DatePicker} this
42216 * @param {Date} date The selected month
42218 'monthchange': true,
42220 * @event evententer
42221 * Fires when mouse over an event
42222 * @param {Calendar} this
42223 * @param {event} Event
42225 'evententer': true,
42227 * @event eventleave
42228 * Fires when the mouse leaves an
42229 * @param {Calendar} this
42232 'eventleave': true,
42234 * @event eventclick
42235 * Fires when the mouse click an
42236 * @param {Calendar} this
42239 'eventclick': true,
42241 * @event eventrender
42242 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42243 * @param {Calendar} this
42244 * @param {data} data to be modified
42246 'eventrender': true
42250 Roo.grid.Grid.superclass.constructor.call(this);
42251 this.on('render', function() {
42252 this.view.el.addClass('x-grid-cal');
42254 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42258 if (!Roo.grid.Calendar.style) {
42259 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42262 '.x-grid-cal .x-grid-col' : {
42263 height: 'auto !important',
42264 'vertical-align': 'top'
42266 '.x-grid-cal .fc-event-hori' : {
42277 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42279 * @cfg {Store} eventStore The store that loads events.
42284 activeDate : false,
42287 monitorWindowResize : false,
42290 resizeColumns : function() {
42291 var col = (this.view.el.getWidth() / 7) - 3;
42292 // loop through cols, and setWidth
42293 for(var i =0 ; i < 7 ; i++){
42294 this.cm.setColumnWidth(i, col);
42297 setDate :function(date) {
42299 Roo.log('setDate?');
42301 this.resizeColumns();
42302 var vd = this.activeDate;
42303 this.activeDate = date;
42304 // if(vd && this.el){
42305 // var t = date.getTime();
42306 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42307 // Roo.log('using add remove');
42309 // this.fireEvent('monthchange', this, date);
42311 // this.cells.removeClass("fc-state-highlight");
42312 // this.cells.each(function(c){
42313 // if(c.dateValue == t){
42314 // c.addClass("fc-state-highlight");
42315 // setTimeout(function(){
42316 // try{c.dom.firstChild.focus();}catch(e){}
42326 var days = date.getDaysInMonth();
42328 var firstOfMonth = date.getFirstDateOfMonth();
42329 var startingPos = firstOfMonth.getDay()-this.startDay;
42331 if(startingPos < this.startDay){
42335 var pm = date.add(Date.MONTH, -1);
42336 var prevStart = pm.getDaysInMonth()-startingPos;
42340 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42342 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42343 //this.cells.addClassOnOver('fc-state-hover');
42345 var cells = this.cells.elements;
42346 var textEls = this.textNodes;
42348 //Roo.each(cells, function(cell){
42349 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42352 days += startingPos;
42354 // convert everything to numbers so it's fast
42355 var day = 86400000;
42356 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42359 //Roo.log(prevStart);
42361 var today = new Date().clearTime().getTime();
42362 var sel = date.clearTime().getTime();
42363 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42364 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42365 var ddMatch = this.disabledDatesRE;
42366 var ddText = this.disabledDatesText;
42367 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42368 var ddaysText = this.disabledDaysText;
42369 var format = this.format;
42371 var setCellClass = function(cal, cell){
42373 //Roo.log('set Cell Class');
42375 var t = d.getTime();
42380 cell.dateValue = t;
42382 cell.className += " fc-today";
42383 cell.className += " fc-state-highlight";
42384 cell.title = cal.todayText;
42387 // disable highlight in other month..
42388 cell.className += " fc-state-highlight";
42393 //cell.className = " fc-state-disabled";
42394 cell.title = cal.minText;
42398 //cell.className = " fc-state-disabled";
42399 cell.title = cal.maxText;
42403 if(ddays.indexOf(d.getDay()) != -1){
42404 // cell.title = ddaysText;
42405 // cell.className = " fc-state-disabled";
42408 if(ddMatch && format){
42409 var fvalue = d.dateFormat(format);
42410 if(ddMatch.test(fvalue)){
42411 cell.title = ddText.replace("%0", fvalue);
42412 cell.className = " fc-state-disabled";
42416 if (!cell.initialClassName) {
42417 cell.initialClassName = cell.dom.className;
42420 cell.dom.className = cell.initialClassName + ' ' + cell.className;
42425 for(; i < startingPos; i++) {
42426 cells[i].dayName = (++prevStart);
42427 Roo.log(textEls[i]);
42428 d.setDate(d.getDate()+1);
42430 //cells[i].className = "fc-past fc-other-month";
42431 setCellClass(this, cells[i]);
42436 for(; i < days; i++){
42437 intDay = i - startingPos + 1;
42438 cells[i].dayName = (intDay);
42439 d.setDate(d.getDate()+1);
42441 cells[i].className = ''; // "x-date-active";
42442 setCellClass(this, cells[i]);
42446 for(; i < 42; i++) {
42447 //textEls[i].innerHTML = (++extraDays);
42449 d.setDate(d.getDate()+1);
42450 cells[i].dayName = (++extraDays);
42451 cells[i].className = "fc-future fc-other-month";
42452 setCellClass(this, cells[i]);
42455 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42457 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42459 // this will cause all the cells to mis
42462 for (var r = 0;r < 6;r++) {
42463 for (var c =0;c < 7;c++) {
42464 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42468 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42469 for(i=0;i<cells.length;i++) {
42471 this.cells.elements[i].dayName = cells[i].dayName ;
42472 this.cells.elements[i].className = cells[i].className;
42473 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42474 this.cells.elements[i].title = cells[i].title ;
42475 this.cells.elements[i].dateValue = cells[i].dateValue ;
42481 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42482 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42484 ////if(totalRows != 6){
42485 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42486 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42489 this.fireEvent('monthchange', this, date);
42494 * Returns the grid's SelectionModel.
42495 * @return {SelectionModel}
42497 getSelectionModel : function(){
42498 if(!this.selModel){
42499 this.selModel = new Roo.grid.CellSelectionModel();
42501 return this.selModel;
42505 this.eventStore.load()
42511 findCell : function(dt) {
42512 dt = dt.clearTime().getTime();
42514 this.cells.each(function(c){
42515 //Roo.log("check " +c.dateValue + '?=' + dt);
42516 if(c.dateValue == dt){
42526 findCells : function(rec) {
42527 var s = rec.data.start_dt.clone().clearTime().getTime();
42529 var e= rec.data.end_dt.clone().clearTime().getTime();
42532 this.cells.each(function(c){
42533 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42535 if(c.dateValue > e){
42538 if(c.dateValue < s){
42547 findBestRow: function(cells)
42551 for (var i =0 ; i < cells.length;i++) {
42552 ret = Math.max(cells[i].rows || 0,ret);
42559 addItem : function(rec)
42561 // look for vertical location slot in
42562 var cells = this.findCells(rec);
42564 rec.row = this.findBestRow(cells);
42566 // work out the location.
42570 for(var i =0; i < cells.length; i++) {
42578 if (crow.start.getY() == cells[i].getY()) {
42580 crow.end = cells[i];
42596 for (var i = 0; i < cells.length;i++) {
42597 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42604 clearEvents: function() {
42606 if (!this.eventStore.getCount()) {
42609 // reset number of rows in cells.
42610 Roo.each(this.cells.elements, function(c){
42614 this.eventStore.each(function(e) {
42615 this.clearEvent(e);
42620 clearEvent : function(ev)
42623 Roo.each(ev.els, function(el) {
42624 el.un('mouseenter' ,this.onEventEnter, this);
42625 el.un('mouseleave' ,this.onEventLeave, this);
42633 renderEvent : function(ev,ctr) {
42635 ctr = this.view.el.select('.fc-event-container',true).first();
42639 this.clearEvent(ev);
42645 var cells = ev.cells;
42646 var rows = ev.rows;
42647 this.fireEvent('eventrender', this, ev);
42649 for(var i =0; i < rows.length; i++) {
42653 cls += ' fc-event-start';
42655 if ((i+1) == rows.length) {
42656 cls += ' fc-event-end';
42659 //Roo.log(ev.data);
42660 // how many rows should it span..
42661 var cg = this.eventTmpl.append(ctr,Roo.apply({
42664 }, ev.data) , true);
42667 cg.on('mouseenter' ,this.onEventEnter, this, ev);
42668 cg.on('mouseleave' ,this.onEventLeave, this, ev);
42669 cg.on('click', this.onEventClick, this, ev);
42673 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42674 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42677 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
42678 cg.setWidth(ebox.right - sbox.x -2);
42682 renderEvents: function()
42684 // first make sure there is enough space..
42686 if (!this.eventTmpl) {
42687 this.eventTmpl = new Roo.Template(
42688 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
42689 '<div class="fc-event-inner">' +
42690 '<span class="fc-event-time">{time}</span>' +
42691 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42693 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
42701 this.cells.each(function(c) {
42702 //Roo.log(c.select('.fc-day-content div',true).first());
42703 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42706 var ctr = this.view.el.select('.fc-event-container',true).first();
42709 this.eventStore.each(function(ev){
42711 this.renderEvent(ev);
42715 this.view.layout();
42719 onEventEnter: function (e, el,event,d) {
42720 this.fireEvent('evententer', this, el, event);
42723 onEventLeave: function (e, el,event,d) {
42724 this.fireEvent('eventleave', this, el, event);
42727 onEventClick: function (e, el,event,d) {
42728 this.fireEvent('eventclick', this, el, event);
42731 onMonthChange: function () {
42735 onLoad: function () {
42737 //Roo.log('calendar onload');
42739 if(this.eventStore.getCount() > 0){
42743 this.eventStore.each(function(d){
42748 if (typeof(add.end_dt) == 'undefined') {
42749 Roo.log("Missing End time in calendar data: ");
42753 if (typeof(add.start_dt) == 'undefined') {
42754 Roo.log("Missing Start time in calendar data: ");
42758 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42759 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42760 add.id = add.id || d.id;
42761 add.title = add.title || '??';
42769 this.renderEvents();
42779 render : function ()
42783 if (!this.view.el.hasClass('course-timesheet')) {
42784 this.view.el.addClass('course-timesheet');
42786 if (this.tsStyle) {
42791 Roo.log(_this.grid.view.el.getWidth());
42794 this.tsStyle = Roo.util.CSS.createStyleSheet({
42795 '.course-timesheet .x-grid-row' : {
42798 '.x-grid-row td' : {
42799 'vertical-align' : 0
42801 '.course-edit-link' : {
42803 'text-overflow' : 'ellipsis',
42804 'overflow' : 'hidden',
42805 'white-space' : 'nowrap',
42806 'cursor' : 'pointer'
42811 '.de-act-sup-link' : {
42812 'color' : 'purple',
42813 'text-decoration' : 'line-through'
42817 'text-decoration' : 'line-through'
42819 '.course-timesheet .course-highlight' : {
42820 'border-top-style': 'dashed !important',
42821 'border-bottom-bottom': 'dashed !important'
42823 '.course-timesheet .course-item' : {
42824 'font-family' : 'tahoma, arial, helvetica',
42825 'font-size' : '11px',
42826 'overflow' : 'hidden',
42827 'padding-left' : '10px',
42828 'padding-right' : '10px',
42829 'padding-top' : '10px'
42837 monitorWindowResize : false,
42838 cellrenderer : function(v,x,r)
42843 xtype: 'CellSelectionModel',
42850 beforeload : function (_self, options)
42852 options.params = options.params || {};
42853 options.params._month = _this.monthField.getValue();
42854 options.params.limit = 9999;
42855 options.params['sort'] = 'when_dt';
42856 options.params['dir'] = 'ASC';
42857 this.proxy.loadResponse = this.loadResponse;
42859 //this.addColumns();
42861 load : function (_self, records, options)
42863 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42864 // if you click on the translation.. you can edit it...
42865 var el = Roo.get(this);
42866 var id = el.dom.getAttribute('data-id');
42867 var d = el.dom.getAttribute('data-date');
42868 var t = el.dom.getAttribute('data-time');
42869 //var id = this.child('span').dom.textContent;
42872 Pman.Dialog.CourseCalendar.show({
42876 productitem_active : id ? 1 : 0
42878 _this.grid.ds.load({});
42883 _this.panel.fireEvent('resize', [ '', '' ]);
42886 loadResponse : function(o, success, response){
42887 // this is overridden on before load..
42889 Roo.log("our code?");
42890 //Roo.log(success);
42891 //Roo.log(response)
42892 delete this.activeRequest;
42894 this.fireEvent("loadexception", this, o, response);
42895 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42900 result = o.reader.read(response);
42902 Roo.log("load exception?");
42903 this.fireEvent("loadexception", this, o, response, e);
42904 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42907 Roo.log("ready...");
42908 // loop through result.records;
42909 // and set this.tdate[date] = [] << array of records..
42911 Roo.each(result.records, function(r){
42913 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42914 _this.tdata[r.data.when_dt.format('j')] = [];
42916 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42919 //Roo.log(_this.tdata);
42921 result.records = [];
42922 result.totalRecords = 6;
42924 // let's generate some duumy records for the rows.
42925 //var st = _this.dateField.getValue();
42927 // work out monday..
42928 //st = st.add(Date.DAY, -1 * st.format('w'));
42930 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42932 var firstOfMonth = date.getFirstDayOfMonth();
42933 var days = date.getDaysInMonth();
42935 var firstAdded = false;
42936 for (var i = 0; i < result.totalRecords ; i++) {
42937 //var d= st.add(Date.DAY, i);
42940 for(var w = 0 ; w < 7 ; w++){
42941 if(!firstAdded && firstOfMonth != w){
42948 var dd = (d > 0 && d < 10) ? "0"+d : d;
42949 row['weekday'+w] = String.format(
42950 '<span style="font-size: 16px;"><b>{0}</b></span>'+
42951 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42953 date.format('Y-m-')+dd
42956 if(typeof(_this.tdata[d]) != 'undefined'){
42957 Roo.each(_this.tdata[d], function(r){
42961 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42962 if(r.parent_id*1>0){
42963 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42966 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42967 deactive = 'de-act-link';
42970 row['weekday'+w] += String.format(
42971 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42973 r.product_id_name, //1
42974 r.when_dt.format('h:ia'), //2
42984 // only do this if something added..
42986 result.records.push(_this.grid.dataSource.reader.newRow(row));
42990 // push it twice. (second one with an hour..
42994 this.fireEvent("load", this, o, o.request.arg);
42995 o.request.callback.call(o.request.scope, result, o.request.arg, true);
42997 sortInfo : {field: 'when_dt', direction : 'ASC' },
42999 xtype: 'HttpProxy',
43002 url : baseURL + '/Roo/Shop_course.php'
43005 xtype: 'JsonReader',
43022 'name': 'parent_id',
43026 'name': 'product_id',
43030 'name': 'productitem_id',
43048 click : function (_self, e)
43050 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43051 sd.setMonth(sd.getMonth()-1);
43052 _this.monthField.setValue(sd.format('Y-m-d'));
43053 _this.grid.ds.load({});
43059 xtype: 'Separator',
43063 xtype: 'MonthField',
43066 render : function (_self)
43068 _this.monthField = _self;
43069 // _this.monthField.set today
43071 select : function (combo, date)
43073 _this.grid.ds.load({});
43076 value : (function() { return new Date(); })()
43079 xtype: 'Separator',
43085 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
43095 click : function (_self, e)
43097 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43098 sd.setMonth(sd.getMonth()+1);
43099 _this.monthField.setValue(sd.format('Y-m-d'));
43100 _this.grid.ds.load({});
43113 * Ext JS Library 1.1.1
43114 * Copyright(c) 2006-2007, Ext JS, LLC.
43116 * Originally Released Under LGPL - original licence link has changed is not relivant.
43119 * <script type="text/javascript">
43123 * @class Roo.LoadMask
43124 * A simple utility class for generically masking elements while loading data. If the element being masked has
43125 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
43126 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
43127 * element's UpdateManager load indicator and will be destroyed after the initial load.
43129 * Create a new LoadMask
43130 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
43131 * @param {Object} config The config object
43133 Roo.LoadMask = function(el, config){
43134 this.el = Roo.get(el);
43135 Roo.apply(this, config);
43137 this.store.on('beforeload', this.onBeforeLoad, this);
43138 this.store.on('load', this.onLoad, this);
43139 this.store.on('loadexception', this.onLoadException, this);
43140 this.removeMask = false;
43142 var um = this.el.getUpdateManager();
43143 um.showLoadIndicator = false; // disable the default indicator
43144 um.on('beforeupdate', this.onBeforeLoad, this);
43145 um.on('update', this.onLoad, this);
43146 um.on('failure', this.onLoad, this);
43147 this.removeMask = true;
43151 Roo.LoadMask.prototype = {
43153 * @cfg {Boolean} removeMask
43154 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
43155 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
43157 removeMask : false,
43159 * @cfg {String} msg
43160 * The text to display in a centered loading message box (defaults to 'Loading...')
43162 msg : 'Loading...',
43164 * @cfg {String} msgCls
43165 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
43167 msgCls : 'x-mask-loading',
43170 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
43176 * Disables the mask to prevent it from being displayed
43178 disable : function(){
43179 this.disabled = true;
43183 * Enables the mask so that it can be displayed
43185 enable : function(){
43186 this.disabled = false;
43189 onLoadException : function()
43191 Roo.log(arguments);
43193 if (typeof(arguments[3]) != 'undefined') {
43194 Roo.MessageBox.alert("Error loading",arguments[3]);
43198 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43199 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43206 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43209 onLoad : function()
43211 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43215 onBeforeLoad : function(){
43216 if(!this.disabled){
43217 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43222 destroy : function(){
43224 this.store.un('beforeload', this.onBeforeLoad, this);
43225 this.store.un('load', this.onLoad, this);
43226 this.store.un('loadexception', this.onLoadException, this);
43228 var um = this.el.getUpdateManager();
43229 um.un('beforeupdate', this.onBeforeLoad, this);
43230 um.un('update', this.onLoad, this);
43231 um.un('failure', this.onLoad, this);
43236 * Ext JS Library 1.1.1
43237 * Copyright(c) 2006-2007, Ext JS, LLC.
43239 * Originally Released Under LGPL - original licence link has changed is not relivant.
43242 * <script type="text/javascript">
43247 * @class Roo.XTemplate
43248 * @extends Roo.Template
43249 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43251 var t = new Roo.XTemplate(
43252 '<select name="{name}">',
43253 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
43257 // then append, applying the master template values
43260 * Supported features:
43265 {a_variable} - output encoded.
43266 {a_variable.format:("Y-m-d")} - call a method on the variable
43267 {a_variable:raw} - unencoded output
43268 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43269 {a_variable:this.method_on_template(...)} - call a method on the template object.
43274 <tpl for="a_variable or condition.."></tpl>
43275 <tpl if="a_variable or condition"></tpl>
43276 <tpl exec="some javascript"></tpl>
43277 <tpl name="named_template"></tpl> (experimental)
43279 <tpl for="."></tpl> - just iterate the property..
43280 <tpl for=".."></tpl> - iterates with the parent (probably the template)
43284 Roo.XTemplate = function()
43286 Roo.XTemplate.superclass.constructor.apply(this, arguments);
43293 Roo.extend(Roo.XTemplate, Roo.Template, {
43296 * The various sub templates
43301 * basic tag replacing syntax
43304 * // you can fake an object call by doing this
43308 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43311 * compile the template
43313 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43316 compile: function()
43320 s = ['<tpl>', s, '</tpl>'].join('');
43322 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43323 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43324 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
43325 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43326 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
43331 while(true == !!(m = s.match(re))){
43332 var forMatch = m[0].match(nameRe),
43333 ifMatch = m[0].match(ifRe),
43334 execMatch = m[0].match(execRe),
43335 namedMatch = m[0].match(namedRe),
43340 name = forMatch && forMatch[1] ? forMatch[1] : '';
43343 // if - puts fn into test..
43344 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43346 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43351 // exec - calls a function... returns empty if true is returned.
43352 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43354 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43362 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43363 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43364 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43367 var uid = namedMatch ? namedMatch[1] : id;
43371 id: namedMatch ? namedMatch[1] : id,
43378 s = s.replace(m[0], '');
43380 s = s.replace(m[0], '{xtpl'+ id + '}');
43385 for(var i = tpls.length-1; i >= 0; --i){
43386 this.compileTpl(tpls[i]);
43387 this.tpls[tpls[i].id] = tpls[i];
43389 this.master = tpls[tpls.length-1];
43393 * same as applyTemplate, except it's done to one of the subTemplates
43394 * when using named templates, you can do:
43396 * var str = pl.applySubTemplate('your-name', values);
43399 * @param {Number} id of the template
43400 * @param {Object} values to apply to template
43401 * @param {Object} parent (normaly the instance of this object)
43403 applySubTemplate : function(id, values, parent)
43407 var t = this.tpls[id];
43411 if(t.test && !t.test.call(this, values, parent)){
43415 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43416 Roo.log(e.toString());
43422 if(t.exec && t.exec.call(this, values, parent)){
43426 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43427 Roo.log(e.toString());
43432 var vs = t.target ? t.target.call(this, values, parent) : values;
43433 parent = t.target ? values : parent;
43434 if(t.target && vs instanceof Array){
43436 for(var i = 0, len = vs.length; i < len; i++){
43437 buf[buf.length] = t.compiled.call(this, vs[i], parent);
43439 return buf.join('');
43441 return t.compiled.call(this, vs, parent);
43443 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43444 Roo.log(e.toString());
43445 Roo.log(t.compiled);
43450 compileTpl : function(tpl)
43452 var fm = Roo.util.Format;
43453 var useF = this.disableFormats !== true;
43454 var sep = Roo.isGecko ? "+" : ",";
43455 var undef = function(str) {
43456 Roo.log("Property not found :" + str);
43460 var fn = function(m, name, format, args)
43462 //Roo.log(arguments);
43463 args = args ? args.replace(/\\'/g,"'") : args;
43464 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43465 if (typeof(format) == 'undefined') {
43466 format= 'htmlEncode';
43468 if (format == 'raw' ) {
43472 if(name.substr(0, 4) == 'xtpl'){
43473 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43476 // build an array of options to determine if value is undefined..
43478 // basically get 'xxxx.yyyy' then do
43479 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43480 // (function () { Roo.log("Property not found"); return ''; })() :
43485 Roo.each(name.split('.'), function(st) {
43486 lookfor += (lookfor.length ? '.': '') + st;
43487 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
43490 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43493 if(format && useF){
43495 args = args ? ',' + args : "";
43497 if(format.substr(0, 5) != "this."){
43498 format = "fm." + format + '(';
43500 format = 'this.call("'+ format.substr(5) + '", ';
43504 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
43508 // called with xxyx.yuu:(test,test)
43510 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
43512 // raw.. - :raw modifier..
43513 return "'"+ sep + udef_st + name + ")"+sep+"'";
43517 // branched to use + in gecko and [].join() in others
43519 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
43520 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43523 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
43524 body.push(tpl.body.replace(/(\r\n|\n)/g,
43525 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43526 body.push("'].join('');};};");
43527 body = body.join('');
43530 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43532 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
43538 applyTemplate : function(values){
43539 return this.master.compiled.call(this, values, {});
43540 //var s = this.subs;
43543 apply : function(){
43544 return this.applyTemplate.apply(this, arguments);
43549 Roo.XTemplate.from = function(el){
43550 el = Roo.getDom(el);
43551 return new Roo.XTemplate(el.value || el.innerHTML);