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(config){
1405 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
1408 Roo.data.MemoryProxy.superclass.constructor.call(this);
1412 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1415 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1418 * Load data from the requested source (in this case an in-memory
1419 * data object passed to the constructor), read the data object into
1420 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1421 * process that block using the passed callback.
1422 * @param {Object} params This parameter is not used by the MemoryProxy class.
1423 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1424 * object into a block of Roo.data.Records.
1425 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1426 * The function must be passed <ul>
1427 * <li>The Record block object</li>
1428 * <li>The "arg" argument from the load function</li>
1429 * <li>A boolean success indicator</li>
1431 * @param {Object} scope The scope in which to call the callback
1432 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1434 load : function(params, reader, callback, scope, arg){
1435 params = params || {};
1438 result = reader.readRecords(params.data ? params.data :this.data);
1440 this.fireEvent("loadexception", this, arg, null, e);
1441 callback.call(scope, null, arg, false);
1444 callback.call(scope, result, arg, true);
1448 update : function(params, records){
1453 * Ext JS Library 1.1.1
1454 * Copyright(c) 2006-2007, Ext JS, LLC.
1456 * Originally Released Under LGPL - original licence link has changed is not relivant.
1459 * <script type="text/javascript">
1462 * @class Roo.data.HttpProxy
1463 * @extends Roo.data.DataProxy
1464 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1465 * configured to reference a certain URL.<br><br>
1467 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1468 * from which the running page was served.<br><br>
1470 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1472 * Be aware that to enable the browser to parse an XML document, the server must set
1473 * the Content-Type header in the HTTP response to "text/xml".
1475 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1476 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1477 * will be used to make the request.
1479 Roo.data.HttpProxy = function(conn){
1480 Roo.data.HttpProxy.superclass.constructor.call(this);
1481 // is conn a conn config or a real conn?
1483 this.useAjax = !conn || !conn.events;
1487 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1488 // thse are take from connection...
1491 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1494 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1495 * extra parameters to each request made by this object. (defaults to undefined)
1498 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1499 * to each request made by this object. (defaults to undefined)
1502 * @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)
1505 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1508 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1514 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1518 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1519 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1520 * a finer-grained basis than the DataProxy events.
1522 getConnection : function(){
1523 return this.useAjax ? Roo.Ajax : this.conn;
1527 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1528 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1529 * process that block using the passed callback.
1530 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1531 * for the request to the remote server.
1532 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1533 * object into a block of Roo.data.Records.
1534 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1535 * The function must be passed <ul>
1536 * <li>The Record block object</li>
1537 * <li>The "arg" argument from the load function</li>
1538 * <li>A boolean success indicator</li>
1540 * @param {Object} scope The scope in which to call the callback
1541 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1543 load : function(params, reader, callback, scope, arg){
1544 if(this.fireEvent("beforeload", this, params) !== false){
1546 params : params || {},
1548 callback : callback,
1553 callback : this.loadResponse,
1557 Roo.applyIf(o, this.conn);
1558 if(this.activeRequest){
1559 Roo.Ajax.abort(this.activeRequest);
1561 this.activeRequest = Roo.Ajax.request(o);
1563 this.conn.request(o);
1566 callback.call(scope||this, null, arg, false);
1571 loadResponse : function(o, success, response){
1572 delete this.activeRequest;
1574 this.fireEvent("loadexception", this, o, response);
1575 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1580 result = o.reader.read(response);
1583 o.raw = { errorMsg : response.responseText };
1584 this.fireEvent("loadexception", this, o, response, e);
1585 o.request.callback.call(o.request.scope, o, o.request.arg, false);
1589 this.fireEvent("load", this, o, o.request.arg);
1590 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1594 update : function(dataSet){
1599 updateResponse : function(dataSet){
1604 * Ext JS Library 1.1.1
1605 * Copyright(c) 2006-2007, Ext JS, LLC.
1607 * Originally Released Under LGPL - original licence link has changed is not relivant.
1610 * <script type="text/javascript">
1614 * @class Roo.data.ScriptTagProxy
1615 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1616 * other than the originating domain of the running page.<br><br>
1618 * <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
1619 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1621 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1622 * source code that is used as the source inside a <script> tag.<br><br>
1624 * In order for the browser to process the returned data, the server must wrap the data object
1625 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1626 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1627 * depending on whether the callback name was passed:
1630 boolean scriptTag = false;
1631 String cb = request.getParameter("callback");
1634 response.setContentType("text/javascript");
1636 response.setContentType("application/x-json");
1638 Writer out = response.getWriter();
1640 out.write(cb + "(");
1642 out.print(dataBlock.toJsonString());
1649 * @param {Object} config A configuration object.
1651 Roo.data.ScriptTagProxy = function(config){
1652 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1653 Roo.apply(this, config);
1654 this.head = document.getElementsByTagName("head")[0];
1657 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1659 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1661 * @cfg {String} url The URL from which to request the data object.
1664 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1668 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1669 * the server the name of the callback function set up by the load call to process the returned data object.
1670 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1671 * javascript output which calls this named function passing the data object as its only parameter.
1673 callbackParam : "callback",
1675 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1676 * name to the request.
1681 * Load data from the configured URL, read the data object into
1682 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1683 * process that block using the passed callback.
1684 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1685 * for the request to the remote server.
1686 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1687 * object into a block of Roo.data.Records.
1688 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1689 * The function must be passed <ul>
1690 * <li>The Record block object</li>
1691 * <li>The "arg" argument from the load function</li>
1692 * <li>A boolean success indicator</li>
1694 * @param {Object} scope The scope in which to call the callback
1695 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1697 load : function(params, reader, callback, scope, arg){
1698 if(this.fireEvent("beforeload", this, params) !== false){
1700 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1703 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1705 url += "&_dc=" + (new Date().getTime());
1707 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1710 cb : "stcCallback"+transId,
1711 scriptId : "stcScript"+transId,
1715 callback : callback,
1721 window[trans.cb] = function(o){
1722 conn.handleResponse(o, trans);
1725 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1727 if(this.autoAbort !== false){
1731 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1733 var script = document.createElement("script");
1734 script.setAttribute("src", url);
1735 script.setAttribute("type", "text/javascript");
1736 script.setAttribute("id", trans.scriptId);
1737 this.head.appendChild(script);
1741 callback.call(scope||this, null, arg, false);
1746 isLoading : function(){
1747 return this.trans ? true : false;
1751 * Abort the current server request.
1754 if(this.isLoading()){
1755 this.destroyTrans(this.trans);
1760 destroyTrans : function(trans, isLoaded){
1761 this.head.removeChild(document.getElementById(trans.scriptId));
1762 clearTimeout(trans.timeoutId);
1764 window[trans.cb] = undefined;
1766 delete window[trans.cb];
1769 // if hasn't been loaded, wait for load to remove it to prevent script error
1770 window[trans.cb] = function(){
1771 window[trans.cb] = undefined;
1773 delete window[trans.cb];
1780 handleResponse : function(o, trans){
1782 this.destroyTrans(trans, true);
1785 result = trans.reader.readRecords(o);
1787 this.fireEvent("loadexception", this, o, trans.arg, e);
1788 trans.callback.call(trans.scope||window, null, trans.arg, false);
1791 this.fireEvent("load", this, o, trans.arg);
1792 trans.callback.call(trans.scope||window, result, trans.arg, true);
1796 handleFailure : function(trans){
1798 this.destroyTrans(trans, false);
1799 this.fireEvent("loadexception", this, null, trans.arg);
1800 trans.callback.call(trans.scope||window, null, trans.arg, false);
1804 * Ext JS Library 1.1.1
1805 * Copyright(c) 2006-2007, Ext JS, LLC.
1807 * Originally Released Under LGPL - original licence link has changed is not relivant.
1810 * <script type="text/javascript">
1814 * @class Roo.data.JsonReader
1815 * @extends Roo.data.DataReader
1816 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1817 * based on mappings in a provided Roo.data.Record constructor.
1819 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1820 * in the reply previously.
1825 var RecordDef = Roo.data.Record.create([
1826 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1827 {name: 'occupation'} // This field will use "occupation" as the mapping.
1829 var myReader = new Roo.data.JsonReader({
1830 totalProperty: "results", // The property which contains the total dataset size (optional)
1831 root: "rows", // The property which contains an Array of row objects
1832 id: "id" // The property within each row object that provides an ID for the record (optional)
1836 * This would consume a JSON file like this:
1838 { 'results': 2, 'rows': [
1839 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1840 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1843 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1844 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1845 * paged from the remote server.
1846 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1847 * @cfg {String} root name of the property which contains the Array of row objects.
1848 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1849 * @cfg {Array} fields Array of field definition objects
1851 * Create a new JsonReader
1852 * @param {Object} meta Metadata configuration options
1853 * @param {Object} recordType Either an Array of field definition objects,
1854 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1856 Roo.data.JsonReader = function(meta, recordType){
1859 // set some defaults:
1861 totalProperty: 'total',
1862 successProperty : 'success',
1867 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1869 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1871 readerType : 'Json',
1874 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1875 * Used by Store query builder to append _requestMeta to params.
1878 metaFromRemote : false,
1880 * This method is only used by a DataProxy which has retrieved data from a remote server.
1881 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1882 * @return {Object} data A data block which is used by an Roo.data.Store object as
1883 * a cache of Roo.data.Records.
1885 read : function(response){
1886 var json = response.responseText;
1888 var o = /* eval:var:o */ eval("("+json+")");
1890 throw {message: "JsonReader.read: Json object not found"};
1896 this.metaFromRemote = true;
1897 this.meta = o.metaData;
1898 this.recordType = Roo.data.Record.create(o.metaData.fields);
1899 this.onMetaChange(this.meta, this.recordType, o);
1901 return this.readRecords(o);
1904 // private function a store will implement
1905 onMetaChange : function(meta, recordType, o){
1912 simpleAccess: function(obj, subsc) {
1919 getJsonAccessor: function(){
1921 return function(expr) {
1923 return(re.test(expr))
1924 ? new Function("obj", "return obj." + expr)
1934 * Create a data block containing Roo.data.Records from an XML document.
1935 * @param {Object} o An object which contains an Array of row objects in the property specified
1936 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1937 * which contains the total size of the dataset.
1938 * @return {Object} data A data block which is used by an Roo.data.Store object as
1939 * a cache of Roo.data.Records.
1941 readRecords : function(o){
1943 * After any data loads, the raw JSON data is available for further custom processing.
1947 var s = this.meta, Record = this.recordType,
1948 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1950 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1952 if(s.totalProperty) {
1953 this.getTotal = this.getJsonAccessor(s.totalProperty);
1955 if(s.successProperty) {
1956 this.getSuccess = this.getJsonAccessor(s.successProperty);
1958 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1960 var g = this.getJsonAccessor(s.id);
1961 this.getId = function(rec) {
1963 return (r === undefined || r === "") ? null : r;
1966 this.getId = function(){return null;};
1969 for(var jj = 0; jj < fl; jj++){
1971 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1972 this.ef[jj] = this.getJsonAccessor(map);
1976 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1977 if(s.totalProperty){
1978 var vt = parseInt(this.getTotal(o), 10);
1983 if(s.successProperty){
1984 var vs = this.getSuccess(o);
1985 if(vs === false || vs === 'false'){
1990 for(var i = 0; i < c; i++){
1993 var id = this.getId(n);
1994 for(var j = 0; j < fl; j++){
1996 var v = this.ef[j](n);
1998 Roo.log('missing convert for ' + f.name);
2002 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
2006 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2012 var record = new Record(values, id);
2014 records[i] = record;
2020 totalRecords : totalRecords
2023 // used when loading children.. @see loadDataFromChildren
2024 toLoadData: function(rec)
2026 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2027 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2028 return { data : data, total : data.length };
2033 * Ext JS Library 1.1.1
2034 * Copyright(c) 2006-2007, Ext JS, LLC.
2036 * Originally Released Under LGPL - original licence link has changed is not relivant.
2039 * <script type="text/javascript">
2043 * @class Roo.data.XmlReader
2044 * @extends Roo.data.DataReader
2045 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2046 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2048 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2049 * header in the HTTP response must be set to "text/xml".</em>
2053 var RecordDef = Roo.data.Record.create([
2054 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2055 {name: 'occupation'} // This field will use "occupation" as the mapping.
2057 var myReader = new Roo.data.XmlReader({
2058 totalRecords: "results", // The element which contains the total dataset size (optional)
2059 record: "row", // The repeated element which contains row information
2060 id: "id" // The element within the row that provides an ID for the record (optional)
2064 * This would consume an XML file like this:
2068 <results>2</results>
2071 <name>Bill</name>
2072 <occupation>Gardener</occupation>
2076 <name>Ben</name>
2077 <occupation>Horticulturalist</occupation>
2081 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2082 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2083 * paged from the remote server.
2084 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2085 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2086 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2087 * a record identifier value.
2089 * Create a new XmlReader
2090 * @param {Object} meta Metadata configuration options
2091 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2092 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2093 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2095 Roo.data.XmlReader = function(meta, recordType){
2097 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2099 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2104 * This method is only used by a DataProxy which has retrieved data from a remote server.
2105 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2106 * to contain a method called 'responseXML' that returns an XML document object.
2107 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2108 * a cache of Roo.data.Records.
2110 read : function(response){
2111 var doc = response.responseXML;
2113 throw {message: "XmlReader.read: XML Document not available"};
2115 return this.readRecords(doc);
2119 * Create a data block containing Roo.data.Records from an XML document.
2120 * @param {Object} doc A parsed XML document.
2121 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2122 * a cache of Roo.data.Records.
2124 readRecords : function(doc){
2126 * After any data loads/reads, the raw XML Document is available for further custom processing.
2130 var root = doc.documentElement || doc;
2131 var q = Roo.DomQuery;
2132 var recordType = this.recordType, fields = recordType.prototype.fields;
2133 var sid = this.meta.id;
2134 var totalRecords = 0, success = true;
2135 if(this.meta.totalRecords){
2136 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2139 if(this.meta.success){
2140 var sv = q.selectValue(this.meta.success, root, true);
2141 success = sv !== false && sv !== 'false';
2144 var ns = q.select(this.meta.record, root);
2145 for(var i = 0, len = ns.length; i < len; i++) {
2148 var id = sid ? q.selectValue(sid, n) : undefined;
2149 for(var j = 0, jlen = fields.length; j < jlen; j++){
2150 var f = fields.items[j];
2151 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2155 var record = new recordType(values, id);
2157 records[records.length] = record;
2163 totalRecords : totalRecords || records.length
2168 * Ext JS Library 1.1.1
2169 * Copyright(c) 2006-2007, Ext JS, LLC.
2171 * Originally Released Under LGPL - original licence link has changed is not relivant.
2174 * <script type="text/javascript">
2178 * @class Roo.data.ArrayReader
2179 * @extends Roo.data.DataReader
2180 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2181 * Each element of that Array represents a row of data fields. The
2182 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2183 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2187 var RecordDef = Roo.data.Record.create([
2188 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2189 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2191 var myReader = new Roo.data.ArrayReader({
2192 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2196 * This would consume an Array like this:
2198 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2202 * Create a new JsonReader
2203 * @param {Object} meta Metadata configuration options.
2204 * @param {Object|Array} recordType Either an Array of field definition objects
2206 * @cfg {Array} fields Array of field definition objects
2207 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2208 * as specified to {@link Roo.data.Record#create},
2209 * or an {@link Roo.data.Record} object
2212 * created using {@link Roo.data.Record#create}.
2214 Roo.data.ArrayReader = function(meta, recordType)
2216 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2219 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2222 * Create a data block containing Roo.data.Records from an XML document.
2223 * @param {Object} o An Array of row objects which represents the dataset.
2224 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2225 * a cache of Roo.data.Records.
2227 readRecords : function(o)
2229 var sid = this.meta ? this.meta.id : null;
2230 var recordType = this.recordType, fields = recordType.prototype.fields;
2233 for(var i = 0; i < root.length; i++){
2236 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2237 for(var j = 0, jlen = fields.length; j < jlen; j++){
2238 var f = fields.items[j];
2239 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2240 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2244 var record = new recordType(values, id);
2246 records[records.length] = record;
2250 totalRecords : records.length
2253 // used when loading children.. @see loadDataFromChildren
2254 toLoadData: function(rec)
2256 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2257 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2264 * Ext JS Library 1.1.1
2265 * Copyright(c) 2006-2007, Ext JS, LLC.
2267 * Originally Released Under LGPL - original licence link has changed is not relivant.
2270 * <script type="text/javascript">
2275 * @class Roo.data.Tree
2276 * @extends Roo.util.Observable
2277 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2278 * in the tree have most standard DOM functionality.
2280 * @param {Node} root (optional) The root node
2282 Roo.data.Tree = function(root){
2285 * The root node for this tree
2290 this.setRootNode(root);
2295 * Fires when a new child node is appended to a node in this tree.
2296 * @param {Tree} tree The owner tree
2297 * @param {Node} parent The parent node
2298 * @param {Node} node The newly appended node
2299 * @param {Number} index The index of the newly appended node
2304 * Fires when a child node is removed from a node in this tree.
2305 * @param {Tree} tree The owner tree
2306 * @param {Node} parent The parent node
2307 * @param {Node} node The child node removed
2312 * Fires when a node is moved to a new location in the tree
2313 * @param {Tree} tree The owner tree
2314 * @param {Node} node The node moved
2315 * @param {Node} oldParent The old parent of this node
2316 * @param {Node} newParent The new parent of this node
2317 * @param {Number} index The index it was moved to
2322 * Fires when a new child node is inserted in a node in this tree.
2323 * @param {Tree} tree The owner tree
2324 * @param {Node} parent The parent node
2325 * @param {Node} node The child node inserted
2326 * @param {Node} refNode The child node the node was inserted before
2330 * @event beforeappend
2331 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2332 * @param {Tree} tree The owner tree
2333 * @param {Node} parent The parent node
2334 * @param {Node} node The child node to be appended
2336 "beforeappend" : true,
2338 * @event beforeremove
2339 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2340 * @param {Tree} tree The owner tree
2341 * @param {Node} parent The parent node
2342 * @param {Node} node The child node to be removed
2344 "beforeremove" : true,
2347 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2348 * @param {Tree} tree The owner tree
2349 * @param {Node} node The node being moved
2350 * @param {Node} oldParent The parent of the node
2351 * @param {Node} newParent The new parent the node is moving to
2352 * @param {Number} index The index it is being moved to
2354 "beforemove" : true,
2356 * @event beforeinsert
2357 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2358 * @param {Tree} tree The owner tree
2359 * @param {Node} parent The parent node
2360 * @param {Node} node The child node to be inserted
2361 * @param {Node} refNode The child node the node is being inserted before
2363 "beforeinsert" : true
2366 Roo.data.Tree.superclass.constructor.call(this);
2369 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2372 proxyNodeEvent : function(){
2373 return this.fireEvent.apply(this, arguments);
2377 * Returns the root node for this tree.
2380 getRootNode : function(){
2385 * Sets the root node for this tree.
2386 * @param {Node} node
2389 setRootNode : function(node){
2391 node.ownerTree = this;
2393 this.registerNode(node);
2398 * Gets a node in this tree by its id.
2399 * @param {String} id
2402 getNodeById : function(id){
2403 return this.nodeHash[id];
2406 registerNode : function(node){
2407 this.nodeHash[node.id] = node;
2410 unregisterNode : function(node){
2411 delete this.nodeHash[node.id];
2414 toString : function(){
2415 return "[Tree"+(this.id?" "+this.id:"")+"]";
2420 * @class Roo.data.Node
2421 * @extends Roo.util.Observable
2422 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2423 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2425 * @param {Object} attributes The attributes/config for the node
2427 Roo.data.Node = function(attributes){
2429 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2432 this.attributes = attributes || {};
2433 this.leaf = this.attributes.leaf;
2435 * The node id. @type String
2437 this.id = this.attributes.id;
2439 this.id = Roo.id(null, "ynode-");
2440 this.attributes.id = this.id;
2445 * All child nodes of this node. @type Array
2447 this.childNodes = [];
2448 if(!this.childNodes.indexOf){ // indexOf is a must
2449 this.childNodes.indexOf = function(o){
2450 for(var i = 0, len = this.length; i < len; i++){
2459 * The parent node for this node. @type Node
2461 this.parentNode = null;
2463 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2465 this.firstChild = null;
2467 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2469 this.lastChild = null;
2471 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2473 this.previousSibling = null;
2475 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2477 this.nextSibling = null;
2482 * Fires when a new child node is appended
2483 * @param {Tree} tree The owner tree
2484 * @param {Node} this This node
2485 * @param {Node} node The newly appended node
2486 * @param {Number} index The index of the newly appended node
2491 * Fires when a child node is removed
2492 * @param {Tree} tree The owner tree
2493 * @param {Node} this This node
2494 * @param {Node} node The removed node
2499 * Fires when this node is moved to a new location in the tree
2500 * @param {Tree} tree The owner tree
2501 * @param {Node} this This node
2502 * @param {Node} oldParent The old parent of this node
2503 * @param {Node} newParent The new parent of this node
2504 * @param {Number} index The index it was moved to
2509 * Fires when a new child node is inserted.
2510 * @param {Tree} tree The owner tree
2511 * @param {Node} this This node
2512 * @param {Node} node The child node inserted
2513 * @param {Node} refNode The child node the node was inserted before
2517 * @event beforeappend
2518 * Fires before a new child is appended, return false to cancel the append.
2519 * @param {Tree} tree The owner tree
2520 * @param {Node} this This node
2521 * @param {Node} node The child node to be appended
2523 "beforeappend" : true,
2525 * @event beforeremove
2526 * Fires before a child is removed, return false to cancel the remove.
2527 * @param {Tree} tree The owner tree
2528 * @param {Node} this This node
2529 * @param {Node} node The child node to be removed
2531 "beforeremove" : true,
2534 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2535 * @param {Tree} tree The owner tree
2536 * @param {Node} this This node
2537 * @param {Node} oldParent The parent of this node
2538 * @param {Node} newParent The new parent this node is moving to
2539 * @param {Number} index The index it is being moved to
2541 "beforemove" : true,
2543 * @event beforeinsert
2544 * Fires before a new child is inserted, return false to cancel the insert.
2545 * @param {Tree} tree The owner tree
2546 * @param {Node} this This node
2547 * @param {Node} node The child node to be inserted
2548 * @param {Node} refNode The child node the node is being inserted before
2550 "beforeinsert" : true
2552 this.listeners = this.attributes.listeners;
2553 Roo.data.Node.superclass.constructor.call(this);
2556 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2557 fireEvent : function(evtName){
2558 // first do standard event for this node
2559 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2562 // then bubble it up to the tree if the event wasn't cancelled
2563 var ot = this.getOwnerTree();
2565 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2573 * Returns true if this node is a leaf
2576 isLeaf : function(){
2577 return this.leaf === true;
2581 setFirstChild : function(node){
2582 this.firstChild = node;
2586 setLastChild : function(node){
2587 this.lastChild = node;
2592 * Returns true if this node is the last child of its parent
2595 isLast : function(){
2596 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2600 * Returns true if this node is the first child of its parent
2603 isFirst : function(){
2604 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2607 hasChildNodes : function(){
2608 return !this.isLeaf() && this.childNodes.length > 0;
2612 * Insert node(s) as the last child node of this node.
2613 * @param {Node/Array} node The node or Array of nodes to append
2614 * @return {Node} The appended node if single append, or null if an array was passed
2616 appendChild : function(node){
2618 if(node instanceof Array){
2620 }else if(arguments.length > 1){
2624 // if passed an array or multiple args do them one by one
2626 for(var i = 0, len = multi.length; i < len; i++) {
2627 this.appendChild(multi[i]);
2630 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2633 var index = this.childNodes.length;
2634 var oldParent = node.parentNode;
2635 // it's a move, make sure we move it cleanly
2637 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2640 oldParent.removeChild(node);
2643 index = this.childNodes.length;
2645 this.setFirstChild(node);
2647 this.childNodes.push(node);
2648 node.parentNode = this;
2649 var ps = this.childNodes[index-1];
2651 node.previousSibling = ps;
2652 ps.nextSibling = node;
2654 node.previousSibling = null;
2656 node.nextSibling = null;
2657 this.setLastChild(node);
2658 node.setOwnerTree(this.getOwnerTree());
2659 this.fireEvent("append", this.ownerTree, this, node, index);
2660 if(this.ownerTree) {
2661 this.ownerTree.fireEvent("appendnode", this, node, index);
2664 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2671 * Removes a child node from this node.
2672 * @param {Node} node The node to remove
2673 * @return {Node} The removed node
2675 removeChild : function(node){
2676 var index = this.childNodes.indexOf(node);
2680 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2684 // remove it from childNodes collection
2685 this.childNodes.splice(index, 1);
2688 if(node.previousSibling){
2689 node.previousSibling.nextSibling = node.nextSibling;
2691 if(node.nextSibling){
2692 node.nextSibling.previousSibling = node.previousSibling;
2695 // update child refs
2696 if(this.firstChild == node){
2697 this.setFirstChild(node.nextSibling);
2699 if(this.lastChild == node){
2700 this.setLastChild(node.previousSibling);
2703 node.setOwnerTree(null);
2704 // clear any references from the node
2705 node.parentNode = null;
2706 node.previousSibling = null;
2707 node.nextSibling = null;
2708 this.fireEvent("remove", this.ownerTree, this, node);
2713 * Inserts the first node before the second node in this nodes childNodes collection.
2714 * @param {Node} node The node to insert
2715 * @param {Node} refNode The node to insert before (if null the node is appended)
2716 * @return {Node} The inserted node
2718 insertBefore : function(node, refNode){
2719 if(!refNode){ // like standard Dom, refNode can be null for append
2720 return this.appendChild(node);
2723 if(node == refNode){
2727 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2730 var index = this.childNodes.indexOf(refNode);
2731 var oldParent = node.parentNode;
2732 var refIndex = index;
2734 // when moving internally, indexes will change after remove
2735 if(oldParent == this && this.childNodes.indexOf(node) < index){
2739 // it's a move, make sure we move it cleanly
2741 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2744 oldParent.removeChild(node);
2747 this.setFirstChild(node);
2749 this.childNodes.splice(refIndex, 0, node);
2750 node.parentNode = this;
2751 var ps = this.childNodes[refIndex-1];
2753 node.previousSibling = ps;
2754 ps.nextSibling = node;
2756 node.previousSibling = null;
2758 node.nextSibling = refNode;
2759 refNode.previousSibling = node;
2760 node.setOwnerTree(this.getOwnerTree());
2761 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2763 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2769 * Returns the child node at the specified index.
2770 * @param {Number} index
2773 item : function(index){
2774 return this.childNodes[index];
2778 * Replaces one child node in this node with another.
2779 * @param {Node} newChild The replacement node
2780 * @param {Node} oldChild The node to replace
2781 * @return {Node} The replaced node
2783 replaceChild : function(newChild, oldChild){
2784 this.insertBefore(newChild, oldChild);
2785 this.removeChild(oldChild);
2790 * Returns the index of a child node
2791 * @param {Node} node
2792 * @return {Number} The index of the node or -1 if it was not found
2794 indexOf : function(child){
2795 return this.childNodes.indexOf(child);
2799 * Returns the tree this node is in.
2802 getOwnerTree : function(){
2803 // if it doesn't have one, look for one
2804 if(!this.ownerTree){
2808 this.ownerTree = p.ownerTree;
2814 return this.ownerTree;
2818 * Returns depth of this node (the root node has a depth of 0)
2821 getDepth : function(){
2824 while(p.parentNode){
2832 setOwnerTree : function(tree){
2833 // if it's move, we need to update everyone
2834 if(tree != this.ownerTree){
2836 this.ownerTree.unregisterNode(this);
2838 this.ownerTree = tree;
2839 var cs = this.childNodes;
2840 for(var i = 0, len = cs.length; i < len; i++) {
2841 cs[i].setOwnerTree(tree);
2844 tree.registerNode(this);
2850 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2851 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2852 * @return {String} The path
2854 getPath : function(attr){
2855 attr = attr || "id";
2856 var p = this.parentNode;
2857 var b = [this.attributes[attr]];
2859 b.unshift(p.attributes[attr]);
2862 var sep = this.getOwnerTree().pathSeparator;
2863 return sep + b.join(sep);
2867 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2868 * function call will be the scope provided or the current node. The arguments to the function
2869 * will be the args provided or the current node. If the function returns false at any point,
2870 * the bubble is stopped.
2871 * @param {Function} fn The function to call
2872 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2873 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2875 bubble : function(fn, scope, args){
2878 if(fn.call(scope || p, args || p) === false){
2886 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2887 * function call will be the scope provided or the current node. The arguments to the function
2888 * will be the args provided or the current node. If the function returns false at any point,
2889 * the cascade is stopped on that branch.
2890 * @param {Function} fn The function to call
2891 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2892 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2894 cascade : function(fn, scope, args){
2895 if(fn.call(scope || this, args || this) !== false){
2896 var cs = this.childNodes;
2897 for(var i = 0, len = cs.length; i < len; i++) {
2898 cs[i].cascade(fn, scope, args);
2904 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2905 * function call will be the scope provided or the current node. The arguments to the function
2906 * will be the args provided or the current node. If the function returns false at any point,
2907 * the iteration stops.
2908 * @param {Function} fn The function to call
2909 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2910 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2912 eachChild : function(fn, scope, args){
2913 var cs = this.childNodes;
2914 for(var i = 0, len = cs.length; i < len; i++) {
2915 if(fn.call(scope || this, args || cs[i]) === false){
2922 * Finds the first child that has the attribute with the specified value.
2923 * @param {String} attribute The attribute name
2924 * @param {Mixed} value The value to search for
2925 * @return {Node} The found child or null if none was found
2927 findChild : function(attribute, value){
2928 var cs = this.childNodes;
2929 for(var i = 0, len = cs.length; i < len; i++) {
2930 if(cs[i].attributes[attribute] == value){
2938 * Finds the first child by a custom function. The child matches if the function passed
2940 * @param {Function} fn
2941 * @param {Object} scope (optional)
2942 * @return {Node} The found child or null if none was found
2944 findChildBy : function(fn, scope){
2945 var cs = this.childNodes;
2946 for(var i = 0, len = cs.length; i < len; i++) {
2947 if(fn.call(scope||cs[i], cs[i]) === true){
2955 * Sorts this nodes children using the supplied sort function
2956 * @param {Function} fn
2957 * @param {Object} scope (optional)
2959 sort : function(fn, scope){
2960 var cs = this.childNodes;
2961 var len = cs.length;
2963 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2965 for(var i = 0; i < len; i++){
2967 n.previousSibling = cs[i-1];
2968 n.nextSibling = cs[i+1];
2970 this.setFirstChild(n);
2973 this.setLastChild(n);
2980 * Returns true if this node is an ancestor (at any point) of the passed node.
2981 * @param {Node} node
2984 contains : function(node){
2985 return node.isAncestor(this);
2989 * Returns true if the passed node is an ancestor (at any point) of this node.
2990 * @param {Node} node
2993 isAncestor : function(node){
2994 var p = this.parentNode;
3004 toString : function(){
3005 return "[Node"+(this.id?" "+this.id:"")+"]";
3009 * Ext JS Library 1.1.1
3010 * Copyright(c) 2006-2007, Ext JS, LLC.
3012 * Originally Released Under LGPL - original licence link has changed is not relivant.
3015 * <script type="text/javascript">
3021 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3022 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3023 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3025 * Create a new Shadow
3026 * @param {Object} config The config object
3028 Roo.Shadow = function(config){
3029 Roo.apply(this, config);
3030 if(typeof this.mode != "string"){
3031 this.mode = this.defaultMode;
3033 var o = this.offset, a = {h: 0};
3034 var rad = Math.floor(this.offset/2);
3035 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3041 a.l -= this.offset + rad;
3042 a.t -= this.offset + rad;
3053 a.l -= (this.offset - rad);
3054 a.t -= this.offset + rad;
3056 a.w -= (this.offset - rad)*2;
3067 a.l -= (this.offset - rad);
3068 a.t -= (this.offset - rad);
3070 a.w -= (this.offset + rad + 1);
3071 a.h -= (this.offset + rad);
3080 Roo.Shadow.prototype = {
3082 * @cfg {String} mode
3083 * The shadow display mode. Supports the following options:<br />
3084 * sides: Shadow displays on both sides and bottom only<br />
3085 * frame: Shadow displays equally on all four sides<br />
3086 * drop: Traditional bottom-right drop shadow (default)
3090 * @cfg {String} offset
3091 * The number of pixels to offset the shadow from the element (defaults to 4)
3096 defaultMode: "drop",
3099 * Displays the shadow under the target element
3100 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3102 show : function(target){
3103 target = Roo.get(target);
3105 this.el = Roo.Shadow.Pool.pull();
3106 if(this.el.dom.nextSibling != target.dom){
3107 this.el.insertBefore(target);
3110 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3112 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3115 target.getLeft(true),
3116 target.getTop(true),
3120 this.el.dom.style.display = "block";
3124 * Returns true if the shadow is visible, else false
3126 isVisible : function(){
3127 return this.el ? true : false;
3131 * Direct alignment when values are already available. Show must be called at least once before
3132 * calling this method to ensure it is initialized.
3133 * @param {Number} left The target element left position
3134 * @param {Number} top The target element top position
3135 * @param {Number} width The target element width
3136 * @param {Number} height The target element height
3138 realign : function(l, t, w, h){
3142 var a = this.adjusts, d = this.el.dom, s = d.style;
3144 s.left = (l+a.l)+"px";
3145 s.top = (t+a.t)+"px";
3146 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3148 if(s.width != sws || s.height != shs){
3152 var cn = d.childNodes;
3153 var sww = Math.max(0, (sw-12))+"px";
3154 cn[0].childNodes[1].style.width = sww;
3155 cn[1].childNodes[1].style.width = sww;
3156 cn[2].childNodes[1].style.width = sww;
3157 cn[1].style.height = Math.max(0, (sh-12))+"px";
3167 this.el.dom.style.display = "none";
3168 Roo.Shadow.Pool.push(this.el);
3174 * Adjust the z-index of this shadow
3175 * @param {Number} zindex The new z-index
3177 setZIndex : function(z){
3180 this.el.setStyle("z-index", z);
3185 // Private utility class that manages the internal Shadow cache
3186 Roo.Shadow.Pool = function(){
3188 var markup = Roo.isIE ?
3189 '<div class="x-ie-shadow"></div>' :
3190 '<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>';
3195 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3196 sh.autoBoxAdjust = false;
3201 push : function(sh){
3207 * Ext JS Library 1.1.1
3208 * Copyright(c) 2006-2007, Ext JS, LLC.
3210 * Originally Released Under LGPL - original licence link has changed is not relivant.
3213 * <script type="text/javascript">
3218 * @class Roo.SplitBar
3219 * @extends Roo.util.Observable
3220 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3224 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3225 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3226 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3227 split.minSize = 100;
3228 split.maxSize = 600;
3229 split.animate = true;
3230 split.on('moved', splitterMoved);
3233 * Create a new SplitBar
3234 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3235 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3236 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3237 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3238 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3239 position of the SplitBar).
3241 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3244 this.el = Roo.get(dragElement, true);
3245 this.el.dom.unselectable = "on";
3247 this.resizingEl = Roo.get(resizingElement, true);
3251 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3252 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3255 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3258 * The minimum size of the resizing element. (Defaults to 0)
3264 * The maximum size of the resizing element. (Defaults to 2000)
3267 this.maxSize = 2000;
3270 * Whether to animate the transition to the new size
3273 this.animate = false;
3276 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3279 this.useShim = false;
3286 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3288 this.proxy = Roo.get(existingProxy).dom;
3291 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3294 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3297 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3300 this.dragSpecs = {};
3303 * @private The adapter to use to positon and resize elements
3305 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3306 this.adapter.init(this);
3308 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3310 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3311 this.el.addClass("x-splitbar-h");
3314 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3315 this.el.addClass("x-splitbar-v");
3321 * Fires when the splitter is moved (alias for {@link #event-moved})
3322 * @param {Roo.SplitBar} this
3323 * @param {Number} newSize the new width or height
3328 * Fires when the splitter is moved
3329 * @param {Roo.SplitBar} this
3330 * @param {Number} newSize the new width or height
3334 * @event beforeresize
3335 * Fires before the splitter is dragged
3336 * @param {Roo.SplitBar} this
3338 "beforeresize" : true,
3340 "beforeapply" : true
3343 Roo.util.Observable.call(this);
3346 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3347 onStartProxyDrag : function(x, y){
3348 this.fireEvent("beforeresize", this);
3350 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3352 o.enableDisplayMode("block");
3353 // all splitbars share the same overlay
3354 Roo.SplitBar.prototype.overlay = o;
3356 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3357 this.overlay.show();
3358 Roo.get(this.proxy).setDisplayed("block");
3359 var size = this.adapter.getElementSize(this);
3360 this.activeMinSize = this.getMinimumSize();;
3361 this.activeMaxSize = this.getMaximumSize();;
3362 var c1 = size - this.activeMinSize;
3363 var c2 = Math.max(this.activeMaxSize - size, 0);
3364 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365 this.dd.resetConstraints();
3366 this.dd.setXConstraint(
3367 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3368 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3370 this.dd.setYConstraint(0, 0);
3372 this.dd.resetConstraints();
3373 this.dd.setXConstraint(0, 0);
3374 this.dd.setYConstraint(
3375 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3376 this.placement == Roo.SplitBar.TOP ? c2 : c1
3379 this.dragSpecs.startSize = size;
3380 this.dragSpecs.startPoint = [x, y];
3381 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3385 * @private Called after the drag operation by the DDProxy
3387 onEndProxyDrag : function(e){
3388 Roo.get(this.proxy).setDisplayed(false);
3389 var endPoint = Roo.lib.Event.getXY(e);
3391 this.overlay.hide();
3394 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3395 newSize = this.dragSpecs.startSize +
3396 (this.placement == Roo.SplitBar.LEFT ?
3397 endPoint[0] - this.dragSpecs.startPoint[0] :
3398 this.dragSpecs.startPoint[0] - endPoint[0]
3401 newSize = this.dragSpecs.startSize +
3402 (this.placement == Roo.SplitBar.TOP ?
3403 endPoint[1] - this.dragSpecs.startPoint[1] :
3404 this.dragSpecs.startPoint[1] - endPoint[1]
3407 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3408 if(newSize != this.dragSpecs.startSize){
3409 if(this.fireEvent('beforeapply', this, newSize) !== false){
3410 this.adapter.setElementSize(this, newSize);
3411 this.fireEvent("moved", this, newSize);
3412 this.fireEvent("resize", this, newSize);
3418 * Get the adapter this SplitBar uses
3419 * @return The adapter object
3421 getAdapter : function(){
3422 return this.adapter;
3426 * Set the adapter this SplitBar uses
3427 * @param {Object} adapter A SplitBar adapter object
3429 setAdapter : function(adapter){
3430 this.adapter = adapter;
3431 this.adapter.init(this);
3435 * Gets the minimum size for the resizing element
3436 * @return {Number} The minimum size
3438 getMinimumSize : function(){
3439 return this.minSize;
3443 * Sets the minimum size for the resizing element
3444 * @param {Number} minSize The minimum size
3446 setMinimumSize : function(minSize){
3447 this.minSize = minSize;
3451 * Gets the maximum size for the resizing element
3452 * @return {Number} The maximum size
3454 getMaximumSize : function(){
3455 return this.maxSize;
3459 * Sets the maximum size for the resizing element
3460 * @param {Number} maxSize The maximum size
3462 setMaximumSize : function(maxSize){
3463 this.maxSize = maxSize;
3467 * Sets the initialize size for the resizing element
3468 * @param {Number} size The initial size
3470 setCurrentSize : function(size){
3471 var oldAnimate = this.animate;
3472 this.animate = false;
3473 this.adapter.setElementSize(this, size);
3474 this.animate = oldAnimate;
3478 * Destroy this splitbar.
3479 * @param {Boolean} removeEl True to remove the element
3481 destroy : function(removeEl){
3486 this.proxy.parentNode.removeChild(this.proxy);
3494 * @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.
3496 Roo.SplitBar.createProxy = function(dir){
3497 var proxy = new Roo.Element(document.createElement("div"));
3498 proxy.unselectable();
3499 var cls = 'x-splitbar-proxy';
3500 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3501 document.body.appendChild(proxy.dom);
3506 * @class Roo.SplitBar.BasicLayoutAdapter
3507 * Default Adapter. It assumes the splitter and resizing element are not positioned
3508 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3510 Roo.SplitBar.BasicLayoutAdapter = function(){
3513 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3514 // do nothing for now
3519 * Called before drag operations to get the current size of the resizing element.
3520 * @param {Roo.SplitBar} s The SplitBar using this adapter
3522 getElementSize : function(s){
3523 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3524 return s.resizingEl.getWidth();
3526 return s.resizingEl.getHeight();
3531 * Called after drag operations to set the size of the resizing element.
3532 * @param {Roo.SplitBar} s The SplitBar using this adapter
3533 * @param {Number} newSize The new size to set
3534 * @param {Function} onComplete A function to be invoked when resizing is complete
3536 setElementSize : function(s, newSize, onComplete){
3537 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3539 s.resizingEl.setWidth(newSize);
3541 onComplete(s, newSize);
3544 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3549 s.resizingEl.setHeight(newSize);
3551 onComplete(s, newSize);
3554 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3561 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3562 * @extends Roo.SplitBar.BasicLayoutAdapter
3563 * Adapter that moves the splitter element to align with the resized sizing element.
3564 * Used with an absolute positioned SplitBar.
3565 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3566 * document.body, make sure you assign an id to the body element.
3568 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3569 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3570 this.container = Roo.get(container);
3573 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3578 getElementSize : function(s){
3579 return this.basic.getElementSize(s);
3582 setElementSize : function(s, newSize, onComplete){
3583 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3586 moveSplitter : function(s){
3587 var yes = Roo.SplitBar;
3588 switch(s.placement){
3590 s.el.setX(s.resizingEl.getRight());
3593 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3596 s.el.setY(s.resizingEl.getBottom());
3599 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3606 * Orientation constant - Create a vertical SplitBar
3610 Roo.SplitBar.VERTICAL = 1;
3613 * Orientation constant - Create a horizontal SplitBar
3617 Roo.SplitBar.HORIZONTAL = 2;
3620 * Placement constant - The resizing element is to the left of the splitter element
3624 Roo.SplitBar.LEFT = 1;
3627 * Placement constant - The resizing element is to the right of the splitter element
3631 Roo.SplitBar.RIGHT = 2;
3634 * Placement constant - The resizing element is positioned above the splitter element
3638 Roo.SplitBar.TOP = 3;
3641 * Placement constant - The resizing element is positioned under splitter element
3645 Roo.SplitBar.BOTTOM = 4;
3648 * Ext JS Library 1.1.1
3649 * Copyright(c) 2006-2007, Ext JS, LLC.
3651 * Originally Released Under LGPL - original licence link has changed is not relivant.
3654 * <script type="text/javascript">
3659 * @extends Roo.util.Observable
3660 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
3661 * This class also supports single and multi selection modes. <br>
3662 * Create a data model bound view:
3664 var store = new Roo.data.Store(...);
3666 var view = new Roo.View({
3668 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
3671 selectedClass: "ydataview-selected",
3675 // listen for node click?
3676 view.on("click", function(vw, index, node, e){
3677 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3681 dataModel.load("foobar.xml");
3683 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3685 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3686 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3688 * Note: old style constructor is still suported (container, template, config)
3692 * @param {Object} config The config object
3695 Roo.View = function(config, depreciated_tpl, depreciated_config){
3697 this.parent = false;
3699 if (typeof(depreciated_tpl) == 'undefined') {
3700 // new way.. - universal constructor.
3701 Roo.apply(this, config);
3702 this.el = Roo.get(this.el);
3705 this.el = Roo.get(config);
3706 this.tpl = depreciated_tpl;
3707 Roo.apply(this, depreciated_config);
3709 this.wrapEl = this.el.wrap().wrap();
3710 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3713 if(typeof(this.tpl) == "string"){
3714 this.tpl = new Roo.Template(this.tpl);
3716 // support xtype ctors..
3717 this.tpl = new Roo.factory(this.tpl, Roo);
3726 * @event beforeclick
3727 * Fires before a click is processed. Returns false to cancel the default action.
3728 * @param {Roo.View} this
3729 * @param {Number} index The index of the target node
3730 * @param {HTMLElement} node The target node
3731 * @param {Roo.EventObject} e The raw event object
3733 "beforeclick" : true,
3736 * Fires when a template node is clicked.
3737 * @param {Roo.View} this
3738 * @param {Number} index The index of the target node
3739 * @param {HTMLElement} node The target node
3740 * @param {Roo.EventObject} e The raw event object
3745 * Fires when a template node is double clicked.
3746 * @param {Roo.View} this
3747 * @param {Number} index The index of the target node
3748 * @param {HTMLElement} node The target node
3749 * @param {Roo.EventObject} e The raw event object
3753 * @event contextmenu
3754 * Fires when a template node is right clicked.
3755 * @param {Roo.View} this
3756 * @param {Number} index The index of the target node
3757 * @param {HTMLElement} node The target node
3758 * @param {Roo.EventObject} e The raw event object
3760 "contextmenu" : true,
3762 * @event selectionchange
3763 * Fires when the selected nodes change.
3764 * @param {Roo.View} this
3765 * @param {Array} selections Array of the selected nodes
3767 "selectionchange" : true,
3770 * @event beforeselect
3771 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3772 * @param {Roo.View} this
3773 * @param {HTMLElement} node The node to be selected
3774 * @param {Array} selections Array of currently selected nodes
3776 "beforeselect" : true,
3778 * @event preparedata
3779 * Fires on every row to render, to allow you to change the data.
3780 * @param {Roo.View} this
3781 * @param {Object} data to be rendered (change this)
3783 "preparedata" : true
3791 "click": this.onClick,
3792 "dblclick": this.onDblClick,
3793 "contextmenu": this.onContextMenu,
3797 this.selections = [];
3799 this.cmp = new Roo.CompositeElementLite([]);
3801 this.store = Roo.factory(this.store, Roo.data);
3802 this.setStore(this.store, true);
3805 if ( this.footer && this.footer.xtype) {
3807 var fctr = this.wrapEl.appendChild(document.createElement("div"));
3809 this.footer.dataSource = this.store;
3810 this.footer.container = fctr;
3811 this.footer = Roo.factory(this.footer, Roo);
3812 fctr.insertFirst(this.el);
3814 // this is a bit insane - as the paging toolbar seems to detach the el..
3815 // dom.parentNode.parentNode.parentNode
3816 // they get detached?
3820 Roo.View.superclass.constructor.call(this);
3825 Roo.extend(Roo.View, Roo.util.Observable, {
3828 * @cfg {Roo.data.Store} store Data store to load data from.
3833 * @cfg {String|Roo.Element} el The container element.
3838 * @cfg {String|Roo.Template} tpl The template used by this View
3842 * @cfg {String} dataName the named area of the template to use as the data area
3843 * Works with domtemplates roo-name="name"
3847 * @cfg {String} selectedClass The css class to add to selected nodes
3849 selectedClass : "x-view-selected",
3851 * @cfg {String} emptyText The empty text to show when nothing is loaded.
3856 * @cfg {String} text to display on mask (default Loading)
3860 * @cfg {Boolean} multiSelect Allow multiple selection
3862 multiSelect : false,
3864 * @cfg {Boolean} singleSelect Allow single selection
3866 singleSelect: false,
3869 * @cfg {Boolean} toggleSelect - selecting
3871 toggleSelect : false,
3874 * @cfg {Boolean} tickable - selecting
3879 * Returns the element this view is bound to.
3880 * @return {Roo.Element}
3889 * Refreshes the view. - called by datachanged on the store. - do not call directly.
3891 refresh : function(){
3892 //Roo.log('refresh');
3895 // if we are using something like 'domtemplate', then
3896 // the what gets used is:
3897 // t.applySubtemplate(NAME, data, wrapping data..)
3898 // the outer template then get' applied with
3899 // the store 'extra data'
3900 // and the body get's added to the
3901 // roo-name="data" node?
3902 // <span class='roo-tpl-{name}'></span> ?????
3906 this.clearSelections();
3909 var records = this.store.getRange();
3910 if(records.length < 1) {
3912 // is this valid?? = should it render a template??
3914 this.el.update(this.emptyText);
3918 if (this.dataName) {
3919 this.el.update(t.apply(this.store.meta)); //????
3920 el = this.el.child('.roo-tpl-' + this.dataName);
3923 for(var i = 0, len = records.length; i < len; i++){
3924 var data = this.prepareData(records[i].data, i, records[i]);
3925 this.fireEvent("preparedata", this, data, i, records[i]);
3927 var d = Roo.apply({}, data);
3930 Roo.apply(d, {'roo-id' : Roo.id()});
3934 Roo.each(this.parent.item, function(item){
3935 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3938 Roo.apply(d, {'roo-data-checked' : 'checked'});
3942 html[html.length] = Roo.util.Format.trim(
3944 t.applySubtemplate(this.dataName, d, this.store.meta) :
3951 el.update(html.join(""));
3952 this.nodes = el.dom.childNodes;
3953 this.updateIndexes(0);
3958 * Function to override to reformat the data that is sent to
3959 * the template for each node.
3960 * DEPRICATED - use the preparedata event handler.
3961 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3962 * a JSON object for an UpdateManager bound view).
3964 prepareData : function(data, index, record)
3966 this.fireEvent("preparedata", this, data, index, record);
3970 onUpdate : function(ds, record){
3971 // Roo.log('on update');
3972 this.clearSelections();
3973 var index = this.store.indexOf(record);
3974 var n = this.nodes[index];
3975 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3976 n.parentNode.removeChild(n);
3977 this.updateIndexes(index, index);
3983 onAdd : function(ds, records, index)
3985 //Roo.log(['on Add', ds, records, index] );
3986 this.clearSelections();
3987 if(this.nodes.length == 0){
3991 var n = this.nodes[index];
3992 for(var i = 0, len = records.length; i < len; i++){
3993 var d = this.prepareData(records[i].data, i, records[i]);
3995 this.tpl.insertBefore(n, d);
3998 this.tpl.append(this.el, d);
4001 this.updateIndexes(index);
4004 onRemove : function(ds, record, index){
4005 // Roo.log('onRemove');
4006 this.clearSelections();
4007 var el = this.dataName ?
4008 this.el.child('.roo-tpl-' + this.dataName) :
4011 el.dom.removeChild(this.nodes[index]);
4012 this.updateIndexes(index);
4016 * Refresh an individual node.
4017 * @param {Number} index
4019 refreshNode : function(index){
4020 this.onUpdate(this.store, this.store.getAt(index));
4023 updateIndexes : function(startIndex, endIndex){
4024 var ns = this.nodes;
4025 startIndex = startIndex || 0;
4026 endIndex = endIndex || ns.length - 1;
4027 for(var i = startIndex; i <= endIndex; i++){
4028 ns[i].nodeIndex = i;
4033 * Changes the data store this view uses and refresh the view.
4034 * @param {Store} store
4036 setStore : function(store, initial){
4037 if(!initial && this.store){
4038 this.store.un("datachanged", this.refresh);
4039 this.store.un("add", this.onAdd);
4040 this.store.un("remove", this.onRemove);
4041 this.store.un("update", this.onUpdate);
4042 this.store.un("clear", this.refresh);
4043 this.store.un("beforeload", this.onBeforeLoad);
4044 this.store.un("load", this.onLoad);
4045 this.store.un("loadexception", this.onLoad);
4049 store.on("datachanged", this.refresh, this);
4050 store.on("add", this.onAdd, this);
4051 store.on("remove", this.onRemove, this);
4052 store.on("update", this.onUpdate, this);
4053 store.on("clear", this.refresh, this);
4054 store.on("beforeload", this.onBeforeLoad, this);
4055 store.on("load", this.onLoad, this);
4056 store.on("loadexception", this.onLoad, this);
4064 * onbeforeLoad - masks the loading area.
4067 onBeforeLoad : function(store,opts)
4069 //Roo.log('onBeforeLoad');
4073 this.el.mask(this.mask ? this.mask : "Loading" );
4075 onLoad : function ()
4082 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4083 * @param {HTMLElement} node
4084 * @return {HTMLElement} The template node
4086 findItemFromChild : function(node){
4087 var el = this.dataName ?
4088 this.el.child('.roo-tpl-' + this.dataName,true) :
4091 if(!node || node.parentNode == el){
4094 var p = node.parentNode;
4095 while(p && p != el){
4096 if(p.parentNode == el){
4105 onClick : function(e){
4106 var item = this.findItemFromChild(e.getTarget());
4108 var index = this.indexOf(item);
4109 if(this.onItemClick(item, index, e) !== false){
4110 this.fireEvent("click", this, index, item, e);
4113 this.clearSelections();
4118 onContextMenu : function(e){
4119 var item = this.findItemFromChild(e.getTarget());
4121 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4126 onDblClick : function(e){
4127 var item = this.findItemFromChild(e.getTarget());
4129 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4133 onItemClick : function(item, index, e)
4135 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4138 if (this.toggleSelect) {
4139 var m = this.isSelected(item) ? 'unselect' : 'select';
4142 _t[m](item, true, false);
4145 if(this.multiSelect || this.singleSelect){
4146 if(this.multiSelect && e.shiftKey && this.lastSelection){
4147 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4149 this.select(item, this.multiSelect && e.ctrlKey);
4150 this.lastSelection = item;
4162 * Get the number of selected nodes.
4165 getSelectionCount : function(){
4166 return this.selections.length;
4170 * Get the currently selected nodes.
4171 * @return {Array} An array of HTMLElements
4173 getSelectedNodes : function(){
4174 return this.selections;
4178 * Get the indexes of the selected nodes.
4181 getSelectedIndexes : function(){
4182 var indexes = [], s = this.selections;
4183 for(var i = 0, len = s.length; i < len; i++){
4184 indexes.push(s[i].nodeIndex);
4190 * Clear all selections
4191 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4193 clearSelections : function(suppressEvent){
4194 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4195 this.cmp.elements = this.selections;
4196 this.cmp.removeClass(this.selectedClass);
4197 this.selections = [];
4199 this.fireEvent("selectionchange", this, this.selections);
4205 * Returns true if the passed node is selected
4206 * @param {HTMLElement/Number} node The node or node index
4209 isSelected : function(node){
4210 var s = this.selections;
4214 node = this.getNode(node);
4215 return s.indexOf(node) !== -1;
4220 * @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
4221 * @param {Boolean} keepExisting (optional) true to keep existing selections
4222 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4224 select : function(nodeInfo, keepExisting, suppressEvent){
4225 if(nodeInfo instanceof Array){
4227 this.clearSelections(true);
4229 for(var i = 0, len = nodeInfo.length; i < len; i++){
4230 this.select(nodeInfo[i], true, true);
4234 var node = this.getNode(nodeInfo);
4235 if(!node || this.isSelected(node)){
4236 return; // already selected.
4239 this.clearSelections(true);
4242 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4243 Roo.fly(node).addClass(this.selectedClass);
4244 this.selections.push(node);
4246 this.fireEvent("selectionchange", this, this.selections);
4254 * @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
4255 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4256 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4258 unselect : function(nodeInfo, keepExisting, suppressEvent)
4260 if(nodeInfo instanceof Array){
4261 Roo.each(this.selections, function(s) {
4262 this.unselect(s, nodeInfo);
4266 var node = this.getNode(nodeInfo);
4267 if(!node || !this.isSelected(node)){
4268 //Roo.log("not selected");
4269 return; // not selected.
4273 Roo.each(this.selections, function(s) {
4275 Roo.fly(node).removeClass(this.selectedClass);
4282 this.selections= ns;
4283 this.fireEvent("selectionchange", this, this.selections);
4287 * Gets a template node.
4288 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4289 * @return {HTMLElement} The node or null if it wasn't found
4291 getNode : function(nodeInfo){
4292 if(typeof nodeInfo == "string"){
4293 return document.getElementById(nodeInfo);
4294 }else if(typeof nodeInfo == "number"){
4295 return this.nodes[nodeInfo];
4301 * Gets a range template nodes.
4302 * @param {Number} startIndex
4303 * @param {Number} endIndex
4304 * @return {Array} An array of nodes
4306 getNodes : function(start, end){
4307 var ns = this.nodes;
4309 end = typeof end == "undefined" ? ns.length - 1 : end;
4312 for(var i = start; i <= end; i++){
4316 for(var i = start; i >= end; i--){
4324 * Finds the index of the passed node
4325 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4326 * @return {Number} The index of the node or -1
4328 indexOf : function(node){
4329 node = this.getNode(node);
4330 if(typeof node.nodeIndex == "number"){
4331 return node.nodeIndex;
4333 var ns = this.nodes;
4334 for(var i = 0, len = ns.length; i < len; i++){
4344 * Ext JS Library 1.1.1
4345 * Copyright(c) 2006-2007, Ext JS, LLC.
4347 * Originally Released Under LGPL - original licence link has changed is not relivant.
4350 * <script type="text/javascript">
4354 * @class Roo.JsonView
4356 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4358 var view = new Roo.JsonView({
4359 container: "my-element",
4360 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4365 // listen for node click?
4366 view.on("click", function(vw, index, node, e){
4367 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4370 // direct load of JSON data
4371 view.load("foobar.php");
4373 // Example from my blog list
4374 var tpl = new Roo.Template(
4375 '<div class="entry">' +
4376 '<a class="entry-title" href="{link}">{title}</a>' +
4377 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4378 "</div><hr />"
4381 var moreView = new Roo.JsonView({
4382 container : "entry-list",
4386 moreView.on("beforerender", this.sortEntries, this);
4388 url: "/blog/get-posts.php",
4389 params: "allposts=true",
4390 text: "Loading Blog Entries..."
4394 * Note: old code is supported with arguments : (container, template, config)
4398 * Create a new JsonView
4400 * @param {Object} config The config object
4403 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4406 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4408 var um = this.el.getUpdateManager();
4409 um.setRenderer(this);
4410 um.on("update", this.onLoad, this);
4411 um.on("failure", this.onLoadException, this);
4414 * @event beforerender
4415 * Fires before rendering of the downloaded JSON data.
4416 * @param {Roo.JsonView} this
4417 * @param {Object} data The JSON data loaded
4421 * Fires when data is loaded.
4422 * @param {Roo.JsonView} this
4423 * @param {Object} data The JSON data loaded
4424 * @param {Object} response The raw Connect response object
4427 * @event loadexception
4428 * Fires when loading fails.
4429 * @param {Roo.JsonView} this
4430 * @param {Object} response The raw Connect response object
4433 'beforerender' : true,
4435 'loadexception' : true
4438 Roo.extend(Roo.JsonView, Roo.View, {
4440 * @type {String} The root property in the loaded JSON object that contains the data
4445 * Refreshes the view.
4447 refresh : function(){
4448 this.clearSelections();
4451 var o = this.jsonData;
4452 if(o && o.length > 0){
4453 for(var i = 0, len = o.length; i < len; i++){
4454 var data = this.prepareData(o[i], i, o);
4455 html[html.length] = this.tpl.apply(data);
4458 html.push(this.emptyText);
4460 this.el.update(html.join(""));
4461 this.nodes = this.el.dom.childNodes;
4462 this.updateIndexes(0);
4466 * 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.
4467 * @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:
4470 url: "your-url.php",
4471 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4472 callback: yourFunction,
4473 scope: yourObject, //(optional scope)
4481 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4482 * 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.
4483 * @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}
4484 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4485 * @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.
4488 var um = this.el.getUpdateManager();
4489 um.update.apply(um, arguments);
4492 // note - render is a standard framework call...
4493 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4494 render : function(el, response){
4496 this.clearSelections();
4500 if (response != '') {
4501 o = Roo.util.JSON.decode(response.responseText);
4504 o = o[this.jsonRoot];
4510 * The current JSON data or null
4513 this.beforeRender();
4518 * Get the number of records in the current JSON dataset
4521 getCount : function(){
4522 return this.jsonData ? this.jsonData.length : 0;
4526 * Returns the JSON object for the specified node(s)
4527 * @param {HTMLElement/Array} node The node or an array of nodes
4528 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4529 * you get the JSON object for the node
4531 getNodeData : function(node){
4532 if(node instanceof Array){
4534 for(var i = 0, len = node.length; i < len; i++){
4535 data.push(this.getNodeData(node[i]));
4539 return this.jsonData[this.indexOf(node)] || null;
4542 beforeRender : function(){
4543 this.snapshot = this.jsonData;
4545 this.sort.apply(this, this.sortInfo);
4547 this.fireEvent("beforerender", this, this.jsonData);
4550 onLoad : function(el, o){
4551 this.fireEvent("load", this, this.jsonData, o);
4554 onLoadException : function(el, o){
4555 this.fireEvent("loadexception", this, o);
4559 * Filter the data by a specific property.
4560 * @param {String} property A property on your JSON objects
4561 * @param {String/RegExp} value Either string that the property values
4562 * should start with, or a RegExp to test against the property
4564 filter : function(property, value){
4567 var ss = this.snapshot;
4568 if(typeof value == "string"){
4569 var vlen = value.length;
4574 value = value.toLowerCase();
4575 for(var i = 0, len = ss.length; i < len; i++){
4577 if(o[property].substr(0, vlen).toLowerCase() == value){
4581 } else if(value.exec){ // regex?
4582 for(var i = 0, len = ss.length; i < len; i++){
4584 if(value.test(o[property])){
4591 this.jsonData = data;
4597 * Filter by a function. The passed function will be called with each
4598 * object in the current dataset. If the function returns true the value is kept,
4599 * otherwise it is filtered.
4600 * @param {Function} fn
4601 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4603 filterBy : function(fn, scope){
4606 var ss = this.snapshot;
4607 for(var i = 0, len = ss.length; i < len; i++){
4609 if(fn.call(scope || this, o)){
4613 this.jsonData = data;
4619 * Clears the current filter.
4621 clearFilter : function(){
4622 if(this.snapshot && this.jsonData != this.snapshot){
4623 this.jsonData = this.snapshot;
4630 * Sorts the data for this view and refreshes it.
4631 * @param {String} property A property on your JSON objects to sort on
4632 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4633 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4635 sort : function(property, dir, sortType){
4636 this.sortInfo = Array.prototype.slice.call(arguments, 0);
4639 var dsc = dir && dir.toLowerCase() == "desc";
4640 var f = function(o1, o2){
4641 var v1 = sortType ? sortType(o1[p]) : o1[p];
4642 var v2 = sortType ? sortType(o2[p]) : o2[p];
4645 return dsc ? +1 : -1;
4647 return dsc ? -1 : +1;
4652 this.jsonData.sort(f);
4654 if(this.jsonData != this.snapshot){
4655 this.snapshot.sort(f);
4661 * Ext JS Library 1.1.1
4662 * Copyright(c) 2006-2007, Ext JS, LLC.
4664 * Originally Released Under LGPL - original licence link has changed is not relivant.
4667 * <script type="text/javascript">
4672 * @class Roo.ColorPalette
4673 * @extends Roo.Component
4674 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
4675 * Here's an example of typical usage:
4677 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
4678 cp.render('my-div');
4680 cp.on('select', function(palette, selColor){
4681 // do something with selColor
4685 * Create a new ColorPalette
4686 * @param {Object} config The config object
4688 Roo.ColorPalette = function(config){
4689 Roo.ColorPalette.superclass.constructor.call(this, config);
4693 * Fires when a color is selected
4694 * @param {ColorPalette} this
4695 * @param {String} color The 6-digit color hex code (without the # symbol)
4701 this.on("select", this.handler, this.scope, true);
4704 Roo.extend(Roo.ColorPalette, Roo.Component, {
4706 * @cfg {String} itemCls
4707 * The CSS class to apply to the containing element (defaults to "x-color-palette")
4709 itemCls : "x-color-palette",
4711 * @cfg {String} value
4712 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
4713 * the hex codes are case-sensitive.
4718 ctype: "Roo.ColorPalette",
4721 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4723 allowReselect : false,
4726 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
4727 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
4728 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4729 * of colors with the width setting until the box is symmetrical.</p>
4730 * <p>You can override individual colors if needed:</p>
4732 var cp = new Roo.ColorPalette();
4733 cp.colors[0] = "FF0000"; // change the first box to red
4736 Or you can provide a custom array of your own for complete control:
4738 var cp = new Roo.ColorPalette();
4739 cp.colors = ["000000", "993300", "333300"];
4744 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4745 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4746 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4747 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4748 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4752 onRender : function(container, position){
4753 var t = new Roo.MasterTemplate(
4754 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
4756 var c = this.colors;
4757 for(var i = 0, len = c.length; i < len; i++){
4760 var el = document.createElement("div");
4761 el.className = this.itemCls;
4763 container.dom.insertBefore(el, position);
4764 this.el = Roo.get(el);
4765 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
4766 if(this.clickEvent != 'click'){
4767 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
4772 afterRender : function(){
4773 Roo.ColorPalette.superclass.afterRender.call(this);
4782 handleClick : function(e, t){
4785 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4786 this.select(c.toUpperCase());
4791 * Selects the specified color in the palette (fires the select event)
4792 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4794 select : function(color){
4795 color = color.replace("#", "");
4796 if(color != this.value || this.allowReselect){
4799 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4801 el.child("a.color-"+color).addClass("x-color-palette-sel");
4803 this.fireEvent("select", this, color);
4808 * Ext JS Library 1.1.1
4809 * Copyright(c) 2006-2007, Ext JS, LLC.
4811 * Originally Released Under LGPL - original licence link has changed is not relivant.
4814 * <script type="text/javascript">
4818 * @class Roo.DatePicker
4819 * @extends Roo.Component
4820 * Simple date picker class.
4822 * Create a new DatePicker
4823 * @param {Object} config The config object
4825 Roo.DatePicker = function(config){
4826 Roo.DatePicker.superclass.constructor.call(this, config);
4828 this.value = config && config.value ?
4829 config.value.clearTime() : new Date().clearTime();
4834 * Fires when a date is selected
4835 * @param {DatePicker} this
4836 * @param {Date} date The selected date
4840 * @event monthchange
4841 * Fires when the displayed month changes
4842 * @param {DatePicker} this
4843 * @param {Date} date The selected month
4849 this.on("select", this.handler, this.scope || this);
4851 // build the disabledDatesRE
4852 if(!this.disabledDatesRE && this.disabledDates){
4853 var dd = this.disabledDates;
4855 for(var i = 0; i < dd.length; i++){
4857 if(i != dd.length-1) {
4861 this.disabledDatesRE = new RegExp(re + ")");
4865 Roo.extend(Roo.DatePicker, Roo.Component, {
4867 * @cfg {String} todayText
4868 * The text to display on the button that selects the current date (defaults to "Today")
4870 todayText : "Today",
4872 * @cfg {String} okText
4873 * The text to display on the ok button
4875 okText : " OK ", //   to give the user extra clicking room
4877 * @cfg {String} cancelText
4878 * The text to display on the cancel button
4880 cancelText : "Cancel",
4882 * @cfg {String} todayTip
4883 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4885 todayTip : "{0} (Spacebar)",
4887 * @cfg {Date} minDate
4888 * Minimum allowable date (JavaScript date object, defaults to null)
4892 * @cfg {Date} maxDate
4893 * Maximum allowable date (JavaScript date object, defaults to null)
4897 * @cfg {String} minText
4898 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4900 minText : "This date is before the minimum date",
4902 * @cfg {String} maxText
4903 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4905 maxText : "This date is after the maximum date",
4907 * @cfg {String} format
4908 * The default date format string which can be overriden for localization support. The format must be
4909 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4913 * @cfg {Array} disabledDays
4914 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4916 disabledDays : null,
4918 * @cfg {String} disabledDaysText
4919 * The tooltip to display when the date falls on a disabled day (defaults to "")
4921 disabledDaysText : "",
4923 * @cfg {RegExp} disabledDatesRE
4924 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4926 disabledDatesRE : null,
4928 * @cfg {String} disabledDatesText
4929 * The tooltip text to display when the date falls on a disabled date (defaults to "")
4931 disabledDatesText : "",
4933 * @cfg {Boolean} constrainToViewport
4934 * True to constrain the date picker to the viewport (defaults to true)
4936 constrainToViewport : true,
4938 * @cfg {Array} monthNames
4939 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4941 monthNames : Date.monthNames,
4943 * @cfg {Array} dayNames
4944 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4946 dayNames : Date.dayNames,
4948 * @cfg {String} nextText
4949 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4951 nextText: 'Next Month (Control+Right)',
4953 * @cfg {String} prevText
4954 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4956 prevText: 'Previous Month (Control+Left)',
4958 * @cfg {String} monthYearText
4959 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4961 monthYearText: 'Choose a month (Control+Up/Down to move years)',
4963 * @cfg {Number} startDay
4964 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4968 * @cfg {Bool} showClear
4969 * Show a clear button (usefull for date form elements that can be blank.)
4975 * Sets the value of the date field
4976 * @param {Date} value The date to set
4978 setValue : function(value){
4979 var old = this.value;
4981 if (typeof(value) == 'string') {
4983 value = Date.parseDate(value, this.format);
4989 this.value = value.clearTime(true);
4991 this.update(this.value);
4996 * Gets the current selected value of the date field
4997 * @return {Date} The selected date
4999 getValue : function(){
5006 this.update(this.activeDate);
5011 onRender : function(container, position){
5014 '<table cellspacing="0">',
5015 '<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>',
5016 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5017 var dn = this.dayNames;
5018 for(var i = 0; i < 7; i++){
5019 var d = this.startDay+i;
5023 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5025 m[m.length] = "</tr></thead><tbody><tr>";
5026 for(var i = 0; i < 42; i++) {
5027 if(i % 7 == 0 && i != 0){
5028 m[m.length] = "</tr><tr>";
5030 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5032 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5033 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5035 var el = document.createElement("div");
5036 el.className = "x-date-picker";
5037 el.innerHTML = m.join("");
5039 container.dom.insertBefore(el, position);
5041 this.el = Roo.get(el);
5042 this.eventEl = Roo.get(el.firstChild);
5044 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5045 handler: this.showPrevMonth,
5047 preventDefault:true,
5051 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5052 handler: this.showNextMonth,
5054 preventDefault:true,
5058 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5060 this.monthPicker = this.el.down('div.x-date-mp');
5061 this.monthPicker.enableDisplayMode('block');
5063 var kn = new Roo.KeyNav(this.eventEl, {
5064 "left" : function(e){
5066 this.showPrevMonth() :
5067 this.update(this.activeDate.add("d", -1));
5070 "right" : function(e){
5072 this.showNextMonth() :
5073 this.update(this.activeDate.add("d", 1));
5078 this.showNextYear() :
5079 this.update(this.activeDate.add("d", -7));
5082 "down" : function(e){
5084 this.showPrevYear() :
5085 this.update(this.activeDate.add("d", 7));
5088 "pageUp" : function(e){
5089 this.showNextMonth();
5092 "pageDown" : function(e){
5093 this.showPrevMonth();
5096 "enter" : function(e){
5097 e.stopPropagation();
5104 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5106 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5108 this.el.unselectable();
5110 this.cells = this.el.select("table.x-date-inner tbody td");
5111 this.textNodes = this.el.query("table.x-date-inner tbody span");
5113 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5115 tooltip: this.monthYearText
5118 this.mbtn.on('click', this.showMonthPicker, this);
5119 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5122 var today = (new Date()).dateFormat(this.format);
5124 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5125 if (this.showClear) {
5126 baseTb.add( new Roo.Toolbar.Fill());
5129 text: String.format(this.todayText, today),
5130 tooltip: String.format(this.todayTip, today),
5131 handler: this.selectToday,
5135 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5138 if (this.showClear) {
5140 baseTb.add( new Roo.Toolbar.Fill());
5143 cls: 'x-btn-icon x-btn-clear',
5144 handler: function() {
5146 this.fireEvent("select", this, '');
5156 this.update(this.value);
5159 createMonthPicker : function(){
5160 if(!this.monthPicker.dom.firstChild){
5161 var buf = ['<table border="0" cellspacing="0">'];
5162 for(var i = 0; i < 6; i++){
5164 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5165 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5167 '<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>' :
5168 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5172 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5174 '</button><button type="button" class="x-date-mp-cancel">',
5176 '</button></td></tr>',
5179 this.monthPicker.update(buf.join(''));
5180 this.monthPicker.on('click', this.onMonthClick, this);
5181 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5183 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5184 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5186 this.mpMonths.each(function(m, a, i){
5189 m.dom.xmonth = 5 + Math.round(i * .5);
5191 m.dom.xmonth = Math.round((i-1) * .5);
5197 showMonthPicker : function(){
5198 this.createMonthPicker();
5199 var size = this.el.getSize();
5200 this.monthPicker.setSize(size);
5201 this.monthPicker.child('table').setSize(size);
5203 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5204 this.updateMPMonth(this.mpSelMonth);
5205 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5206 this.updateMPYear(this.mpSelYear);
5208 this.monthPicker.slideIn('t', {duration:.2});
5211 updateMPYear : function(y){
5213 var ys = this.mpYears.elements;
5214 for(var i = 1; i <= 10; i++){
5215 var td = ys[i-1], y2;
5217 y2 = y + Math.round(i * .5);
5218 td.firstChild.innerHTML = y2;
5221 y2 = y - (5-Math.round(i * .5));
5222 td.firstChild.innerHTML = y2;
5225 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5229 updateMPMonth : function(sm){
5230 this.mpMonths.each(function(m, a, i){
5231 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5235 selectMPMonth: function(m){
5239 onMonthClick : function(e, t){
5241 var el = new Roo.Element(t), pn;
5242 if(el.is('button.x-date-mp-cancel')){
5243 this.hideMonthPicker();
5245 else if(el.is('button.x-date-mp-ok')){
5246 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5247 this.hideMonthPicker();
5249 else if(pn = el.up('td.x-date-mp-month', 2)){
5250 this.mpMonths.removeClass('x-date-mp-sel');
5251 pn.addClass('x-date-mp-sel');
5252 this.mpSelMonth = pn.dom.xmonth;
5254 else if(pn = el.up('td.x-date-mp-year', 2)){
5255 this.mpYears.removeClass('x-date-mp-sel');
5256 pn.addClass('x-date-mp-sel');
5257 this.mpSelYear = pn.dom.xyear;
5259 else if(el.is('a.x-date-mp-prev')){
5260 this.updateMPYear(this.mpyear-10);
5262 else if(el.is('a.x-date-mp-next')){
5263 this.updateMPYear(this.mpyear+10);
5267 onMonthDblClick : function(e, t){
5269 var el = new Roo.Element(t), pn;
5270 if(pn = el.up('td.x-date-mp-month', 2)){
5271 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5272 this.hideMonthPicker();
5274 else if(pn = el.up('td.x-date-mp-year', 2)){
5275 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5276 this.hideMonthPicker();
5280 hideMonthPicker : function(disableAnim){
5281 if(this.monthPicker){
5282 if(disableAnim === true){
5283 this.monthPicker.hide();
5285 this.monthPicker.slideOut('t', {duration:.2});
5291 showPrevMonth : function(e){
5292 this.update(this.activeDate.add("mo", -1));
5296 showNextMonth : function(e){
5297 this.update(this.activeDate.add("mo", 1));
5301 showPrevYear : function(){
5302 this.update(this.activeDate.add("y", -1));
5306 showNextYear : function(){
5307 this.update(this.activeDate.add("y", 1));
5311 handleMouseWheel : function(e){
5312 var delta = e.getWheelDelta();
5314 this.showPrevMonth();
5316 } else if(delta < 0){
5317 this.showNextMonth();
5323 handleDateClick : function(e, t){
5325 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5326 this.setValue(new Date(t.dateValue));
5327 this.fireEvent("select", this, this.value);
5332 selectToday : function(){
5333 this.setValue(new Date().clearTime());
5334 this.fireEvent("select", this, this.value);
5338 update : function(date)
5340 var vd = this.activeDate;
5341 this.activeDate = date;
5343 var t = date.getTime();
5344 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5345 this.cells.removeClass("x-date-selected");
5346 this.cells.each(function(c){
5347 if(c.dom.firstChild.dateValue == t){
5348 c.addClass("x-date-selected");
5349 setTimeout(function(){
5350 try{c.dom.firstChild.focus();}catch(e){}
5359 var days = date.getDaysInMonth();
5360 var firstOfMonth = date.getFirstDateOfMonth();
5361 var startingPos = firstOfMonth.getDay()-this.startDay;
5363 if(startingPos <= this.startDay){
5367 var pm = date.add("mo", -1);
5368 var prevStart = pm.getDaysInMonth()-startingPos;
5370 var cells = this.cells.elements;
5371 var textEls = this.textNodes;
5372 days += startingPos;
5374 // convert everything to numbers so it's fast
5376 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5377 var today = new Date().clearTime().getTime();
5378 var sel = date.clearTime().getTime();
5379 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5380 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5381 var ddMatch = this.disabledDatesRE;
5382 var ddText = this.disabledDatesText;
5383 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5384 var ddaysText = this.disabledDaysText;
5385 var format = this.format;
5387 var setCellClass = function(cal, cell){
5389 var t = d.getTime();
5390 cell.firstChild.dateValue = t;
5392 cell.className += " x-date-today";
5393 cell.title = cal.todayText;
5396 cell.className += " x-date-selected";
5397 setTimeout(function(){
5398 try{cell.firstChild.focus();}catch(e){}
5403 cell.className = " x-date-disabled";
5404 cell.title = cal.minText;
5408 cell.className = " x-date-disabled";
5409 cell.title = cal.maxText;
5413 if(ddays.indexOf(d.getDay()) != -1){
5414 cell.title = ddaysText;
5415 cell.className = " x-date-disabled";
5418 if(ddMatch && format){
5419 var fvalue = d.dateFormat(format);
5420 if(ddMatch.test(fvalue)){
5421 cell.title = ddText.replace("%0", fvalue);
5422 cell.className = " x-date-disabled";
5428 for(; i < startingPos; i++) {
5429 textEls[i].innerHTML = (++prevStart);
5430 d.setDate(d.getDate()+1);
5431 cells[i].className = "x-date-prevday";
5432 setCellClass(this, cells[i]);
5434 for(; i < days; i++){
5435 intDay = i - startingPos + 1;
5436 textEls[i].innerHTML = (intDay);
5437 d.setDate(d.getDate()+1);
5438 cells[i].className = "x-date-active";
5439 setCellClass(this, cells[i]);
5442 for(; i < 42; i++) {
5443 textEls[i].innerHTML = (++extraDays);
5444 d.setDate(d.getDate()+1);
5445 cells[i].className = "x-date-nextday";
5446 setCellClass(this, cells[i]);
5449 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5450 this.fireEvent('monthchange', this, date);
5452 if(!this.internalRender){
5453 var main = this.el.dom.firstChild;
5454 var w = main.offsetWidth;
5455 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5456 Roo.fly(main).setWidth(w);
5457 this.internalRender = true;
5458 // opera does not respect the auto grow header center column
5459 // then, after it gets a width opera refuses to recalculate
5460 // without a second pass
5461 if(Roo.isOpera && !this.secondPass){
5462 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5463 this.secondPass = true;
5464 this.update.defer(10, this, [date]);
5476 * @class Roo.panel.Cropbox
5477 * @extends Roo.BoxComponent
5478 * Panel Cropbox class
5479 * @cfg {String} emptyText show when image has been loaded
5480 * @cfg {String} rotateNotify show when image too small to rotate
5481 * @cfg {Number} errorTimeout default 3000
5482 * @cfg {Number} minWidth default 300
5483 * @cfg {Number} minHeight default 300
5484 * @cfg {Number} outputMaxWidth default 1200
5485 * @cfg {Number} windowSize default 300
5486 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
5487 * @cfg {Boolean} isDocument (true|false) default false
5488 * @cfg {String} url action url
5489 * @cfg {String} paramName default 'imageUpload'
5490 * @cfg {String} method default POST
5491 * @cfg {Boolean} loadMask (true|false) default true
5492 * @cfg {Boolean} loadingText default 'Loading...'
5495 * Create a new Cropbox
5496 * @param {Object} config The config object
5499 Roo.panel.Cropbox = function(config){
5500 Roo.panel.Cropbox.superclass.constructor.call(this, config);
5504 * @event beforeselectfile
5505 * Fire before select file
5506 * @param {Roo.panel.Cropbox} this
5508 "beforeselectfile" : true,
5511 * Fire after initEvent
5512 * @param {Roo.panel.Cropbox} this
5517 * Fire after initEvent
5518 * @param {Roo.panel.Cropbox} this
5519 * @param {String} data
5524 * Fire when preparing the file data
5525 * @param {Roo.panel.Cropbox} this
5526 * @param {Object} file
5531 * Fire when get exception
5532 * @param {Roo.panel.Cropbox} this
5533 * @param {XMLHttpRequest} xhr
5537 * @event beforeloadcanvas
5538 * Fire before load the canvas
5539 * @param {Roo.panel.Cropbox} this
5540 * @param {String} src
5542 "beforeloadcanvas" : true,
5545 * Fire when trash image
5546 * @param {Roo.panel.Cropbox} this
5551 * Fire when download the image
5552 * @param {Roo.panel.Cropbox} this
5556 * @event footerbuttonclick
5557 * Fire when footerbuttonclick
5558 * @param {Roo.panel.Cropbox} this
5559 * @param {String} type
5561 "footerbuttonclick" : true,
5565 * @param {Roo.panel.Cropbox} this
5570 * Fire when rotate the image
5571 * @param {Roo.panel.Cropbox} this
5572 * @param {String} pos
5577 * Fire when inspect the file
5578 * @param {Roo.panel.Cropbox} this
5579 * @param {Object} file
5584 * Fire when xhr upload the file
5585 * @param {Roo.panel.Cropbox} this
5586 * @param {Object} data
5591 * Fire when arrange the file data
5592 * @param {Roo.panel.Cropbox} this
5593 * @param {Object} formData
5598 * Fire after load the canvas
5599 * @param {Roo.panel.Cropbox}
5600 * @param {Object} imgEl
5605 this.buttons = this.buttons || Roo.panel.Cropbox.footer.STANDARD;
5608 Roo.extend(Roo.panel.Cropbox, Roo.Component, {
5610 emptyText : 'Click to upload image',
5611 rotateNotify : 'Image is too small to rotate',
5612 errorTimeout : 3000,
5623 outputMaxWidth : 1200,
5628 cropType : 'image/jpeg',
5630 canvasLoaded : false,
5633 paramName : 'imageUpload',
5635 loadingText : 'Loading...',
5638 getAutoCreate : function()
5642 cls : 'roo-upload-cropbox',
5646 cls : 'roo-upload-cropbox-selector',
5651 cls : 'roo-upload-cropbox-body',
5652 style : 'cursor:pointer',
5656 cls : 'roo-upload-cropbox-preview'
5660 cls : 'roo-upload-cropbox-thumb'
5664 cls : 'roo-upload-cropbox-empty-notify',
5665 html : this.emptyText
5669 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
5670 html : this.rotateNotify
5676 cls : 'roo-upload-cropbox-footer',
5679 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
5689 onRender : function(ct, position)
5691 Roo.panel.Cropbox.superclass.onRender.call(this, ct, position);
5694 if (this.el.attr('xtype')) {
5695 this.el.attr('xtypex', this.el.attr('xtype'));
5696 this.el.dom.removeAttribute('xtype');
5702 var cfg = Roo.apply({}, this.getAutoCreate());
5704 cfg.id = this.id || Roo.id();
5707 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
5710 if (this.style) { // fixme needs to support more complex style data.
5711 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
5714 this.el = ct.createChild(cfg, position);
5719 if (this.buttons.length) {
5721 Roo.each(this.buttons, function(bb) {
5723 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
5725 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
5731 this.maskEl = this.el;
5735 initEvents : function()
5737 this.urlAPI = (window.createObjectURL && window) ||
5738 (window.URL && URL.revokeObjectURL && URL) ||
5739 (window.webkitURL && webkitURL);
5741 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
5742 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
5744 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
5745 this.selectorEl.hide();
5747 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
5748 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
5750 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
5751 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
5752 this.thumbEl.hide();
5754 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
5755 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
5757 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
5758 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
5759 this.errorEl.hide();
5761 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
5762 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
5763 this.footerEl.hide();
5765 this.setThumbBoxSize();
5771 this.fireEvent('initial', this);
5778 window.addEventListener("resize", function() { _this.resize(); } );
5780 this.bodyEl.on('click', this.beforeSelectFile, this);
5783 this.bodyEl.on('touchstart', this.onTouchStart, this);
5784 this.bodyEl.on('touchmove', this.onTouchMove, this);
5785 this.bodyEl.on('touchend', this.onTouchEnd, this);
5789 this.bodyEl.on('mousedown', this.onMouseDown, this);
5790 this.bodyEl.on('mousemove', this.onMouseMove, this);
5791 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
5792 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
5793 Roo.get(document).on('mouseup', this.onMouseUp, this);
5796 this.selectorEl.on('change', this.onFileSelected, this);
5804 this.baseRotate = 1;
5805 this.dragable = false;
5806 this.pinching = false;
5809 this.cropData = false;
5810 this.notifyEl.dom.innerHTML = this.emptyText;
5812 // this.selectorEl.dom.value = '';
5818 if(this.fireEvent('resize', this) != false){
5819 this.setThumbBoxPosition();
5820 this.setCanvasPosition();
5824 onFooterButtonClick : function(e, el, o, type)
5827 case 'rotate-left' :
5828 this.onRotateLeft(e);
5830 case 'rotate-right' :
5831 this.onRotateRight(e);
5834 this.beforeSelectFile(e);
5849 this.fireEvent('footerbuttonclick', this, type);
5852 beforeSelectFile : function(e)
5856 if(this.fireEvent('beforeselectfile', this) != false){
5857 this.selectorEl.dom.click();
5861 onFileSelected : function(e)
5865 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
5869 var file = this.selectorEl.dom.files[0];
5871 if(this.fireEvent('inspect', this, file) != false){
5879 this.fireEvent('trash', this);
5882 download : function(e)
5884 this.fireEvent('download', this);
5887 loadCanvas : function(src)
5889 if(this.fireEvent('beforeloadcanvas', this, src) != false){
5893 this.imageEl = document.createElement('img');
5897 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
5899 this.imageEl.src = src;
5903 onLoadCanvas : function()
5905 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
5906 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
5908 if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
5910 this.bodyEl.un('click', this.beforeSelectFile, this);
5912 this.notifyEl.hide();
5913 this.thumbEl.show();
5914 this.footerEl.show();
5916 this.baseRotateLevel();
5918 if(this.isDocument){
5919 this.setThumbBoxSize();
5922 this.setThumbBoxPosition();
5924 this.baseScaleLevel();
5930 this.canvasLoaded = true;
5935 this.maskEl.unmask();
5940 setCanvasPosition : function()
5946 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
5947 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
5949 this.previewEl.setLeft(pw);
5950 this.previewEl.setTop(ph);
5954 onMouseDown : function(e)
5958 this.dragable = true;
5959 this.pinching = false;
5961 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
5962 this.dragable = false;
5966 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
5967 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
5971 onMouseMove : function(e)
5975 if(!this.canvasLoaded){
5979 if (!this.dragable){
5983 var minX = Math.ceil(this.thumbEl.getLeft(true));
5984 var minY = Math.ceil(this.thumbEl.getTop(true));
5986 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
5987 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
6001 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
6002 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
6004 x = x - this.mouseX;
6005 y = y - this.mouseY;
6007 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
6008 var bgY = Math.ceil(y + this.previewEl.getTop(true));
6010 bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
6011 bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
6013 this.previewEl.setLeft(bgX);
6014 this.previewEl.setTop(bgY);
6016 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
6017 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
6020 onMouseUp : function(e)
6024 this.dragable = false;
6027 onMouseWheel : function(e)
6031 this.startScale = this.scale;
6032 this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
6034 if(!this.zoomable()){
6035 this.scale = this.startScale;
6045 zoomable : function()
6047 var minScale = this.thumbEl.getWidth() / this.minWidth;
6049 if(this.minWidth < this.minHeight){
6050 minScale = this.thumbEl.getHeight() / this.minHeight;
6053 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
6054 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
6056 var maxWidth = this.imageEl.OriginWidth;
6057 var maxHeight = this.imageEl.OriginHeight;
6061 (this.rotate == 0 || this.rotate == 180) &&
6063 width > this.imageEl.OriginWidth ||
6064 height > this.imageEl.OriginHeight ||
6065 (width < this.minWidth && height < this.minHeight)
6073 (this.rotate == 90 || this.rotate == 270) &&
6075 width > this.imageEl.OriginWidth ||
6076 height > this.imageEl.OriginHeight ||
6077 (width < this.minHeight && height < this.minWidth)
6085 (this.rotate == 0 || this.rotate == 180) &&
6087 (this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight) && width < this.minWidth ||
6088 (this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight) && height < this.minHeight ||
6098 (this.rotate == 90 || this.rotate == 270) &&
6100 width < this.minHeight ||
6101 width > this.imageEl.OriginWidth ||
6102 height < this.minWidth ||
6103 height > this.imageEl.OriginHeight
6113 onRotateLeft : function(e)
6115 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
6117 var minScale = this.thumbEl.getWidth() / this.minWidth;
6119 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
6120 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
6122 this.startScale = this.scale;
6124 while (this.getScaleLevel() < minScale){
6126 this.scale = this.scale + 1;
6128 if(!this.zoomable()){
6133 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
6134 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
6139 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
6146 this.scale = this.startScale;
6148 this.onRotateFail();
6153 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
6155 if(this.isDocument){
6156 this.setThumbBoxSize();
6157 this.setThumbBoxPosition();
6158 this.setCanvasPosition();
6163 this.fireEvent('rotate', this, 'left');
6167 onRotateRight : function(e)
6169 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
6171 var minScale = this.thumbEl.getWidth() / this.minWidth;
6173 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
6174 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
6176 this.startScale = this.scale;
6178 while (this.getScaleLevel() < minScale){
6180 this.scale = this.scale + 1;
6182 if(!this.zoomable()){
6187 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
6188 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
6193 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
6200 this.scale = this.startScale;
6202 this.onRotateFail();
6207 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
6209 if(this.isDocument){
6210 this.setThumbBoxSize();
6211 this.setThumbBoxPosition();
6212 this.setCanvasPosition();
6217 this.fireEvent('rotate', this, 'right');
6220 onRotateFail : function()
6222 this.errorEl.show(true);
6226 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
6231 this.previewEl.dom.innerHTML = '';
6233 var canvasEl = document.createElement("canvas");
6235 var contextEl = canvasEl.getContext("2d");
6237 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
6238 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
6239 var center = this.imageEl.OriginWidth / 2;
6241 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
6242 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
6243 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
6244 center = this.imageEl.OriginHeight / 2;
6247 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
6249 contextEl.translate(center, center);
6250 contextEl.rotate(this.rotate * Math.PI / 180);
6252 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
6254 this.canvasEl = document.createElement("canvas");
6256 this.contextEl = this.canvasEl.getContext("2d");
6258 switch (this.rotate) {
6261 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
6262 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
6264 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
6269 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
6270 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
6272 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
6273 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
6277 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
6282 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
6283 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
6285 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
6286 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
6290 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
6295 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
6296 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
6298 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
6299 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
6303 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
6310 this.previewEl.appendChild(this.canvasEl);
6312 this.setCanvasPosition();
6317 if(!this.canvasLoaded){
6321 var imageCanvas = document.createElement("canvas");
6323 var imageContext = imageCanvas.getContext("2d");
6325 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
6326 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
6328 var center = imageCanvas.width / 2;
6330 imageContext.translate(center, center);
6332 imageContext.rotate(this.rotate * Math.PI / 180);
6334 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
6336 var canvas = document.createElement("canvas");
6338 var context = canvas.getContext("2d");
6340 canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
6342 canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
6344 switch (this.rotate) {
6347 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
6348 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
6350 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
6351 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
6353 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
6354 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
6356 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
6357 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
6359 if(canvas.width > this.outputMaxWidth) {
6360 var scale = this.outputMaxWidth / canvas.width;
6361 canvas.width = canvas.width * scale;
6362 canvas.height = canvas.height * scale;
6363 context.scale(scale, scale);
6366 context.fillStyle = 'white';
6367 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
6370 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
6375 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
6376 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
6378 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
6379 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
6381 var targetWidth = this.minWidth - 2 * x;
6382 var targetHeight = this.minHeight - 2 * y;
6386 if((x == 0 && y == 0) || (x == 0 && y > 0)){
6387 scale = targetWidth / width;
6390 if(x > 0 && y == 0){
6391 scale = targetHeight / height;
6395 scale = targetWidth / width;
6398 scale = targetHeight / height;
6402 context.scale(scale, scale);
6404 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
6405 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
6407 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
6408 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
6410 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
6412 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
6417 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
6418 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
6420 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
6421 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
6423 var targetWidth = this.minWidth - 2 * x;
6424 var targetHeight = this.minHeight - 2 * y;
6428 if((x == 0 && y == 0) || (x == 0 && y > 0)){
6429 scale = targetWidth / width;
6432 if(x > 0 && y == 0){
6433 scale = targetHeight / height;
6437 scale = targetWidth / width;
6440 scale = targetHeight / height;
6444 context.scale(scale, scale);
6446 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
6447 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
6449 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
6450 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
6452 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
6453 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
6455 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
6460 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
6461 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
6463 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
6464 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
6466 var targetWidth = this.minWidth - 2 * x;
6467 var targetHeight = this.minHeight - 2 * y;
6471 if((x == 0 && y == 0) || (x == 0 && y > 0)){
6472 scale = targetWidth / width;
6475 if(x > 0 && y == 0){
6476 scale = targetHeight / height;
6480 scale = targetWidth / width;
6483 scale = targetHeight / height;
6487 context.scale(scale, scale);
6488 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
6489 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
6491 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
6492 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
6494 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
6496 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
6503 this.cropData = canvas.toDataURL(this.cropType);
6505 if(this.fireEvent('crop', this, this.cropData) !== false){
6506 this.process(this.file, this.cropData);
6513 setThumbBoxSize : function()
6517 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
6518 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
6519 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
6521 this.minWidth = width;
6522 this.minHeight = height;
6524 if(this.rotate == 90 || this.rotate == 270){
6525 this.minWidth = height;
6526 this.minHeight = width;
6530 height = this.windowSize;
6531 width = Math.ceil(this.minWidth * height / this.minHeight);
6533 if(this.minWidth > this.minHeight){
6534 width = this.windowSize;
6535 height = Math.ceil(this.minHeight * width / this.minWidth);
6538 this.thumbEl.setStyle({
6539 width : width + 'px',
6540 height : height + 'px'
6547 setThumbBoxPosition : function()
6549 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
6550 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
6552 this.thumbEl.setLeft(x);
6553 this.thumbEl.setTop(y);
6557 baseRotateLevel : function()
6559 this.baseRotate = 1;
6562 typeof(this.exif) != 'undefined' &&
6563 typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != 'undefined' &&
6564 [1, 3, 6, 8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != -1
6566 this.baseRotate = this.exif[Roo.panel.Cropbox['tags']['Orientation']];
6569 this.rotate = Roo.panel.Cropbox['Orientation'][this.baseRotate];
6573 baseScaleLevel : function()
6577 if(this.isDocument){
6579 if(this.baseRotate == 6 || this.baseRotate == 8){
6581 height = this.thumbEl.getHeight();
6582 this.baseScale = height / this.imageEl.OriginWidth;
6584 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
6585 width = this.thumbEl.getWidth();
6586 this.baseScale = width / this.imageEl.OriginHeight;
6592 height = this.thumbEl.getHeight();
6593 this.baseScale = height / this.imageEl.OriginHeight;
6595 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
6596 width = this.thumbEl.getWidth();
6597 this.baseScale = width / this.imageEl.OriginWidth;
6603 if(this.baseRotate == 6 || this.baseRotate == 8){
6605 width = this.thumbEl.getHeight();
6606 this.baseScale = width / this.imageEl.OriginHeight;
6608 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
6609 height = this.thumbEl.getWidth();
6610 this.baseScale = height / this.imageEl.OriginHeight;
6613 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
6614 height = this.thumbEl.getWidth();
6615 this.baseScale = height / this.imageEl.OriginHeight;
6617 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
6618 width = this.thumbEl.getHeight();
6619 this.baseScale = width / this.imageEl.OriginWidth;
6626 width = this.thumbEl.getWidth();
6627 this.baseScale = width / this.imageEl.OriginWidth;
6629 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
6630 height = this.thumbEl.getHeight();
6631 this.baseScale = height / this.imageEl.OriginHeight;
6634 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
6636 height = this.thumbEl.getHeight();
6637 this.baseScale = height / this.imageEl.OriginHeight;
6639 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
6640 width = this.thumbEl.getWidth();
6641 this.baseScale = width / this.imageEl.OriginWidth;
6646 if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
6647 this.baseScale = width / this.minWidth;
6653 getScaleLevel : function()
6655 return this.baseScale * Math.pow(1.02, this.scale);
6658 onTouchStart : function(e)
6660 if(!this.canvasLoaded){
6661 this.beforeSelectFile(e);
6665 var touches = e.browserEvent.touches;
6671 if(touches.length == 1){
6672 this.onMouseDown(e);
6676 if(touches.length != 2){
6682 for(var i = 0, finger; finger = touches[i]; i++){
6683 coords.push(finger.pageX, finger.pageY);
6686 var x = Math.pow(coords[0] - coords[2], 2);
6687 var y = Math.pow(coords[1] - coords[3], 2);
6689 this.startDistance = Math.sqrt(x + y);
6691 this.startScale = this.scale;
6693 this.pinching = true;
6694 this.dragable = false;
6698 onTouchMove : function(e)
6700 if(!this.pinching && !this.dragable){
6704 var touches = e.browserEvent.touches;
6711 this.onMouseMove(e);
6717 for(var i = 0, finger; finger = touches[i]; i++){
6718 coords.push(finger.pageX, finger.pageY);
6721 var x = Math.pow(coords[0] - coords[2], 2);
6722 var y = Math.pow(coords[1] - coords[3], 2);
6724 this.endDistance = Math.sqrt(x + y);
6726 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
6728 if(!this.zoomable()){
6729 this.scale = this.startScale;
6737 onTouchEnd : function(e)
6739 this.pinching = false;
6740 this.dragable = false;
6744 process : function(file, crop)
6747 this.maskEl.mask(this.loadingText);
6750 this.xhr = new XMLHttpRequest();
6752 file.xhr = this.xhr;
6754 this.xhr.open(this.method, this.url, true);
6757 "Accept": "application/json",
6758 "Cache-Control": "no-cache",
6759 "X-Requested-With": "XMLHttpRequest"
6762 for (var headerName in headers) {
6763 var headerValue = headers[headerName];
6765 this.xhr.setRequestHeader(headerName, headerValue);
6771 this.xhr.onload = function()
6773 _this.xhrOnLoad(_this.xhr);
6776 this.xhr.onerror = function()
6778 _this.xhrOnError(_this.xhr);
6781 var formData = new FormData();
6783 formData.append('returnHTML', 'NO');
6786 formData.append('crop', crop);
6787 var blobBin = atob(crop.split(',')[1]);
6789 for(var i = 0; i < blobBin.length; i++) {
6790 array.push(blobBin.charCodeAt(i));
6792 var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
6793 formData.append(this.paramName, croppedFile, file.name);
6796 if(typeof(file.filename) != 'undefined'){
6797 formData.append('filename', file.filename);
6800 if(typeof(file.mimetype) != 'undefined'){
6801 formData.append('mimetype', file.mimetype);
6804 if(this.fireEvent('arrange', this, formData) != false){
6805 this.xhr.send(formData);
6809 xhrOnLoad : function(xhr)
6812 this.maskEl.unmask();
6815 if (xhr.readyState !== 4) {
6816 this.fireEvent('exception', this, xhr);
6820 var response = Roo.decode(xhr.responseText);
6822 if(!response.success){
6823 this.fireEvent('exception', this, xhr);
6827 var response = Roo.decode(xhr.responseText);
6829 this.fireEvent('upload', this, response);
6833 xhrOnError : function()
6836 this.maskEl.unmask();
6839 Roo.log('xhr on error');
6841 var response = Roo.decode(xhr.responseText);
6847 prepare : function(file)
6850 this.maskEl.mask(this.loadingText);
6856 if(typeof(file) === 'string'){
6857 this.loadCanvas(file);
6861 if(!file || !this.urlAPI){
6866 if(typeof(file.type) != 'undefined' && file.type.length != 0) {
6867 this.cropType = file.type;
6872 if(this.fireEvent('prepare', this, this.file) != false){
6874 var reader = new FileReader();
6876 reader.onload = function (e) {
6877 if (e.target.error) {
6878 Roo.log(e.target.error);
6882 var buffer = e.target.result,
6883 dataView = new DataView(buffer),
6885 maxOffset = dataView.byteLength - 4,
6889 if (dataView.getUint16(0) === 0xffd8) {
6890 while (offset < maxOffset) {
6891 markerBytes = dataView.getUint16(offset);
6893 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
6894 markerLength = dataView.getUint16(offset + 2) + 2;
6895 if (offset + markerLength > dataView.byteLength) {
6896 Roo.log('Invalid meta data: Invalid segment size.');
6900 if(markerBytes == 0xffe1){
6901 _this.parseExifData(
6908 offset += markerLength;
6918 var url = _this.urlAPI.createObjectURL(_this.file);
6920 _this.loadCanvas(url);
6925 reader.readAsArrayBuffer(this.file);
6931 parseExifData : function(dataView, offset, length)
6933 var tiffOffset = offset + 10,
6937 if (dataView.getUint32(offset + 4) !== 0x45786966) {
6938 // No Exif data, might be XMP data instead
6942 // Check for the ASCII code for "Exif" (0x45786966):
6943 if (dataView.getUint32(offset + 4) !== 0x45786966) {
6944 // No Exif data, might be XMP data instead
6947 if (tiffOffset + 8 > dataView.byteLength) {
6948 Roo.log('Invalid Exif data: Invalid segment size.');
6951 // Check for the two null bytes:
6952 if (dataView.getUint16(offset + 8) !== 0x0000) {
6953 Roo.log('Invalid Exif data: Missing byte alignment offset.');
6956 // Check the byte alignment:
6957 switch (dataView.getUint16(tiffOffset)) {
6959 littleEndian = true;
6962 littleEndian = false;
6965 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
6968 // Check for the TIFF tag marker (0x002A):
6969 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
6970 Roo.log('Invalid Exif data: Missing TIFF marker.');
6973 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
6974 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
6979 tiffOffset + dirOffset,
6984 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
6989 if (dirOffset + 6 > dataView.byteLength) {
6990 Roo.log('Invalid Exif data: Invalid directory offset.');
6993 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
6994 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
6995 if (dirEndOffset + 4 > dataView.byteLength) {
6996 Roo.log('Invalid Exif data: Invalid directory size.');
6999 for (i = 0; i < tagsNumber; i += 1) {
7003 dirOffset + 2 + 12 * i, // tag offset
7007 // Return the offset to the next directory:
7008 return dataView.getUint32(dirEndOffset, littleEndian);
7011 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
7013 var tag = dataView.getUint16(offset, littleEndian);
7015 this.exif[tag] = this.getExifValue(
7019 dataView.getUint16(offset + 2, littleEndian), // tag type
7020 dataView.getUint32(offset + 4, littleEndian), // tag length
7025 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
7027 var tagType = Roo.panel.Cropbox.exifTagTypes[type],
7036 Roo.log('Invalid Exif data: Invalid tag type.');
7040 tagSize = tagType.size * length;
7041 // Determine if the value is contained in the dataOffset bytes,
7042 // or if the value at the dataOffset is a pointer to the actual data:
7043 dataOffset = tagSize > 4 ?
7044 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
7045 if (dataOffset + tagSize > dataView.byteLength) {
7046 Roo.log('Invalid Exif data: Invalid data offset.');
7050 return tagType.getValue(dataView, dataOffset, littleEndian);
7053 for (i = 0; i < length; i += 1) {
7054 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
7057 if (tagType.ascii) {
7059 // Concatenate the chars:
7060 for (i = 0; i < values.length; i += 1) {
7062 // Ignore the terminating NULL byte(s):
7063 if (c === '\u0000') {
7075 Roo.apply(Roo.panel.Cropbox, {
7077 'Orientation': 0x0112
7083 3: 180, //'bottom-right',
7084 // 4: 'bottom-left',
7086 6: 90, //'right-top',
7087 // 7: 'right-bottom',
7088 8: 270 //'left-bottom'
7092 // byte, 8-bit unsigned int:
7094 getValue: function (dataView, dataOffset) {
7095 return dataView.getUint8(dataOffset);
7099 // ascii, 8-bit byte:
7101 getValue: function (dataView, dataOffset) {
7102 return String.fromCharCode(dataView.getUint8(dataOffset));
7107 // short, 16 bit int:
7109 getValue: function (dataView, dataOffset, littleEndian) {
7110 return dataView.getUint16(dataOffset, littleEndian);
7114 // long, 32 bit int:
7116 getValue: function (dataView, dataOffset, littleEndian) {
7117 return dataView.getUint32(dataOffset, littleEndian);
7121 // rational = two long values, first is numerator, second is denominator:
7123 getValue: function (dataView, dataOffset, littleEndian) {
7124 return dataView.getUint32(dataOffset, littleEndian) /
7125 dataView.getUint32(dataOffset + 4, littleEndian);
7129 // slong, 32 bit signed int:
7131 getValue: function (dataView, dataOffset, littleEndian) {
7132 return dataView.getInt32(dataOffset, littleEndian);
7136 // srational, two slongs, first is numerator, second is denominator:
7138 getValue: function (dataView, dataOffset, littleEndian) {
7139 return dataView.getInt32(dataOffset, littleEndian) /
7140 dataView.getInt32(dataOffset + 4, littleEndian);
7150 cls : 'btn-group roo-upload-cropbox-rotate-left',
7151 action : 'rotate-left',
7155 cls : 'btn btn-default',
7156 html : '<i class="fa fa-undo"></i>'
7162 cls : 'btn-group roo-upload-cropbox-picture',
7167 cls : 'btn btn-default',
7168 html : '<i class="fa fa-picture-o"></i>'
7174 cls : 'btn-group roo-upload-cropbox-rotate-right',
7175 action : 'rotate-right',
7179 cls : 'btn btn-default',
7180 html : '<i class="fa fa-repeat"></i>'
7188 cls : 'btn-group roo-upload-cropbox-rotate-left',
7189 action : 'rotate-left',
7193 cls : 'btn btn-default',
7194 html : '<i class="fa fa-undo"></i>'
7200 cls : 'btn-group roo-upload-cropbox-download',
7201 action : 'download',
7205 cls : 'btn btn-default',
7206 html : '<i class="fa fa-download"></i>'
7212 cls : 'btn-group roo-upload-cropbox-crop',
7217 cls : 'btn btn-default',
7218 html : '<i class="fa fa-crop"></i>'
7224 cls : 'btn-group roo-upload-cropbox-trash',
7229 cls : 'btn btn-default',
7230 html : '<i class="fa fa-trash"></i>'
7236 cls : 'btn-group roo-upload-cropbox-rotate-right',
7237 action : 'rotate-right',
7241 cls : 'btn btn-default',
7242 html : '<i class="fa fa-repeat"></i>'
7250 cls : 'btn-group roo-upload-cropbox-rotate-left',
7251 action : 'rotate-left',
7255 cls : 'btn btn-default',
7256 html : '<i class="fa fa-undo"></i>'
7262 cls : 'btn-group roo-upload-cropbox-rotate-right',
7263 action : 'rotate-right',
7267 cls : 'btn btn-default',
7268 html : '<i class="fa fa-repeat"></i>'
7277 * Ext JS Library 1.1.1
7278 * Copyright(c) 2006-2007, Ext JS, LLC.
7280 * Originally Released Under LGPL - original licence link has changed is not relivant.
7283 * <script type="text/javascript">
7286 * @class Roo.panel.Tab
7287 * @extends Roo.util.Observable
7288 * A lightweight tab container.
7292 // basic tabs 1, built from existing content
7293 var tabs = new Roo.panel.Tab("tabs1");
7294 tabs.addTab("script", "View Script");
7295 tabs.addTab("markup", "View Markup");
7296 tabs.activate("script");
7298 // more advanced tabs, built from javascript
7299 var jtabs = new Roo.panel.Tab("jtabs");
7300 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
7302 // set up the UpdateManager
7303 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
7304 var updater = tab2.getUpdateManager();
7305 updater.setDefaultUrl("ajax1.htm");
7306 tab2.on('activate', updater.refresh, updater, true);
7308 // Use setUrl for Ajax loading
7309 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
7310 tab3.setUrl("ajax2.htm", null, true);
7313 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
7316 jtabs.activate("jtabs-1");
7319 * Create a new TabPanel.
7320 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
7321 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
7323 Roo.panel.Tab = function(container, config){
7325 * The container element for this TabPanel.
7328 this.el = Roo.get(container, true);
7330 if(typeof config == "boolean"){
7331 this.tabPosition = config ? "bottom" : "top";
7333 Roo.apply(this, config);
7336 if(this.tabPosition == "bottom"){
7337 this.bodyEl = Roo.get(this.createBody(this.el.dom));
7338 this.el.addClass("x-tabs-bottom");
7340 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
7341 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
7342 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
7344 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
7346 if(this.tabPosition != "bottom"){
7347 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
7350 this.bodyEl = Roo.get(this.createBody(this.el.dom));
7351 this.el.addClass("x-tabs-top");
7355 this.bodyEl.setStyle("position", "relative");
7358 this.activateDelegate = this.activate.createDelegate(this);
7363 * Fires when the active tab changes
7364 * @param {Roo.panel.Tab} this
7365 * @param {Roo.TabPanelItem} activePanel The new active tab
7369 * @event beforetabchange
7370 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
7371 * @param {Roo.panel.Tab} this
7372 * @param {Object} e Set cancel to true on this object to cancel the tab change
7373 * @param {Roo.TabPanelItem} tab The tab being changed to
7375 "beforetabchange" : true
7378 Roo.EventManager.onWindowResize(this.onResize, this);
7379 this.cpad = this.el.getPadding("lr");
7380 this.hiddenCount = 0;
7383 // toolbar on the tabbar support...
7385 var tcfg = this.toolbar;
7386 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
7387 this.toolbar = new Roo.Toolbar(tcfg);
7389 var tbl = tcfg.container.child('table', true);
7390 tbl.setAttribute('width', '100%');
7397 Roo.panel.Tab.superclass.constructor.call(this);
7400 Roo.extend(Roo.panel.Tab, Roo.util.Observable, {
7402 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
7404 tabPosition : "top",
7406 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
7408 currentTabWidth : 0,
7410 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
7414 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
7418 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
7420 preferredTabWidth : 175,
7422 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
7426 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
7428 monitorResize : true,
7430 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
7435 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
7436 * @param {String} id The id of the div to use <b>or create</b>
7437 * @param {String} text The text for the tab
7438 * @param {String} content (optional) Content to put in the TabPanelItem body
7439 * @param {Boolean} closable (optional) True to create a close icon on the tab
7440 * @return {Roo.TabPanelItem} The created TabPanelItem
7442 addTab : function(id, text, content, closable){
7443 var item = new Roo.TabPanelItem(this, id, text, closable);
7444 this.addTabItem(item);
7446 item.setContent(content);
7452 * Returns the {@link Roo.TabPanelItem} with the specified id/index
7453 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
7454 * @return {Roo.TabPanelItem}
7456 getTab : function(id){
7457 return this.items[id];
7461 * Hides the {@link Roo.TabPanelItem} with the specified id/index
7462 * @param {String/Number} id The id or index of the TabPanelItem to hide.
7464 hideTab : function(id){
7465 var t = this.items[id];
7469 this.autoSizeTabs();
7474 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
7475 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
7477 unhideTab : function(id){
7478 var t = this.items[id];
7482 this.autoSizeTabs();
7487 * Adds an existing {@link Roo.TabPanelItem}.
7488 * @param {Roo.TabPanelItem} item The TabPanelItem to add
7490 addTabItem : function(item){
7491 this.items[item.id] = item;
7492 this.items.push(item);
7493 if(this.resizeTabs){
7494 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
7495 this.autoSizeTabs();
7502 * Removes a {@link Roo.TabPanelItem}.
7503 * @param {String/Number} id The id or index of the TabPanelItem to remove.
7505 removeTab : function(id){
7506 var items = this.items;
7507 var tab = items[id];
7508 if(!tab) { return; }
7509 var index = items.indexOf(tab);
7510 if(this.active == tab && items.length > 1){
7511 var newTab = this.getNextAvailable(index);
7516 this.stripEl.dom.removeChild(tab.pnode.dom);
7517 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
7518 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
7520 items.splice(index, 1);
7521 delete this.items[tab.id];
7522 tab.fireEvent("close", tab);
7523 tab.purgeListeners();
7524 this.autoSizeTabs();
7527 getNextAvailable : function(start){
7528 var items = this.items;
7530 // look for a next tab that will slide over to
7531 // replace the one being removed
7532 while(index < items.length){
7533 var item = items[++index];
7534 if(item && !item.isHidden()){
7538 // if one isn't found select the previous tab (on the left)
7541 var item = items[--index];
7542 if(item && !item.isHidden()){
7550 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
7551 * @param {String/Number} id The id or index of the TabPanelItem to disable.
7553 disableTab : function(id){
7554 var tab = this.items[id];
7555 if(tab && this.active != tab){
7561 * Enables a {@link Roo.TabPanelItem} that is disabled.
7562 * @param {String/Number} id The id or index of the TabPanelItem to enable.
7564 enableTab : function(id){
7565 var tab = this.items[id];
7570 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
7571 * @param {String/Number} id The id or index of the TabPanelItem to activate.
7572 * @return {Roo.TabPanelItem} The TabPanelItem.
7574 activate : function(id){
7575 var tab = this.items[id];
7579 if(tab == this.active || tab.disabled){
7583 this.fireEvent("beforetabchange", this, e, tab);
7584 if(e.cancel !== true && !tab.disabled){
7588 this.active = this.items[id];
7590 this.fireEvent("tabchange", this, this.active);
7596 * Gets the active {@link Roo.TabPanelItem}.
7597 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
7599 getActiveTab : function(){
7604 * Updates the tab body element to fit the height of the container element
7605 * for overflow scrolling
7606 * @param {Number} targetHeight (optional) Override the starting height from the elements height
7608 syncHeight : function(targetHeight){
7609 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
7610 var bm = this.bodyEl.getMargins();
7611 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
7612 this.bodyEl.setHeight(newHeight);
7616 onResize : function(){
7617 if(this.monitorResize){
7618 this.autoSizeTabs();
7623 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
7625 beginUpdate : function(){
7626 this.updating = true;
7630 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
7632 endUpdate : function(){
7633 this.updating = false;
7634 this.autoSizeTabs();
7638 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
7640 autoSizeTabs : function(){
7641 var count = this.items.length;
7642 var vcount = count - this.hiddenCount;
7643 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
7646 var w = Math.max(this.el.getWidth() - this.cpad, 10);
7647 var availWidth = Math.floor(w / vcount);
7648 var b = this.stripBody;
7649 if(b.getWidth() > w){
7650 var tabs = this.items;
7651 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
7652 if(availWidth < this.minTabWidth){
7653 /*if(!this.sleft){ // incomplete scrolling code
7654 this.createScrollButtons();
7657 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
7660 if(this.currentTabWidth < this.preferredTabWidth){
7661 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
7667 * Returns the number of tabs in this TabPanel.
7670 getCount : function(){
7671 return this.items.length;
7675 * Resizes all the tabs to the passed width
7676 * @param {Number} The new width
7678 setTabWidth : function(width){
7679 this.currentTabWidth = width;
7680 for(var i = 0, len = this.items.length; i < len; i++) {
7681 if(!this.items[i].isHidden()) {
7682 this.items[i].setWidth(width);
7688 * Destroys this TabPanel
7689 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
7691 destroy : function(removeEl){
7692 Roo.EventManager.removeResizeListener(this.onResize, this);
7693 for(var i = 0, len = this.items.length; i < len; i++){
7694 this.items[i].purgeListeners();
7696 if(removeEl === true){
7704 * @class Roo.TabPanelItem
7705 * @extends Roo.util.Observable
7706 * Represents an individual item (tab plus body) in a TabPanel.
7707 * @param {Roo.panel.Tab} tabPanel The {@link Roo.panel.Tab} this TabPanelItem belongs to
7708 * @param {String} id The id of this TabPanelItem
7709 * @param {String} text The text for the tab of this TabPanelItem
7710 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
7712 Roo.TabPanelItem = function(tabPanel, id, text, closable){
7714 * The {@link Roo.panel.Tab} this TabPanelItem belongs to
7715 * @type Roo.panel.Tab
7717 this.tabPanel = tabPanel;
7719 * The id for this TabPanelItem
7724 this.disabled = false;
7728 this.loaded = false;
7729 this.closable = closable;
7732 * The body element for this TabPanelItem.
7735 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
7736 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
7737 this.bodyEl.setStyle("display", "block");
7738 this.bodyEl.setStyle("zoom", "1");
7741 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
7743 this.el = Roo.get(els.el, true);
7744 this.inner = Roo.get(els.inner, true);
7745 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
7746 this.pnode = Roo.get(els.el.parentNode, true);
7747 this.el.on("mousedown", this.onTabMouseDown, this);
7748 this.el.on("click", this.onTabClick, this);
7751 var c = Roo.get(els.close, true);
7752 c.dom.title = this.closeText;
7753 c.addClassOnOver("close-over");
7754 c.on("click", this.closeClick, this);
7760 * Fires when this tab becomes the active tab.
7761 * @param {Roo.panel.Tab} tabPanel The parent TabPanel
7762 * @param {Roo.TabPanelItem} this
7766 * @event beforeclose
7767 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
7768 * @param {Roo.TabPanelItem} this
7769 * @param {Object} e Set cancel to true on this object to cancel the close.
7771 "beforeclose": true,
7774 * Fires when this tab is closed.
7775 * @param {Roo.TabPanelItem} this
7780 * Fires when this tab is no longer the active tab.
7781 * @param {Roo.panel.Tab} tabPanel The parent TabPanel
7782 * @param {Roo.TabPanelItem} this
7786 this.hidden = false;
7788 Roo.TabPanelItem.superclass.constructor.call(this);
7791 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
7792 purgeListeners : function(){
7793 Roo.util.Observable.prototype.purgeListeners.call(this);
7794 this.el.removeAllListeners();
7797 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
7800 this.pnode.addClass("on");
7803 this.tabPanel.stripWrap.repaint();
7805 this.fireEvent("activate", this.tabPanel, this);
7809 * Returns true if this tab is the active tab.
7812 isActive : function(){
7813 return this.tabPanel.getActiveTab() == this;
7817 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
7820 this.pnode.removeClass("on");
7822 this.fireEvent("deactivate", this.tabPanel, this);
7825 hideAction : function(){
7827 this.bodyEl.setStyle("position", "absolute");
7828 this.bodyEl.setLeft("-20000px");
7829 this.bodyEl.setTop("-20000px");
7832 showAction : function(){
7833 this.bodyEl.setStyle("position", "relative");
7834 this.bodyEl.setTop("");
7835 this.bodyEl.setLeft("");
7840 * Set the tooltip for the tab.
7841 * @param {String} tooltip The tab's tooltip
7843 setTooltip : function(text){
7844 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
7845 this.textEl.dom.qtip = text;
7846 this.textEl.dom.removeAttribute('title');
7848 this.textEl.dom.title = text;
7852 onTabClick : function(e){
7854 this.tabPanel.activate(this.id);
7857 onTabMouseDown : function(e){
7859 this.tabPanel.activate(this.id);
7862 getWidth : function(){
7863 return this.inner.getWidth();
7866 setWidth : function(width){
7867 var iwidth = width - this.pnode.getPadding("lr");
7868 this.inner.setWidth(iwidth);
7869 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
7870 this.pnode.setWidth(width);
7874 * Show or hide the tab
7875 * @param {Boolean} hidden True to hide or false to show.
7877 setHidden : function(hidden){
7878 this.hidden = hidden;
7879 this.pnode.setStyle("display", hidden ? "none" : "");
7883 * Returns true if this tab is "hidden"
7886 isHidden : function(){
7891 * Returns the text for this tab
7894 getText : function(){
7898 autoSize : function(){
7899 //this.el.beginMeasure();
7900 this.textEl.setWidth(1);
7902 * #2804 [new] Tabs in Roojs
7903 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
7905 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
7906 //this.el.endMeasure();
7910 * Sets the text for the tab (Note: this also sets the tooltip text)
7911 * @param {String} text The tab's text and tooltip
7913 setText : function(text){
7915 this.textEl.update(text);
7916 this.setTooltip(text);
7917 if(!this.tabPanel.resizeTabs){
7922 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
7924 activate : function(){
7925 this.tabPanel.activate(this.id);
7929 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
7931 disable : function(){
7932 if(this.tabPanel.active != this){
7933 this.disabled = true;
7934 this.pnode.addClass("disabled");
7939 * Enables this TabPanelItem if it was previously disabled.
7941 enable : function(){
7942 this.disabled = false;
7943 this.pnode.removeClass("disabled");
7947 * Sets the content for this TabPanelItem.
7948 * @param {String} content The content
7949 * @param {Boolean} loadScripts true to look for and load scripts
7951 setContent : function(content, loadScripts){
7952 this.bodyEl.update(content, loadScripts);
7956 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
7957 * @return {Roo.UpdateManager} The UpdateManager
7959 getUpdateManager : function(){
7960 return this.bodyEl.getUpdateManager();
7964 * Set a URL to be used to load the content for this TabPanelItem.
7965 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
7966 * @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)
7967 * @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)
7968 * @return {Roo.UpdateManager} The UpdateManager
7970 setUrl : function(url, params, loadOnce){
7971 if(this.refreshDelegate){
7972 this.un('activate', this.refreshDelegate);
7974 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
7975 this.on("activate", this.refreshDelegate);
7976 return this.bodyEl.getUpdateManager();
7980 _handleRefresh : function(url, params, loadOnce){
7981 if(!loadOnce || !this.loaded){
7982 var updater = this.bodyEl.getUpdateManager();
7983 updater.update(url, params, this._setLoaded.createDelegate(this));
7988 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
7989 * Will fail silently if the setUrl method has not been called.
7990 * This does not activate the panel, just updates its content.
7992 refresh : function(){
7993 if(this.refreshDelegate){
7994 this.loaded = false;
7995 this.refreshDelegate();
8000 _setLoaded : function(){
8005 closeClick : function(e){
8008 this.fireEvent("beforeclose", this, o);
8009 if(o.cancel !== true){
8010 this.tabPanel.removeTab(this.id);
8014 * The text displayed in the tooltip for the close icon.
8017 closeText : "Close this tab"
8021 Roo.panel.Tab.prototype.createStrip = function(container){
8022 var strip = document.createElement("div");
8023 strip.className = "x-tabs-wrap";
8024 container.appendChild(strip);
8028 Roo.panel.Tab.prototype.createStripList = function(strip){
8029 // div wrapper for retard IE
8030 // returns the "tr" element.
8031 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
8032 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
8033 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
8034 return strip.firstChild.firstChild.firstChild.firstChild;
8037 Roo.panel.Tab.prototype.createBody = function(container){
8038 var body = document.createElement("div");
8039 Roo.id(body, "tab-body");
8040 Roo.fly(body).addClass("x-tabs-body");
8041 container.appendChild(body);
8045 Roo.panel.Tab.prototype.createItemBody = function(bodyEl, id){
8046 var body = Roo.getDom(id);
8048 body = document.createElement("div");
8051 Roo.fly(body).addClass("x-tabs-item-body");
8052 bodyEl.insertBefore(body, bodyEl.firstChild);
8056 Roo.panel.Tab.prototype.createStripElements = function(stripEl, text, closable){
8057 var td = document.createElement("td");
8058 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
8059 //stripEl.appendChild(td);
8061 td.className = "x-tabs-closable";
8063 this.closeTpl = new Roo.Template(
8064 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
8065 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
8066 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
8069 var el = this.closeTpl.overwrite(td, {"text": text});
8070 var close = el.getElementsByTagName("div")[0];
8071 var inner = el.getElementsByTagName("em")[0];
8072 return {"el": el, "close": close, "inner": inner};
8075 this.tabTpl = new Roo.Template(
8076 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
8077 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
8080 var el = this.tabTpl.overwrite(td, {"text": text});
8081 var inner = el.getElementsByTagName("em")[0];
8082 return {"el": el, "inner": inner};
8086 * Ext JS Library 1.1.1
8087 * Copyright(c) 2006-2007, Ext JS, LLC.
8089 * Originally Released Under LGPL - original licence link has changed is not relivant.
8092 * <script type="text/javascript">
8097 * @extends Roo.util.Observable
8098 * Simple Button class
8099 * @cfg {String} text The button text
8100 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
8101 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
8102 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
8103 * @cfg {Object} scope The scope of the handler
8104 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
8105 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
8106 * @cfg {Boolean} hidden True to start hidden (defaults to false)
8107 * @cfg {Boolean} disabled True to start disabled (defaults to false)
8108 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
8109 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
8110 applies if enableToggle = true)
8111 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
8112 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
8113 an {@link Roo.util.ClickRepeater} config object (defaults to false).
8115 * Create a new button
8116 * @param {Object} config The config object
8118 Roo.Button = function(renderTo, config)
8122 renderTo = config.renderTo || false;
8125 Roo.apply(this, config);
8129 * Fires when this button is clicked
8130 * @param {Button} this
8131 * @param {EventObject} e The click event
8136 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
8137 * @param {Button} this
8138 * @param {Boolean} pressed
8143 * Fires when the mouse hovers over the button
8144 * @param {Button} this
8145 * @param {Event} e The event object
8150 * Fires when the mouse exits the button
8151 * @param {Button} this
8152 * @param {Event} e The event object
8157 * Fires when the button is rendered
8158 * @param {Button} this
8163 this.menu = Roo.menu.MenuMgr.get(this.menu);
8165 // register listeners first!! - so render can be captured..
8166 Roo.util.Observable.call(this);
8168 this.render(renderTo);
8174 Roo.extend(Roo.Button, Roo.util.Observable, {
8180 * Read-only. True if this button is hidden
8185 * Read-only. True if this button is disabled
8190 * Read-only. True if this button is pressed (only if enableToggle = true)
8196 * @cfg {Number} tabIndex
8197 * The DOM tabIndex for this button (defaults to undefined)
8199 tabIndex : undefined,
8202 * @cfg {Boolean} enableToggle
8203 * True to enable pressed/not pressed toggling (defaults to false)
8205 enableToggle: false,
8207 * @cfg {Roo.menu.Menu} menu
8208 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
8212 * @cfg {String} menuAlign
8213 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
8215 menuAlign : "tl-bl?",
8218 * @cfg {String} iconCls
8219 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
8221 iconCls : undefined,
8223 * @cfg {String} type
8224 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
8229 menuClassTarget: 'tr',
8232 * @cfg {String} clickEvent
8233 * The type of event to map to the button's event handler (defaults to 'click')
8235 clickEvent : 'click',
8238 * @cfg {Boolean} handleMouseEvents
8239 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
8241 handleMouseEvents : true,
8244 * @cfg {String} tooltipType
8245 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
8247 tooltipType : 'qtip',
8251 * A CSS class to apply to the button's main element.
8255 * @cfg {Roo.Template} template (Optional)
8256 * An {@link Roo.Template} with which to create the Button's main element. This Template must
8257 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
8258 * require code modifications if required elements (e.g. a button) aren't present.
8262 render : function(renderTo){
8264 if(this.hideParent){
8265 this.parentEl = Roo.get(renderTo);
8269 if(!Roo.Button.buttonTemplate){
8270 // hideous table template
8271 Roo.Button.buttonTemplate = new Roo.Template(
8272 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
8273 '<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>',
8274 "</tr></tbody></table>");
8276 this.template = Roo.Button.buttonTemplate;
8278 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
8279 var btnEl = btn.child("button:first");
8280 btnEl.on('focus', this.onFocus, this);
8281 btnEl.on('blur', this.onBlur, this);
8283 btn.addClass(this.cls);
8286 btnEl.setStyle('background-image', 'url(' +this.icon +')');
8289 btnEl.addClass(this.iconCls);
8291 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
8294 if(this.tabIndex !== undefined){
8295 btnEl.dom.tabIndex = this.tabIndex;
8298 if(typeof this.tooltip == 'object'){
8299 Roo.QuickTips.tips(Roo.apply({
8303 btnEl.dom[this.tooltipType] = this.tooltip;
8307 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
8311 this.el.dom.id = this.el.id = this.id;
8314 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
8315 this.menu.on("show", this.onMenuShow, this);
8316 this.menu.on("hide", this.onMenuHide, this);
8318 btn.addClass("x-btn");
8319 if(Roo.isIE && !Roo.isIE7){
8320 this.autoWidth.defer(1, this);
8324 if(this.handleMouseEvents){
8325 btn.on("mouseover", this.onMouseOver, this);
8326 btn.on("mouseout", this.onMouseOut, this);
8327 btn.on("mousedown", this.onMouseDown, this);
8329 btn.on(this.clickEvent, this.onClick, this);
8330 //btn.on("mouseup", this.onMouseUp, this);
8337 Roo.ButtonToggleMgr.register(this);
8339 this.el.addClass("x-btn-pressed");
8342 var repeater = new Roo.util.ClickRepeater(btn,
8343 typeof this.repeat == "object" ? this.repeat : {}
8345 repeater.on("click", this.onClick, this);
8348 this.fireEvent('render', this);
8352 * Returns the button's underlying element
8353 * @return {Roo.Element} The element
8360 * Destroys this Button and removes any listeners.
8362 destroy : function(){
8363 Roo.ButtonToggleMgr.unregister(this);
8364 this.el.removeAllListeners();
8365 this.purgeListeners();
8370 autoWidth : function(){
8372 this.el.setWidth("auto");
8373 if(Roo.isIE7 && Roo.isStrict){
8374 var ib = this.el.child('button');
8375 if(ib && ib.getWidth() > 20){
8377 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
8382 this.el.beginMeasure();
8384 if(this.el.getWidth() < this.minWidth){
8385 this.el.setWidth(this.minWidth);
8388 this.el.endMeasure();
8395 * Assigns this button's click handler
8396 * @param {Function} handler The function to call when the button is clicked
8397 * @param {Object} scope (optional) Scope for the function passed in
8399 setHandler : function(handler, scope){
8400 this.handler = handler;
8405 * Sets this button's text
8406 * @param {String} text The button text
8408 setText : function(text){
8411 this.el.child("td.x-btn-center button.x-btn-text").update(text);
8417 * Gets the text for this button
8418 * @return {String} The button text
8420 getText : function(){
8428 this.hidden = false;
8430 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
8440 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
8445 * Convenience function for boolean show/hide
8446 * @param {Boolean} visible True to show, false to hide
8448 setVisible: function(visible){
8456 * Similar to toggle, but does not trigger event.
8457 * @param {Boolean} state [required] Force a particular state
8459 setPressed : function(state)
8461 if(state != this.pressed){
8463 this.el.addClass("x-btn-pressed");
8464 this.pressed = true;
8466 this.el.removeClass("x-btn-pressed");
8467 this.pressed = false;
8473 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
8474 * @param {Boolean} state (optional) Force a particular state
8476 toggle : function(state){
8477 state = state === undefined ? !this.pressed : state;
8478 if(state != this.pressed){
8480 this.el.addClass("x-btn-pressed");
8481 this.pressed = true;
8482 this.fireEvent("toggle", this, true);
8484 this.el.removeClass("x-btn-pressed");
8485 this.pressed = false;
8486 this.fireEvent("toggle", this, false);
8488 if(this.toggleHandler){
8489 this.toggleHandler.call(this.scope || this, this, state);
8500 this.el.child('button:first').focus();
8504 * Disable this button
8506 disable : function(){
8508 this.el.addClass("x-btn-disabled");
8510 this.disabled = true;
8514 * Enable this button
8516 enable : function(){
8518 this.el.removeClass("x-btn-disabled");
8520 this.disabled = false;
8524 * Convenience function for boolean enable/disable
8525 * @param {Boolean} enabled True to enable, false to disable
8527 setDisabled : function(v){
8528 this[v !== true ? "enable" : "disable"]();
8532 onClick : function(e)
8541 if(this.enableToggle){
8544 if(this.menu && !this.menu.isVisible()){
8545 this.menu.show(this.el, this.menuAlign);
8547 this.fireEvent("click", this, e);
8549 this.el.removeClass("x-btn-over");
8550 this.handler.call(this.scope || this, this, e);
8555 onMouseOver : function(e){
8557 this.el.addClass("x-btn-over");
8558 this.fireEvent('mouseover', this, e);
8562 onMouseOut : function(e){
8563 if(!e.within(this.el, true)){
8564 this.el.removeClass("x-btn-over");
8565 this.fireEvent('mouseout', this, e);
8569 onFocus : function(e){
8571 this.el.addClass("x-btn-focus");
8575 onBlur : function(e){
8576 this.el.removeClass("x-btn-focus");
8579 onMouseDown : function(e){
8580 if(!this.disabled && e.button == 0){
8581 this.el.addClass("x-btn-click");
8582 Roo.get(document).on('mouseup', this.onMouseUp, this);
8586 onMouseUp : function(e){
8588 this.el.removeClass("x-btn-click");
8589 Roo.get(document).un('mouseup', this.onMouseUp, this);
8593 onMenuShow : function(e){
8594 this.el.addClass("x-btn-menu-active");
8597 onMenuHide : function(e){
8598 this.el.removeClass("x-btn-menu-active");
8602 // Private utility class used by Button
8603 Roo.ButtonToggleMgr = function(){
8606 function toggleGroup(btn, state){
8608 var g = groups[btn.toggleGroup];
8609 for(var i = 0, l = g.length; i < l; i++){
8618 register : function(btn){
8619 if(!btn.toggleGroup){
8622 var g = groups[btn.toggleGroup];
8624 g = groups[btn.toggleGroup] = [];
8627 btn.on("toggle", toggleGroup);
8630 unregister : function(btn){
8631 if(!btn.toggleGroup){
8634 var g = groups[btn.toggleGroup];
8637 btn.un("toggle", toggleGroup);
8643 * Ext JS Library 1.1.1
8644 * Copyright(c) 2006-2007, Ext JS, LLC.
8646 * Originally Released Under LGPL - original licence link has changed is not relivant.
8649 * <script type="text/javascript">
8653 * @class Roo.SplitButton
8654 * @extends Roo.Button
8655 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
8656 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
8657 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
8658 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
8659 * @cfg {String} arrowTooltip The title attribute of the arrow
8661 * Create a new menu button
8662 * @param {String/HTMLElement/Element} renderTo The element to append the button to
8663 * @param {Object} config The config object
8665 Roo.SplitButton = function(renderTo, config){
8666 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
8669 * Fires when this button's arrow is clicked
8670 * @param {SplitButton} this
8671 * @param {EventObject} e The click event
8673 this.addEvents({"arrowclick":true});
8676 Roo.extend(Roo.SplitButton, Roo.Button, {
8677 render : function(renderTo){
8678 // this is one sweet looking template!
8679 var tpl = new Roo.Template(
8680 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
8681 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
8682 '<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>',
8683 "</tbody></table></td><td>",
8684 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
8685 '<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>',
8686 "</tbody></table></td></tr></table>"
8688 var btn = tpl.append(renderTo, [this.text, this.type], true);
8689 var btnEl = btn.child("button");
8691 btn.addClass(this.cls);
8694 btnEl.setStyle('background-image', 'url(' +this.icon +')');
8697 btnEl.addClass(this.iconCls);
8699 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
8703 if(this.handleMouseEvents){
8704 btn.on("mouseover", this.onMouseOver, this);
8705 btn.on("mouseout", this.onMouseOut, this);
8706 btn.on("mousedown", this.onMouseDown, this);
8707 btn.on("mouseup", this.onMouseUp, this);
8709 btn.on(this.clickEvent, this.onClick, this);
8711 if(typeof this.tooltip == 'object'){
8712 Roo.QuickTips.tips(Roo.apply({
8716 btnEl.dom[this.tooltipType] = this.tooltip;
8719 if(this.arrowTooltip){
8720 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
8729 this.el.addClass("x-btn-pressed");
8731 if(Roo.isIE && !Roo.isIE7){
8732 this.autoWidth.defer(1, this);
8737 this.menu.on("show", this.onMenuShow, this);
8738 this.menu.on("hide", this.onMenuHide, this);
8740 this.fireEvent('render', this);
8744 autoWidth : function(){
8746 var tbl = this.el.child("table:first");
8747 var tbl2 = this.el.child("table:last");
8748 this.el.setWidth("auto");
8749 tbl.setWidth("auto");
8750 if(Roo.isIE7 && Roo.isStrict){
8751 var ib = this.el.child('button:first');
8752 if(ib && ib.getWidth() > 20){
8754 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
8759 this.el.beginMeasure();
8761 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
8762 tbl.setWidth(this.minWidth-tbl2.getWidth());
8765 this.el.endMeasure();
8768 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
8772 * Sets this button's click handler
8773 * @param {Function} handler The function to call when the button is clicked
8774 * @param {Object} scope (optional) Scope for the function passed above
8776 setHandler : function(handler, scope){
8777 this.handler = handler;
8782 * Sets this button's arrow click handler
8783 * @param {Function} handler The function to call when the arrow is clicked
8784 * @param {Object} scope (optional) Scope for the function passed above
8786 setArrowHandler : function(handler, scope){
8787 this.arrowHandler = handler;
8796 this.el.child("button:first").focus();
8801 onClick : function(e){
8804 if(e.getTarget(".x-btn-menu-arrow-wrap")){
8805 if(this.menu && !this.menu.isVisible()){
8806 this.menu.show(this.el, this.menuAlign);
8808 this.fireEvent("arrowclick", this, e);
8809 if(this.arrowHandler){
8810 this.arrowHandler.call(this.scope || this, this, e);
8813 this.fireEvent("click", this, e);
8815 this.handler.call(this.scope || this, this, e);
8821 onMouseDown : function(e){
8823 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
8827 onMouseUp : function(e){
8828 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
8834 Roo.MenuButton = Roo.SplitButton;/*
8836 * Ext JS Library 1.1.1
8837 * Copyright(c) 2006-2007, Ext JS, LLC.
8839 * Originally Released Under LGPL - original licence link has changed is not relivant.
8842 * <script type="text/javascript">
8846 * @class Roo.Toolbar
8847 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
8848 * Basic Toolbar class.
8850 * Creates a new Toolbar
8851 * @param {Object} container The config object
8853 Roo.Toolbar = function(container, buttons, config)
8855 /// old consturctor format still supported..
8856 if(container instanceof Array){ // omit the container for later rendering
8857 buttons = container;
8861 if (typeof(container) == 'object' && container.xtype) {
8863 container = config.container;
8864 buttons = config.buttons || []; // not really - use items!!
8867 if (config && config.items) {
8868 xitems = config.items;
8869 delete config.items;
8871 Roo.apply(this, config);
8872 this.buttons = buttons;
8875 this.render(container);
8877 this.xitems = xitems;
8878 Roo.each(xitems, function(b) {
8884 Roo.Toolbar.prototype = {
8886 * @cfg {Array} items
8887 * array of button configs or elements to add (will be converted to a MixedCollection)
8891 * @cfg {String/HTMLElement/Element} container
8892 * The id or element that will contain the toolbar
8895 render : function(ct){
8896 this.el = Roo.get(ct);
8898 this.el.addClass(this.cls);
8900 // using a table allows for vertical alignment
8901 // 100% width is needed by Safari...
8902 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
8903 this.tr = this.el.child("tr", true);
8905 this.items = new Roo.util.MixedCollection(false, function(o){
8906 return o.id || ("item" + (++autoId));
8909 this.add.apply(this, this.buttons);
8910 delete this.buttons;
8915 * Adds element(s) to the toolbar -- this function takes a variable number of
8916 * arguments of mixed type and adds them to the toolbar.
8917 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
8919 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
8920 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
8921 * <li>Field: Any form field (equivalent to {@link #addField})</li>
8922 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
8923 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
8924 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
8925 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
8926 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
8927 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
8929 * @param {Mixed} arg2
8930 * @param {Mixed} etc.
8933 var a = arguments, l = a.length;
8934 for(var i = 0; i < l; i++){
8939 _add : function(el) {
8942 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
8945 if (el.applyTo){ // some kind of form field
8946 return this.addField(el);
8948 if (el.render){ // some kind of Toolbar.Item
8949 return this.addItem(el);
8951 if (typeof el == "string"){ // string
8952 if(el == "separator" || el == "-"){
8953 return this.addSeparator();
8956 return this.addSpacer();
8959 return this.addFill();
8961 return this.addText(el);
8964 if(el.tagName){ // element
8965 return this.addElement(el);
8967 if(typeof el == "object"){ // must be button config?
8968 return this.addButton(el);
8976 * Add an Xtype element
8977 * @param {Object} xtype Xtype Object
8978 * @return {Object} created Object
8980 addxtype : function(e){
8985 * Returns the Element for this toolbar.
8986 * @return {Roo.Element}
8994 * @return {Roo.Toolbar.Item} The separator item
8996 addSeparator : function(){
8997 return this.addItem(new Roo.Toolbar.Separator());
9001 * Adds a spacer element
9002 * @return {Roo.Toolbar.Spacer} The spacer item
9004 addSpacer : function(){
9005 return this.addItem(new Roo.Toolbar.Spacer());
9009 * Adds a fill element that forces subsequent additions to the right side of the toolbar
9010 * @return {Roo.Toolbar.Fill} The fill item
9012 addFill : function(){
9013 return this.addItem(new Roo.Toolbar.Fill());
9017 * Adds any standard HTML element to the toolbar
9018 * @param {String/HTMLElement/Element} el The element or id of the element to add
9019 * @return {Roo.Toolbar.Item} The element's item
9021 addElement : function(el){
9022 return this.addItem(new Roo.Toolbar.Item(el));
9025 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
9026 * @type Roo.util.MixedCollection
9031 * Adds any Toolbar.Item or subclass
9032 * @param {Roo.Toolbar.Item} item
9033 * @return {Roo.Toolbar.Item} The item
9035 addItem : function(item){
9036 var td = this.nextBlock();
9038 this.items.add(item);
9043 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
9044 * @param {Object/Array} config A button config or array of configs
9045 * @return {Roo.Toolbar.Button/Array}
9047 addButton : function(config){
9048 if(config instanceof Array){
9050 for(var i = 0, len = config.length; i < len; i++) {
9051 buttons.push(this.addButton(config[i]));
9056 if(!(config instanceof Roo.Toolbar.Button)){
9058 new Roo.Toolbar.SplitButton(config) :
9059 new Roo.Toolbar.Button(config);
9061 var td = this.nextBlock();
9068 * Adds text to the toolbar
9069 * @param {String} text The text to add
9070 * @return {Roo.Toolbar.Item} The element's item
9072 addText : function(text){
9073 return this.addItem(new Roo.Toolbar.TextItem(text));
9077 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
9078 * @param {Number} index The index where the item is to be inserted
9079 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
9080 * @return {Roo.Toolbar.Button/Item}
9082 insertButton : function(index, item){
9083 if(item instanceof Array){
9085 for(var i = 0, len = item.length; i < len; i++) {
9086 buttons.push(this.insertButton(index + i, item[i]));
9090 if (!(item instanceof Roo.Toolbar.Button)){
9091 item = new Roo.Toolbar.Button(item);
9093 var td = document.createElement("td");
9094 this.tr.insertBefore(td, this.tr.childNodes[index]);
9096 this.items.insert(index, item);
9101 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
9102 * @param {Object} config
9103 * @return {Roo.Toolbar.Item} The element's item
9105 addDom : function(config, returnEl){
9106 var td = this.nextBlock();
9107 Roo.DomHelper.overwrite(td, config);
9108 var ti = new Roo.Toolbar.Item(td.firstChild);
9115 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
9116 * @type Roo.util.MixedCollection
9121 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
9122 * Note: the field should not have been rendered yet. For a field that has already been
9123 * rendered, use {@link #addElement}.
9124 * @param {Roo.form.Field} field
9125 * @return {Roo.ToolbarItem}
9129 addField : function(field) {
9132 this.fields = new Roo.util.MixedCollection(false, function(o){
9133 return o.id || ("item" + (++autoId));
9138 var td = this.nextBlock();
9140 var ti = new Roo.Toolbar.Item(td.firstChild);
9143 this.fields.add(field);
9154 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
9155 this.el.child('div').hide();
9163 this.el.child('div').show();
9167 nextBlock : function(){
9168 var td = document.createElement("td");
9169 this.tr.appendChild(td);
9174 destroy : function(){
9175 if(this.items){ // rendered?
9176 Roo.destroy.apply(Roo, this.items.items);
9178 if(this.fields){ // rendered?
9179 Roo.destroy.apply(Roo, this.fields.items);
9181 Roo.Element.uncache(this.el, this.tr);
9186 * @class Roo.Toolbar.Item
9187 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
9189 * Creates a new Item
9190 * @param {HTMLElement} el
9192 Roo.Toolbar.Item = function(el){
9194 if (typeof (el.xtype) != 'undefined') {
9199 this.el = Roo.getDom(el);
9200 this.id = Roo.id(this.el);
9201 this.hidden = false;
9206 * Fires when the button is rendered
9207 * @param {Button} this
9211 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
9213 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
9214 //Roo.Toolbar.Item.prototype = {
9217 * Get this item's HTML Element
9218 * @return {HTMLElement}
9225 render : function(td){
9228 td.appendChild(this.el);
9230 this.fireEvent('render', this);
9234 * Removes and destroys this item.
9236 destroy : function(){
9237 this.td.parentNode.removeChild(this.td);
9244 this.hidden = false;
9245 this.td.style.display = "";
9253 this.td.style.display = "none";
9257 * Convenience function for boolean show/hide.
9258 * @param {Boolean} visible true to show/false to hide
9260 setVisible: function(visible){
9269 * Try to focus this item.
9272 Roo.fly(this.el).focus();
9276 * Disables this item.
9278 disable : function(){
9279 Roo.fly(this.td).addClass("x-item-disabled");
9280 this.disabled = true;
9281 this.el.disabled = true;
9285 * Enables this item.
9287 enable : function(){
9288 Roo.fly(this.td).removeClass("x-item-disabled");
9289 this.disabled = false;
9290 this.el.disabled = false;
9296 * @class Roo.Toolbar.Separator
9297 * @extends Roo.Toolbar.Item
9298 * A simple toolbar separator class
9300 * Creates a new Separator
9302 Roo.Toolbar.Separator = function(cfg){
9304 var s = document.createElement("span");
9305 s.className = "ytb-sep";
9310 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
9312 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
9314 disable:Roo.emptyFn,
9319 * @class Roo.Toolbar.Spacer
9320 * @extends Roo.Toolbar.Item
9321 * A simple element that adds extra horizontal space to a toolbar.
9323 * Creates a new Spacer
9325 Roo.Toolbar.Spacer = function(cfg){
9326 var s = document.createElement("div");
9327 s.className = "ytb-spacer";
9331 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
9333 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
9335 disable:Roo.emptyFn,
9340 * @class Roo.Toolbar.Fill
9341 * @extends Roo.Toolbar.Spacer
9342 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
9344 * Creates a new Spacer
9346 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
9348 render : function(td){
9349 td.style.width = '100%';
9350 Roo.Toolbar.Fill.superclass.render.call(this, td);
9355 * @class Roo.Toolbar.TextItem
9356 * @extends Roo.Toolbar.Item
9357 * A simple class that renders text directly into a toolbar.
9359 * Creates a new TextItem
9360 * @cfg {string} text
9362 Roo.Toolbar.TextItem = function(cfg){
9363 var text = cfg || "";
9364 if (typeof(cfg) == 'object') {
9365 text = cfg.text || "";
9369 var s = document.createElement("span");
9370 s.className = "ytb-text";
9376 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
9378 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
9382 disable:Roo.emptyFn,
9388 this.hidden = false;
9389 this.el.style.display = "";
9397 this.el.style.display = "none";
9403 * @class Roo.Toolbar.Button
9404 * @extends Roo.Button
9405 * A button that renders into a toolbar.
9407 * Creates a new Button
9408 * @param {Object} config A standard {@link Roo.Button} config object
9410 Roo.Toolbar.Button = function(config){
9411 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
9413 Roo.extend(Roo.Toolbar.Button, Roo.Button,
9417 render : function(td){
9419 Roo.Toolbar.Button.superclass.render.call(this, td);
9423 * Removes and destroys this button
9425 destroy : function(){
9426 Roo.Toolbar.Button.superclass.destroy.call(this);
9427 this.td.parentNode.removeChild(this.td);
9434 this.hidden = false;
9435 this.td.style.display = "";
9443 this.td.style.display = "none";
9447 * Disables this item
9449 disable : function(){
9450 Roo.fly(this.td).addClass("x-item-disabled");
9451 this.disabled = true;
9457 enable : function(){
9458 Roo.fly(this.td).removeClass("x-item-disabled");
9459 this.disabled = false;
9463 Roo.ToolbarButton = Roo.Toolbar.Button;
9466 * @class Roo.Toolbar.SplitButton
9467 * @extends Roo.SplitButton
9468 * A menu button that renders into a toolbar.
9470 * Creates a new SplitButton
9471 * @param {Object} config A standard {@link Roo.SplitButton} config object
9473 Roo.Toolbar.SplitButton = function(config){
9474 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
9476 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
9477 render : function(td){
9479 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
9483 * Removes and destroys this button
9485 destroy : function(){
9486 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
9487 this.td.parentNode.removeChild(this.td);
9494 this.hidden = false;
9495 this.td.style.display = "";
9503 this.td.style.display = "none";
9508 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
9510 * Ext JS Library 1.1.1
9511 * Copyright(c) 2006-2007, Ext JS, LLC.
9513 * Originally Released Under LGPL - original licence link has changed is not relivant.
9516 * <script type="text/javascript">
9520 * @class Roo.PagingToolbar
9521 * @extends Roo.Toolbar
9522 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
9523 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
9525 * Create a new PagingToolbar
9526 * @param {Object} config The config object
9528 Roo.PagingToolbar = function(el, ds, config)
9530 // old args format still supported... - xtype is prefered..
9531 if (typeof(el) == 'object' && el.xtype) {
9532 // created from xtype...
9535 el = config.container;
9539 items = config.items;
9543 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
9546 this.renderButtons(this.el);
9549 // supprot items array.
9551 Roo.each(items, function(e) {
9552 this.add(Roo.factory(e));
9557 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
9560 * @cfg {String/HTMLElement/Element} container
9561 * container The id or element that will contain the toolbar
9564 * @cfg {Boolean} displayInfo
9565 * True to display the displayMsg (defaults to false)
9570 * @cfg {Number} pageSize
9571 * The number of records to display per page (defaults to 20)
9575 * @cfg {String} displayMsg
9576 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
9578 displayMsg : 'Displaying {0} - {1} of {2}',
9580 * @cfg {String} emptyMsg
9581 * The message to display when no records are found (defaults to "No data to display")
9583 emptyMsg : 'No data to display',
9585 * Customizable piece of the default paging text (defaults to "Page")
9588 beforePageText : "Page",
9590 * Customizable piece of the default paging text (defaults to "of %0")
9593 afterPageText : "of {0}",
9595 * Customizable piece of the default paging text (defaults to "First Page")
9598 firstText : "First Page",
9600 * Customizable piece of the default paging text (defaults to "Previous Page")
9603 prevText : "Previous Page",
9605 * Customizable piece of the default paging text (defaults to "Next Page")
9608 nextText : "Next Page",
9610 * Customizable piece of the default paging text (defaults to "Last Page")
9613 lastText : "Last Page",
9615 * Customizable piece of the default paging text (defaults to "Refresh")
9618 refreshText : "Refresh",
9621 renderButtons : function(el){
9622 Roo.PagingToolbar.superclass.render.call(this, el);
9623 this.first = this.addButton({
9624 tooltip: this.firstText,
9625 cls: "x-btn-icon x-grid-page-first",
9627 handler: this.onClick.createDelegate(this, ["first"])
9629 this.prev = this.addButton({
9630 tooltip: this.prevText,
9631 cls: "x-btn-icon x-grid-page-prev",
9633 handler: this.onClick.createDelegate(this, ["prev"])
9635 //this.addSeparator();
9636 this.add(this.beforePageText);
9637 this.field = Roo.get(this.addDom({
9642 cls: "x-grid-page-number"
9644 this.field.on("keydown", this.onPagingKeydown, this);
9645 this.field.on("focus", function(){this.dom.select();});
9646 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
9647 this.field.setHeight(18);
9648 //this.addSeparator();
9649 this.next = this.addButton({
9650 tooltip: this.nextText,
9651 cls: "x-btn-icon x-grid-page-next",
9653 handler: this.onClick.createDelegate(this, ["next"])
9655 this.last = this.addButton({
9656 tooltip: this.lastText,
9657 cls: "x-btn-icon x-grid-page-last",
9659 handler: this.onClick.createDelegate(this, ["last"])
9661 //this.addSeparator();
9662 this.loading = this.addButton({
9663 tooltip: this.refreshText,
9664 cls: "x-btn-icon x-grid-loading",
9665 handler: this.onClick.createDelegate(this, ["refresh"])
9668 if(this.displayInfo){
9669 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
9674 updateInfo : function(){
9676 var count = this.ds.getCount();
9677 var msg = count == 0 ?
9681 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
9683 this.displayEl.update(msg);
9688 onLoad : function(ds, r, o){
9689 this.cursor = o.params ? o.params.start : 0;
9690 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
9692 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
9693 this.field.dom.value = ap;
9694 this.first.setDisabled(ap == 1);
9695 this.prev.setDisabled(ap == 1);
9696 this.next.setDisabled(ap == ps);
9697 this.last.setDisabled(ap == ps);
9698 this.loading.enable();
9703 getPageData : function(){
9704 var total = this.ds.getTotalCount();
9707 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
9708 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
9713 onLoadError : function(){
9714 this.loading.enable();
9718 onPagingKeydown : function(e){
9720 var d = this.getPageData();
9722 var v = this.field.dom.value, pageNum;
9723 if(!v || isNaN(pageNum = parseInt(v, 10))){
9724 this.field.dom.value = d.activePage;
9727 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
9728 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
9731 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))
9733 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
9734 this.field.dom.value = pageNum;
9735 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
9738 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
9740 var v = this.field.dom.value, pageNum;
9741 var increment = (e.shiftKey) ? 10 : 1;
9742 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
9745 if(!v || isNaN(pageNum = parseInt(v, 10))) {
9746 this.field.dom.value = d.activePage;
9749 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
9751 this.field.dom.value = parseInt(v, 10) + increment;
9752 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
9753 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
9760 beforeLoad : function(){
9762 this.loading.disable();
9766 * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
9767 * @param {String} which (first|prev|next|last|refresh) which button to press.
9771 onClick : function(which){
9775 ds.load({params:{start: 0, limit: this.pageSize}});
9778 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
9781 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
9784 var total = ds.getTotalCount();
9785 var extra = total % this.pageSize;
9786 var lastStart = extra ? (total - extra) : total-this.pageSize;
9787 ds.load({params:{start: lastStart, limit: this.pageSize}});
9790 ds.load({params:{start: this.cursor, limit: this.pageSize}});
9796 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
9797 * @param {Roo.data.Store} store The data store to unbind
9799 unbind : function(ds){
9800 ds.un("beforeload", this.beforeLoad, this);
9801 ds.un("load", this.onLoad, this);
9802 ds.un("loadexception", this.onLoadError, this);
9803 ds.un("remove", this.updateInfo, this);
9804 ds.un("add", this.updateInfo, this);
9805 this.ds = undefined;
9809 * Binds the paging toolbar to the specified {@link Roo.data.Store}
9810 * @param {Roo.data.Store} store The data store to bind
9812 bind : function(ds){
9813 ds.on("beforeload", this.beforeLoad, this);
9814 ds.on("load", this.onLoad, this);
9815 ds.on("loadexception", this.onLoadError, this);
9816 ds.on("remove", this.updateInfo, this);
9817 ds.on("add", this.updateInfo, this);
9822 * Ext JS Library 1.1.1
9823 * Copyright(c) 2006-2007, Ext JS, LLC.
9825 * Originally Released Under LGPL - original licence link has changed is not relivant.
9828 * <script type="text/javascript">
9832 * @class Roo.Resizable
9833 * @extends Roo.util.Observable
9834 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
9835 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
9836 * 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
9837 * the element will be wrapped for you automatically.</p>
9838 * <p>Here is the list of valid resize handles:</p>
9841 ------ -------------------
9850 'hd' horizontal drag
9853 * <p>Here's an example showing the creation of a typical Resizable:</p>
9855 var resizer = new Roo.Resizable("element-id", {
9863 resizer.on("resize", myHandler);
9865 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
9866 * resizer.east.setDisplayed(false);</p>
9867 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
9868 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
9869 * resize operation's new size (defaults to [0, 0])
9870 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
9871 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
9872 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
9873 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
9874 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
9875 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
9876 * @cfg {Number} width The width of the element in pixels (defaults to null)
9877 * @cfg {Number} height The height of the element in pixels (defaults to null)
9878 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
9879 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
9880 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
9881 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
9882 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
9883 * in favor of the handles config option (defaults to false)
9884 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
9885 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
9886 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
9887 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
9888 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
9889 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
9890 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
9891 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
9892 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
9893 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
9894 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
9896 * Create a new resizable component
9897 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
9898 * @param {Object} config configuration options
9900 Roo.Resizable = function(el, config)
9902 this.el = Roo.get(el);
9904 if(config && config.wrap){
9905 config.resizeChild = this.el;
9906 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
9907 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
9908 this.el.setStyle("overflow", "hidden");
9909 this.el.setPositioning(config.resizeChild.getPositioning());
9910 config.resizeChild.clearPositioning();
9911 if(!config.width || !config.height){
9912 var csize = config.resizeChild.getSize();
9913 this.el.setSize(csize.width, csize.height);
9915 if(config.pinned && !config.adjustments){
9916 config.adjustments = "auto";
9920 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
9921 this.proxy.unselectable();
9922 this.proxy.enableDisplayMode('block');
9924 Roo.apply(this, config);
9927 this.disableTrackOver = true;
9928 this.el.addClass("x-resizable-pinned");
9930 // if the element isn't positioned, make it relative
9931 var position = this.el.getStyle("position");
9932 if(position != "absolute" && position != "fixed"){
9933 this.el.setStyle("position", "relative");
9935 if(!this.handles){ // no handles passed, must be legacy style
9936 this.handles = 's,e,se';
9937 if(this.multiDirectional){
9938 this.handles += ',n,w';
9941 if(this.handles == "all"){
9942 this.handles = "n s e w ne nw se sw";
9944 var hs = this.handles.split(/\s*?[,;]\s*?| /);
9945 var ps = Roo.Resizable.positions;
9946 for(var i = 0, len = hs.length; i < len; i++){
9947 if(hs[i] && ps[hs[i]]){
9948 var pos = ps[hs[i]];
9949 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
9953 this.corner = this.southeast;
9955 // updateBox = the box can move..
9956 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
9957 this.updateBox = true;
9960 this.activeHandle = null;
9962 if(this.resizeChild){
9963 if(typeof this.resizeChild == "boolean"){
9964 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
9966 this.resizeChild = Roo.get(this.resizeChild, true);
9970 if(this.adjustments == "auto"){
9971 var rc = this.resizeChild;
9972 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
9973 if(rc && (hw || hn)){
9974 rc.position("relative");
9975 rc.setLeft(hw ? hw.el.getWidth() : 0);
9976 rc.setTop(hn ? hn.el.getHeight() : 0);
9978 this.adjustments = [
9979 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
9980 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
9985 this.dd = this.dynamic ?
9986 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
9987 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
9993 * @event beforeresize
9994 * Fired before resize is allowed. Set enabled to false to cancel resize.
9995 * @param {Roo.Resizable} this
9996 * @param {Roo.EventObject} e The mousedown event
9998 "beforeresize" : true,
10001 * Fired a resizing.
10002 * @param {Roo.Resizable} this
10003 * @param {Number} x The new x position
10004 * @param {Number} y The new y position
10005 * @param {Number} w The new w width
10006 * @param {Number} h The new h hight
10007 * @param {Roo.EventObject} e The mouseup event
10012 * Fired after a resize.
10013 * @param {Roo.Resizable} this
10014 * @param {Number} width The new width
10015 * @param {Number} height The new height
10016 * @param {Roo.EventObject} e The mouseup event
10021 if(this.width !== null && this.height !== null){
10022 this.resizeTo(this.width, this.height);
10024 this.updateChildSize();
10027 this.el.dom.style.zoom = 1;
10029 Roo.Resizable.superclass.constructor.call(this);
10032 Roo.extend(Roo.Resizable, Roo.util.Observable, {
10033 resizeChild : false,
10034 adjustments : [0, 0],
10044 multiDirectional : false,
10045 disableTrackOver : false,
10046 easing : 'easeOutStrong',
10047 widthIncrement : 0,
10048 heightIncrement : 0,
10052 preserveRatio : false,
10053 transparent: false,
10059 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
10061 constrainTo: undefined,
10063 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
10065 resizeRegion: undefined,
10069 * Perform a manual resize
10070 * @param {Number} width
10071 * @param {Number} height
10073 resizeTo : function(width, height){
10074 this.el.setSize(width, height);
10075 this.updateChildSize();
10076 this.fireEvent("resize", this, width, height, null);
10080 startSizing : function(e, handle){
10081 this.fireEvent("beforeresize", this, e);
10082 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
10085 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
10086 this.overlay.unselectable();
10087 this.overlay.enableDisplayMode("block");
10088 this.overlay.on("mousemove", this.onMouseMove, this);
10089 this.overlay.on("mouseup", this.onMouseUp, this);
10091 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
10093 this.resizing = true;
10094 this.startBox = this.el.getBox();
10095 this.startPoint = e.getXY();
10096 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
10097 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
10099 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10100 this.overlay.show();
10102 if(this.constrainTo) {
10103 var ct = Roo.get(this.constrainTo);
10104 this.resizeRegion = ct.getRegion().adjust(
10105 ct.getFrameWidth('t'),
10106 ct.getFrameWidth('l'),
10107 -ct.getFrameWidth('b'),
10108 -ct.getFrameWidth('r')
10112 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
10114 this.proxy.setBox(this.startBox);
10116 this.proxy.setStyle('visibility', 'visible');
10122 onMouseDown : function(handle, e){
10125 this.activeHandle = handle;
10126 this.startSizing(e, handle);
10131 onMouseUp : function(e){
10132 var size = this.resizeElement();
10133 this.resizing = false;
10135 this.overlay.hide();
10137 this.fireEvent("resize", this, size.width, size.height, e);
10141 updateChildSize : function(){
10143 if(this.resizeChild){
10145 var child = this.resizeChild;
10146 var adj = this.adjustments;
10147 if(el.dom.offsetWidth){
10148 var b = el.getSize(true);
10149 child.setSize(b.width+adj[0], b.height+adj[1]);
10151 // Second call here for IE
10152 // The first call enables instant resizing and
10153 // the second call corrects scroll bars if they
10156 setTimeout(function(){
10157 if(el.dom.offsetWidth){
10158 var b = el.getSize(true);
10159 child.setSize(b.width+adj[0], b.height+adj[1]);
10167 snap : function(value, inc, min){
10168 if(!inc || !value) {
10171 var newValue = value;
10172 var m = value % inc;
10175 newValue = value + (inc-m);
10177 newValue = value - m;
10180 return Math.max(min, newValue);
10184 resizeElement : function(){
10185 var box = this.proxy.getBox();
10186 if(this.updateBox){
10187 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
10189 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
10191 this.updateChildSize();
10199 constrain : function(v, diff, m, mx){
10202 }else if(v - diff > mx){
10209 onMouseMove : function(e){
10212 try{// try catch so if something goes wrong the user doesn't get hung
10214 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
10218 //var curXY = this.startPoint;
10219 var curSize = this.curSize || this.startBox;
10220 var x = this.startBox.x, y = this.startBox.y;
10221 var ox = x, oy = y;
10222 var w = curSize.width, h = curSize.height;
10223 var ow = w, oh = h;
10224 var mw = this.minWidth, mh = this.minHeight;
10225 var mxw = this.maxWidth, mxh = this.maxHeight;
10226 var wi = this.widthIncrement;
10227 var hi = this.heightIncrement;
10229 var eventXY = e.getXY();
10230 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
10231 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
10233 var pos = this.activeHandle.position;
10238 w = Math.min(Math.max(mw, w), mxw);
10243 h = Math.min(Math.max(mh, h), mxh);
10248 w = Math.min(Math.max(mw, w), mxw);
10249 h = Math.min(Math.max(mh, h), mxh);
10252 diffY = this.constrain(h, diffY, mh, mxh);
10259 var adiffX = Math.abs(diffX);
10260 var sub = (adiffX % wi); // how much
10261 if (sub > (wi/2)) { // far enough to snap
10262 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
10264 // remove difference..
10265 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
10269 x = Math.max(this.minX, x);
10272 diffX = this.constrain(w, diffX, mw, mxw);
10278 w = Math.min(Math.max(mw, w), mxw);
10279 diffY = this.constrain(h, diffY, mh, mxh);
10284 diffX = this.constrain(w, diffX, mw, mxw);
10285 diffY = this.constrain(h, diffY, mh, mxh);
10292 diffX = this.constrain(w, diffX, mw, mxw);
10294 h = Math.min(Math.max(mh, h), mxh);
10300 var sw = this.snap(w, wi, mw);
10301 var sh = this.snap(h, hi, mh);
10302 if(sw != w || sh != h){
10325 if(this.preserveRatio){
10330 h = Math.min(Math.max(mh, h), mxh);
10335 w = Math.min(Math.max(mw, w), mxw);
10340 w = Math.min(Math.max(mw, w), mxw);
10346 w = Math.min(Math.max(mw, w), mxw);
10352 h = Math.min(Math.max(mh, h), mxh);
10360 h = Math.min(Math.max(mh, h), mxh);
10370 h = Math.min(Math.max(mh, h), mxh);
10378 if (pos == 'hdrag') {
10381 this.proxy.setBounds(x, y, w, h);
10383 this.resizeElement();
10387 this.fireEvent("resizing", this, x, y, w, h, e);
10391 handleOver : function(){
10393 this.el.addClass("x-resizable-over");
10398 handleOut : function(){
10399 if(!this.resizing){
10400 this.el.removeClass("x-resizable-over");
10405 * Returns the element this component is bound to.
10406 * @return {Roo.Element}
10408 getEl : function(){
10413 * Returns the resizeChild element (or null).
10414 * @return {Roo.Element}
10416 getResizeChild : function(){
10417 return this.resizeChild;
10419 groupHandler : function()
10424 * Destroys this resizable. If the element was wrapped and
10425 * removeEl is not true then the element remains.
10426 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10428 destroy : function(removeEl){
10429 this.proxy.remove();
10431 this.overlay.removeAllListeners();
10432 this.overlay.remove();
10434 var ps = Roo.Resizable.positions;
10436 if(typeof ps[k] != "function" && this[ps[k]]){
10437 var h = this[ps[k]];
10438 h.el.removeAllListeners();
10443 this.el.update("");
10450 // hash to map config positions to true positions
10451 Roo.Resizable.positions = {
10452 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
10457 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
10459 // only initialize the template if resizable is used
10460 var tpl = Roo.DomHelper.createTemplate(
10461 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
10464 Roo.Resizable.Handle.prototype.tpl = tpl;
10466 this.position = pos;
10468 // show north drag fro topdra
10469 var handlepos = pos == 'hdrag' ? 'north' : pos;
10471 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
10472 if (pos == 'hdrag') {
10473 this.el.setStyle('cursor', 'pointer');
10475 this.el.unselectable();
10477 this.el.setOpacity(0);
10479 this.el.on("mousedown", this.onMouseDown, this);
10480 if(!disableTrackOver){
10481 this.el.on("mouseover", this.onMouseOver, this);
10482 this.el.on("mouseout", this.onMouseOut, this);
10487 Roo.Resizable.Handle.prototype = {
10488 afterResize : function(rz){
10493 onMouseDown : function(e){
10494 this.rz.onMouseDown(this, e);
10497 onMouseOver : function(e){
10498 this.rz.handleOver(this, e);
10501 onMouseOut : function(e){
10502 this.rz.handleOut(this, e);
10506 * Ext JS Library 1.1.1
10507 * Copyright(c) 2006-2007, Ext JS, LLC.
10509 * Originally Released Under LGPL - original licence link has changed is not relivant.
10512 * <script type="text/javascript">
10516 * @class Roo.Editor
10517 * @extends Roo.Component
10518 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
10520 * Create a new Editor
10521 * @param {Roo.form.Field} field The Field object (or descendant)
10522 * @param {Object} config The config object
10524 Roo.Editor = function(field, config){
10525 Roo.Editor.superclass.constructor.call(this, config);
10526 this.field = field;
10529 * @event beforestartedit
10530 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
10531 * false from the handler of this event.
10532 * @param {Editor} this
10533 * @param {Roo.Element} boundEl The underlying element bound to this editor
10534 * @param {Mixed} value The field value being set
10536 "beforestartedit" : true,
10539 * Fires when this editor is displayed
10540 * @param {Roo.Element} boundEl The underlying element bound to this editor
10541 * @param {Mixed} value The starting field value
10543 "startedit" : true,
10545 * @event beforecomplete
10546 * Fires after a change has been made to the field, but before the change is reflected in the underlying
10547 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
10548 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
10549 * event will not fire since no edit actually occurred.
10550 * @param {Editor} this
10551 * @param {Mixed} value The current field value
10552 * @param {Mixed} startValue The original field value
10554 "beforecomplete" : true,
10557 * Fires after editing is complete and any changed value has been written to the underlying field.
10558 * @param {Editor} this
10559 * @param {Mixed} value The current field value
10560 * @param {Mixed} startValue The original field value
10564 * @event specialkey
10565 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10566 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10567 * @param {Roo.form.Field} this
10568 * @param {Roo.EventObject} e The event object
10570 "specialkey" : true
10574 Roo.extend(Roo.Editor, Roo.Component, {
10576 * @cfg {Boolean/String} autosize
10577 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
10578 * or "height" to adopt the height only (defaults to false)
10581 * @cfg {Boolean} revertInvalid
10582 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
10583 * validation fails (defaults to true)
10586 * @cfg {Boolean} ignoreNoChange
10587 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
10588 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
10589 * will never be ignored.
10592 * @cfg {Boolean} hideEl
10593 * False to keep the bound element visible while the editor is displayed (defaults to true)
10596 * @cfg {Mixed} value
10597 * The data value of the underlying field (defaults to "")
10601 * @cfg {String} alignment
10602 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
10606 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
10607 * for bottom-right shadow (defaults to "frame")
10611 * @cfg {Boolean} constrain True to constrain the editor to the viewport
10615 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
10617 completeOnEnter : false,
10619 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
10621 cancelOnEsc : false,
10623 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
10628 onRender : function(ct, position){
10629 this.el = new Roo.Layer({
10630 shadow: this.shadow,
10636 constrain: this.constrain
10638 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
10639 if(this.field.msgTarget != 'title'){
10640 this.field.msgTarget = 'qtip';
10642 this.field.render(this.el);
10644 this.field.el.dom.setAttribute('autocomplete', 'off');
10646 this.field.on("specialkey", this.onSpecialKey, this);
10647 if(this.swallowKeys){
10648 this.field.el.swallowEvent(['keydown','keypress']);
10651 this.field.on("blur", this.onBlur, this);
10652 if(this.field.grow){
10653 this.field.on("autosize", this.el.sync, this.el, {delay:1});
10657 onSpecialKey : function(field, e)
10659 //Roo.log('editor onSpecialKey');
10660 if(this.completeOnEnter && e.getKey() == e.ENTER){
10662 this.completeEdit();
10665 // do not fire special key otherwise it might hide close the editor...
10666 if(e.getKey() == e.ENTER){
10669 if(this.cancelOnEsc && e.getKey() == e.ESC){
10673 this.fireEvent('specialkey', field, e);
10678 * Starts the editing process and shows the editor.
10679 * @param {String/HTMLElement/Element} el The element to edit
10680 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
10681 * to the innerHTML of el.
10683 startEdit : function(el, value){
10685 this.completeEdit();
10687 this.boundEl = Roo.get(el);
10688 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
10689 if(!this.rendered){
10690 this.render(this.parentEl || document.body);
10692 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
10695 this.startValue = v;
10696 this.field.setValue(v);
10698 var sz = this.boundEl.getSize();
10699 switch(this.autoSize){
10701 this.setSize(sz.width, "");
10704 this.setSize("", sz.height);
10707 this.setSize(sz.width, sz.height);
10710 this.el.alignTo(this.boundEl, this.alignment);
10711 this.editing = true;
10713 Roo.QuickTips.disable();
10719 * Sets the height and width of this editor.
10720 * @param {Number} width The new width
10721 * @param {Number} height The new height
10723 setSize : function(w, h){
10724 this.field.setSize(w, h);
10731 * Realigns the editor to the bound field based on the current alignment config value.
10733 realign : function(){
10734 this.el.alignTo(this.boundEl, this.alignment);
10738 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
10739 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
10741 completeEdit : function(remainVisible){
10745 var v = this.getValue();
10746 if(this.revertInvalid !== false && !this.field.isValid()){
10747 v = this.startValue;
10748 this.cancelEdit(true);
10750 if(String(v) === String(this.startValue) && this.ignoreNoChange){
10751 this.editing = false;
10755 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
10756 this.editing = false;
10757 if(this.updateEl && this.boundEl){
10758 this.boundEl.update(v);
10760 if(remainVisible !== true){
10763 this.fireEvent("complete", this, v, this.startValue);
10768 onShow : function(){
10770 if(this.hideEl !== false){
10771 this.boundEl.hide();
10774 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
10775 this.fixIEFocus = true;
10776 this.deferredFocus.defer(50, this);
10778 this.field.focus();
10780 this.fireEvent("startedit", this.boundEl, this.startValue);
10783 deferredFocus : function(){
10785 this.field.focus();
10790 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
10791 * reverted to the original starting value.
10792 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
10793 * cancel (defaults to false)
10795 cancelEdit : function(remainVisible){
10797 this.setValue(this.startValue);
10798 if(remainVisible !== true){
10805 onBlur : function(){
10806 if(this.allowBlur !== true && this.editing){
10807 this.completeEdit();
10812 onHide : function(){
10814 this.completeEdit();
10818 if(this.field.collapse){
10819 this.field.collapse();
10822 if(this.hideEl !== false){
10823 this.boundEl.show();
10826 Roo.QuickTips.enable();
10831 * Sets the data value of the editor
10832 * @param {Mixed} value Any valid value supported by the underlying field
10834 setValue : function(v){
10835 this.field.setValue(v);
10839 * Gets the data value of the editor
10840 * @return {Mixed} The data value
10842 getValue : function(){
10843 return this.field.getValue();
10847 * Ext JS Library 1.1.1
10848 * Copyright(c) 2006-2007, Ext JS, LLC.
10850 * Originally Released Under LGPL - original licence link has changed is not relivant.
10853 * <script type="text/javascript">
10857 * @class Roo.BasicDialog
10858 * @extends Roo.util.Observable
10859 * @parent none builder
10860 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
10862 var dlg = new Roo.BasicDialog("my-dlg", {
10871 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
10872 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
10873 dlg.addButton('Cancel', dlg.hide, dlg);
10876 <b>A Dialog should always be a direct child of the body element.</b>
10877 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
10878 * @cfg {String} title Default text to display in the title bar (defaults to null)
10879 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
10880 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
10881 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
10882 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
10883 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
10884 * (defaults to null with no animation)
10885 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
10886 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
10887 * property for valid values (defaults to 'all')
10888 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
10889 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
10890 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
10891 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
10892 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
10893 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
10894 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
10895 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
10896 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
10897 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
10898 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
10899 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
10900 * draggable = true (defaults to false)
10901 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
10902 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10903 * shadow (defaults to false)
10904 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
10905 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
10906 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
10907 * @cfg {Array} buttons Array of buttons
10908 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
10910 * Create a new BasicDialog.
10911 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
10912 * @param {Object} config Configuration options
10914 Roo.BasicDialog = function(el, config){
10915 this.el = Roo.get(el);
10916 var dh = Roo.DomHelper;
10917 if(!this.el && config && config.autoCreate){
10918 if(typeof config.autoCreate == "object"){
10919 if(!config.autoCreate.id){
10920 config.autoCreate.id = el;
10922 this.el = dh.append(document.body,
10923 config.autoCreate, true);
10925 this.el = dh.append(document.body,
10926 {tag: "div", id: el, style:'visibility:hidden;'}, true);
10930 el.setDisplayed(true);
10931 el.hide = this.hideAction;
10933 el.addClass("x-dlg");
10935 Roo.apply(this, config);
10937 this.proxy = el.createProxy("x-dlg-proxy");
10938 this.proxy.hide = this.hideAction;
10939 this.proxy.setOpacity(.5);
10943 el.setWidth(config.width);
10946 el.setHeight(config.height);
10948 this.size = el.getSize();
10949 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
10950 this.xy = [config.x,config.y];
10952 this.xy = el.getCenterXY(true);
10954 /** The header element @type Roo.Element */
10955 this.header = el.child("> .x-dlg-hd");
10956 /** The body element @type Roo.Element */
10957 this.body = el.child("> .x-dlg-bd");
10958 /** The footer element @type Roo.Element */
10959 this.footer = el.child("> .x-dlg-ft");
10962 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
10965 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
10968 this.header.unselectable();
10970 this.header.update(this.title);
10972 // this element allows the dialog to be focused for keyboard event
10973 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
10974 this.focusEl.swallowEvent("click", true);
10976 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
10978 // wrap the body and footer for special rendering
10979 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
10981 this.bwrap.dom.appendChild(this.footer.dom);
10984 this.bg = this.el.createChild({
10985 tag: "div", cls:"x-dlg-bg",
10986 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
10988 this.centerBg = this.bg.child("div.x-dlg-bg-center");
10991 if(this.autoScroll !== false && !this.autoTabs){
10992 this.body.setStyle("overflow", "auto");
10995 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
10997 if(this.closable !== false){
10998 this.el.addClass("x-dlg-closable");
10999 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
11000 this.close.on("click", this.closeClick, this);
11001 this.close.addClassOnOver("x-dlg-close-over");
11003 if(this.collapsible !== false){
11004 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
11005 this.collapseBtn.on("click", this.collapseClick, this);
11006 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
11007 this.header.on("dblclick", this.collapseClick, this);
11009 if(this.resizable !== false){
11010 this.el.addClass("x-dlg-resizable");
11011 this.resizer = new Roo.Resizable(el, {
11012 minWidth: this.minWidth || 80,
11013 minHeight:this.minHeight || 80,
11014 handles: this.resizeHandles || "all",
11017 this.resizer.on("beforeresize", this.beforeResize, this);
11018 this.resizer.on("resize", this.onResize, this);
11020 if(this.draggable !== false){
11021 el.addClass("x-dlg-draggable");
11022 if (!this.proxyDrag) {
11023 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
11026 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
11028 dd.setHandleElId(this.header.id);
11029 dd.endDrag = this.endMove.createDelegate(this);
11030 dd.startDrag = this.startMove.createDelegate(this);
11031 dd.onDrag = this.onDrag.createDelegate(this);
11036 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
11037 this.mask.enableDisplayMode("block");
11039 this.el.addClass("x-dlg-modal");
11042 this.shadow = new Roo.Shadow({
11043 mode : typeof this.shadow == "string" ? this.shadow : "sides",
11044 offset : this.shadowOffset
11047 this.shadowOffset = 0;
11049 if(Roo.useShims && this.shim !== false){
11050 this.shim = this.el.createShim();
11051 this.shim.hide = this.hideAction;
11059 if (this.buttons) {
11060 var bts= this.buttons;
11062 Roo.each(bts, function(b) {
11071 * Fires when a key is pressed
11072 * @param {Roo.BasicDialog} this
11073 * @param {Roo.EventObject} e
11078 * Fires when this dialog is moved by the user.
11079 * @param {Roo.BasicDialog} this
11080 * @param {Number} x The new page X
11081 * @param {Number} y The new page Y
11086 * Fires when this dialog is resized by the user.
11087 * @param {Roo.BasicDialog} this
11088 * @param {Number} width The new width
11089 * @param {Number} height The new height
11093 * @event beforehide
11094 * Fires before this dialog is hidden.
11095 * @param {Roo.BasicDialog} this
11097 "beforehide" : true,
11100 * Fires when this dialog is hidden.
11101 * @param {Roo.BasicDialog} this
11105 * @event beforeshow
11106 * Fires before this dialog is shown.
11107 * @param {Roo.BasicDialog} this
11109 "beforeshow" : true,
11112 * Fires when this dialog is shown.
11113 * @param {Roo.BasicDialog} this
11117 el.on("keydown", this.onKeyDown, this);
11118 el.on("mousedown", this.toFront, this);
11119 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
11121 Roo.DialogManager.register(this);
11122 Roo.BasicDialog.superclass.constructor.call(this);
11125 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
11126 shadowOffset: Roo.isIE ? 6 : 5,
11129 minButtonWidth: 75,
11130 defaultButton: null,
11131 buttonAlign: "right",
11136 * Sets the dialog title text
11137 * @param {String} text The title text to display
11138 * @return {Roo.BasicDialog} this
11140 setTitle : function(text){
11141 this.header.update(text);
11146 closeClick : function(){
11151 collapseClick : function(){
11152 this[this.collapsed ? "expand" : "collapse"]();
11156 * Collapses the dialog to its minimized state (only the title bar is visible).
11157 * Equivalent to the user clicking the collapse dialog button.
11159 collapse : function(){
11160 if(!this.collapsed){
11161 this.collapsed = true;
11162 this.el.addClass("x-dlg-collapsed");
11163 this.restoreHeight = this.el.getHeight();
11164 this.resizeTo(this.el.getWidth(), this.header.getHeight());
11169 * Expands a collapsed dialog back to its normal state. Equivalent to the user
11170 * clicking the expand dialog button.
11172 expand : function(){
11173 if(this.collapsed){
11174 this.collapsed = false;
11175 this.el.removeClass("x-dlg-collapsed");
11176 this.resizeTo(this.el.getWidth(), this.restoreHeight);
11181 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
11182 * @return {Roo.panel.Tab} The tabs component
11184 initTabs : function(){
11185 var tabs = this.getTabs();
11186 while(tabs.getTab(0)){
11189 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
11191 tabs.addTab(Roo.id(dom), dom.title);
11199 beforeResize : function(){
11200 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
11204 onResize : function(){
11205 this.refreshSize();
11206 this.syncBodyHeight();
11207 this.adjustAssets();
11209 this.fireEvent("resize", this, this.size.width, this.size.height);
11213 onKeyDown : function(e){
11214 if(this.isVisible()){
11215 this.fireEvent("keydown", this, e);
11220 * Resizes the dialog.
11221 * @param {Number} width
11222 * @param {Number} height
11223 * @return {Roo.BasicDialog} this
11225 resizeTo : function(width, height){
11226 this.el.setSize(width, height);
11227 this.size = {width: width, height: height};
11228 this.syncBodyHeight();
11229 if(this.fixedcenter){
11232 if(this.isVisible()){
11233 this.constrainXY();
11234 this.adjustAssets();
11236 this.fireEvent("resize", this, width, height);
11242 * Resizes the dialog to fit the specified content size.
11243 * @param {Number} width
11244 * @param {Number} height
11245 * @return {Roo.BasicDialog} this
11247 setContentSize : function(w, h){
11248 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
11249 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
11250 //if(!this.el.isBorderBox()){
11251 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
11252 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
11255 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
11256 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
11258 this.resizeTo(w, h);
11263 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
11264 * executed in response to a particular key being pressed while the dialog is active.
11265 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
11266 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11267 * @param {Function} fn The function to call
11268 * @param {Object} scope (optional) The scope of the function
11269 * @return {Roo.BasicDialog} this
11271 addKeyListener : function(key, fn, scope){
11272 var keyCode, shift, ctrl, alt;
11273 if(typeof key == "object" && !(key instanceof Array)){
11274 keyCode = key["key"];
11275 shift = key["shift"];
11276 ctrl = key["ctrl"];
11281 var handler = function(dlg, e){
11282 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
11283 var k = e.getKey();
11284 if(keyCode instanceof Array){
11285 for(var i = 0, len = keyCode.length; i < len; i++){
11286 if(keyCode[i] == k){
11287 fn.call(scope || window, dlg, k, e);
11293 fn.call(scope || window, dlg, k, e);
11298 this.on("keydown", handler);
11303 * Returns the panel.Tab component (creates it if it doesn't exist).
11304 * Note: If you wish to simply check for the existence of tabs without creating them,
11305 * check for a null 'tabs' property.
11306 * @return {Roo.panel.Tab} The tabs component
11308 getTabs : function(){
11310 this.el.addClass("x-dlg-auto-tabs");
11311 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
11312 this.tabs = new Roo.panel.Tab(this.body.dom, this.tabPosition == "bottom");
11318 * Adds a button to the footer section of the dialog.
11319 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
11320 * object or a valid Roo.DomHelper element config
11321 * @param {Function} handler The function called when the button is clicked
11322 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
11323 * @return {Roo.Button} The new button
11325 addButton : function(config, handler, scope){
11326 var dh = Roo.DomHelper;
11328 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
11330 if(!this.btnContainer){
11331 var tb = this.footer.createChild({
11333 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
11334 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
11336 this.btnContainer = tb.firstChild.firstChild.firstChild;
11341 minWidth: this.minButtonWidth,
11344 if(typeof config == "string"){
11345 bconfig.text = config;
11348 bconfig.dhconfig = config;
11350 Roo.apply(bconfig, config);
11354 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
11355 bconfig.position = Math.max(0, bconfig.position);
11356 fc = this.btnContainer.childNodes[bconfig.position];
11359 var btn = new Roo.Button(
11361 this.btnContainer.insertBefore(document.createElement("td"),fc)
11362 : this.btnContainer.appendChild(document.createElement("td")),
11363 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
11366 this.syncBodyHeight();
11369 * Array of all the buttons that have been added to this dialog via addButton
11374 this.buttons.push(btn);
11379 * Sets the default button to be focused when the dialog is displayed.
11380 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
11381 * @return {Roo.BasicDialog} this
11383 setDefaultButton : function(btn){
11384 this.defaultButton = btn;
11389 getHeaderFooterHeight : function(safe){
11392 height += this.header.getHeight();
11395 var fm = this.footer.getMargins();
11396 height += (this.footer.getHeight()+fm.top+fm.bottom);
11398 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
11399 height += this.centerBg.getPadding("tb");
11404 syncBodyHeight : function()
11406 var bd = this.body, // the text
11407 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
11409 var height = this.size.height - this.getHeaderFooterHeight(false);
11410 bd.setHeight(height-bd.getMargins("tb"));
11411 var hh = this.header.getHeight();
11412 var h = this.size.height-hh;
11415 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
11416 bw.setHeight(h-cb.getPadding("tb"));
11418 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
11419 bd.setWidth(bw.getWidth(true));
11421 this.tabs.syncHeight();
11423 this.tabs.el.repaint();
11429 * Restores the previous state of the dialog if Roo.state is configured.
11430 * @return {Roo.BasicDialog} this
11432 restoreState : function(){
11433 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
11434 if(box && box.width){
11435 this.xy = [box.x, box.y];
11436 this.resizeTo(box.width, box.height);
11442 beforeShow : function(){
11444 if(this.fixedcenter){
11445 this.xy = this.el.getCenterXY(true);
11448 Roo.get(document.body).addClass("x-body-masked");
11449 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
11452 this.constrainXY();
11456 animShow : function(){
11457 var b = Roo.get(this.animateTarget).getBox();
11458 this.proxy.setSize(b.width, b.height);
11459 this.proxy.setLocation(b.x, b.y);
11461 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
11462 true, .35, this.showEl.createDelegate(this));
11466 * Shows the dialog.
11467 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
11468 * @return {Roo.BasicDialog} this
11470 show : function(animateTarget){
11471 if (this.fireEvent("beforeshow", this) === false){
11474 if(this.syncHeightBeforeShow){
11475 this.syncBodyHeight();
11476 }else if(this.firstShow){
11477 this.firstShow = false;
11478 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
11480 this.animateTarget = animateTarget || this.animateTarget;
11481 if(!this.el.isVisible()){
11483 if(this.animateTarget && Roo.get(this.animateTarget)){
11493 showEl : function(){
11495 this.el.setXY(this.xy);
11497 this.adjustAssets(true);
11500 // IE peekaboo bug - fix found by Dave Fenwick
11504 this.fireEvent("show", this);
11508 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
11509 * dialog itself will receive focus.
11511 focus : function(){
11512 if(this.defaultButton){
11513 this.defaultButton.focus();
11515 this.focusEl.focus();
11520 constrainXY : function(){
11521 if(this.constraintoviewport !== false){
11522 if(!this.viewSize){
11523 if(this.container){
11524 var s = this.container.getSize();
11525 this.viewSize = [s.width, s.height];
11527 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
11530 var s = Roo.get(this.container||document).getScroll();
11532 var x = this.xy[0], y = this.xy[1];
11533 var w = this.size.width, h = this.size.height;
11534 var vw = this.viewSize[0], vh = this.viewSize[1];
11535 // only move it if it needs it
11537 // first validate right/bottom
11538 if(x + w > vw+s.left){
11542 if(y + h > vh+s.top){
11546 // then make sure top/left isn't negative
11558 if(this.isVisible()){
11559 this.el.setLocation(x, y);
11560 this.adjustAssets();
11567 onDrag : function(){
11568 if(!this.proxyDrag){
11569 this.xy = this.el.getXY();
11570 this.adjustAssets();
11575 adjustAssets : function(doShow){
11576 var x = this.xy[0], y = this.xy[1];
11577 var w = this.size.width, h = this.size.height;
11578 if(doShow === true){
11580 this.shadow.show(this.el);
11586 if(this.shadow && this.shadow.isVisible()){
11587 this.shadow.show(this.el);
11589 if(this.shim && this.shim.isVisible()){
11590 this.shim.setBounds(x, y, w, h);
11595 adjustViewport : function(w, h){
11597 w = Roo.lib.Dom.getViewWidth();
11598 h = Roo.lib.Dom.getViewHeight();
11601 this.viewSize = [w, h];
11602 if(this.modal && this.mask.isVisible()){
11603 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
11604 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
11606 if(this.isVisible()){
11607 this.constrainXY();
11612 * Destroys this dialog and all its supporting elements (including any tabs, shim,
11613 * shadow, proxy, mask, etc.) Also removes all event listeners.
11614 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
11616 destroy : function(removeEl){
11617 if(this.isVisible()){
11618 this.animateTarget = null;
11621 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
11623 this.tabs.destroy(removeEl);
11636 for(var i = 0, len = this.buttons.length; i < len; i++){
11637 this.buttons[i].destroy();
11640 this.el.removeAllListeners();
11641 if(removeEl === true){
11642 this.el.update("");
11645 Roo.DialogManager.unregister(this);
11649 startMove : function(){
11650 if(this.proxyDrag){
11653 if(this.constraintoviewport !== false){
11654 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
11659 endMove : function(){
11660 if(!this.proxyDrag){
11661 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
11663 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
11666 this.refreshSize();
11667 this.adjustAssets();
11669 this.fireEvent("move", this, this.xy[0], this.xy[1]);
11673 * Brings this dialog to the front of any other visible dialogs
11674 * @return {Roo.BasicDialog} this
11676 toFront : function(){
11677 Roo.DialogManager.bringToFront(this);
11682 * Sends this dialog to the back (under) of any other visible dialogs
11683 * @return {Roo.BasicDialog} this
11685 toBack : function(){
11686 Roo.DialogManager.sendToBack(this);
11691 * Centers this dialog in the viewport
11692 * @return {Roo.BasicDialog} this
11694 center : function(){
11695 var xy = this.el.getCenterXY(true);
11696 this.moveTo(xy[0], xy[1]);
11701 * Moves the dialog's top-left corner to the specified point
11702 * @param {Number} x
11703 * @param {Number} y
11704 * @return {Roo.BasicDialog} this
11706 moveTo : function(x, y){
11708 if(this.isVisible()){
11709 this.el.setXY(this.xy);
11710 this.adjustAssets();
11716 * Aligns the dialog to the specified element
11717 * @param {String/HTMLElement/Roo.Element} element The element to align to.
11718 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
11719 * @param {Array} offsets (optional) Offset the positioning by [x, y]
11720 * @return {Roo.BasicDialog} this
11722 alignTo : function(element, position, offsets){
11723 this.xy = this.el.getAlignToXY(element, position, offsets);
11724 if(this.isVisible()){
11725 this.el.setXY(this.xy);
11726 this.adjustAssets();
11732 * Anchors an element to another element and realigns it when the window is resized.
11733 * @param {String/HTMLElement/Roo.Element} element The element to align to.
11734 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
11735 * @param {Array} offsets (optional) Offset the positioning by [x, y]
11736 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
11737 * is a number, it is used as the buffer delay (defaults to 50ms).
11738 * @return {Roo.BasicDialog} this
11740 anchorTo : function(el, alignment, offsets, monitorScroll){
11741 var action = function(){
11742 this.alignTo(el, alignment, offsets);
11744 Roo.EventManager.onWindowResize(action, this);
11745 var tm = typeof monitorScroll;
11746 if(tm != 'undefined'){
11747 Roo.EventManager.on(window, 'scroll', action, this,
11748 {buffer: tm == 'number' ? monitorScroll : 50});
11755 * Returns true if the dialog is visible
11756 * @return {Boolean}
11758 isVisible : function(){
11759 return this.el.isVisible();
11763 animHide : function(callback){
11764 var b = Roo.get(this.animateTarget).getBox();
11766 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
11768 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
11769 this.hideEl.createDelegate(this, [callback]));
11773 * Hides the dialog.
11774 * @param {Function} callback (optional) Function to call when the dialog is hidden
11775 * @return {Roo.BasicDialog} this
11777 hide : function(callback){
11778 if (this.fireEvent("beforehide", this) === false){
11782 this.shadow.hide();
11787 // sometimes animateTarget seems to get set.. causing problems...
11788 // this just double checks..
11789 if(this.animateTarget && Roo.get(this.animateTarget)) {
11790 this.animHide(callback);
11793 this.hideEl(callback);
11799 hideEl : function(callback){
11803 Roo.get(document.body).removeClass("x-body-masked");
11805 this.fireEvent("hide", this);
11806 if(typeof callback == "function"){
11812 hideAction : function(){
11813 this.setLeft("-10000px");
11814 this.setTop("-10000px");
11815 this.setStyle("visibility", "hidden");
11819 refreshSize : function(){
11820 this.size = this.el.getSize();
11821 this.xy = this.el.getXY();
11822 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
11826 // z-index is managed by the DialogManager and may be overwritten at any time
11827 setZIndex : function(index){
11829 this.mask.setStyle("z-index", index);
11832 this.shim.setStyle("z-index", ++index);
11835 this.shadow.setZIndex(++index);
11837 this.el.setStyle("z-index", ++index);
11839 this.proxy.setStyle("z-index", ++index);
11842 this.resizer.proxy.setStyle("z-index", ++index);
11845 this.lastZIndex = index;
11849 * Returns the element for this dialog
11850 * @return {Roo.Element} The underlying dialog Element
11852 getEl : function(){
11858 * @class Roo.DialogManager
11859 * Provides global access to BasicDialogs that have been created and
11860 * support for z-indexing (layering) multiple open dialogs.
11862 Roo.DialogManager = function(){
11864 var accessList = [];
11868 var sortDialogs = function(d1, d2){
11869 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
11873 var orderDialogs = function(){
11874 accessList.sort(sortDialogs);
11875 var seed = Roo.DialogManager.zseed;
11876 for(var i = 0, len = accessList.length; i < len; i++){
11877 var dlg = accessList[i];
11879 dlg.setZIndex(seed + (i*10));
11886 * The starting z-index for BasicDialogs (defaults to 9000)
11887 * @type Number The z-index value
11892 register : function(dlg){
11893 list[dlg.id] = dlg;
11894 accessList.push(dlg);
11898 unregister : function(dlg){
11899 delete list[dlg.id];
11902 if(!accessList.indexOf){
11903 for( i = 0, len = accessList.length; i < len; i++){
11904 if(accessList[i] == dlg){
11905 accessList.splice(i, 1);
11910 i = accessList.indexOf(dlg);
11912 accessList.splice(i, 1);
11918 * Gets a registered dialog by id
11919 * @param {String/Object} id The id of the dialog or a dialog
11920 * @return {Roo.BasicDialog} this
11922 get : function(id){
11923 return typeof id == "object" ? id : list[id];
11927 * Brings the specified dialog to the front
11928 * @param {String/Object} dlg The id of the dialog or a dialog
11929 * @return {Roo.BasicDialog} this
11931 bringToFront : function(dlg){
11932 dlg = this.get(dlg);
11935 dlg._lastAccess = new Date().getTime();
11942 * Sends the specified dialog to the back
11943 * @param {String/Object} dlg The id of the dialog or a dialog
11944 * @return {Roo.BasicDialog} this
11946 sendToBack : function(dlg){
11947 dlg = this.get(dlg);
11948 dlg._lastAccess = -(new Date().getTime());
11954 * Hides all dialogs
11956 hideAll : function(){
11957 for(var id in list){
11958 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
11967 * @class Roo.LayoutDialog
11968 * @extends Roo.BasicDialog
11969 * @children Roo.panel.Content
11970 * @parent builder none
11971 * Dialog which provides adjustments for working with a layout in a Dialog.
11972 * Add your necessary layout config options to the dialog's config.<br>
11973 * Example usage (including a nested layout):
11976 dialog = new Roo.LayoutDialog("download-dlg", {
11985 // layout config merges with the dialog config
11987 tabPosition: "top",
11988 alwaysShowTabs: true
11991 dialog.addKeyListener(27, dialog.hide, dialog);
11992 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
11993 dialog.addButton("Build It!", this.getDownload, this);
11995 // we can even add nested layouts
11996 var innerLayout = new Roo.BorderLayout("dl-inner", {
12006 innerLayout.beginUpdate();
12007 innerLayout.add("east", new Roo.panel.Content("dl-details"));
12008 innerLayout.add("center", new Roo.panel.Content("selection-panel"));
12009 innerLayout.endUpdate(true);
12011 var layout = dialog.getLayout();
12012 layout.beginUpdate();
12013 layout.add("center", new Roo.panel.Content("standard-panel",
12014 {title: "Download the Source", fitToFrame:true}));
12015 layout.add("center", new Roo.panel.NestedLayout(innerLayout,
12016 {title: "Build your own roo.js"}));
12017 layout.getRegion("center").showPanel(sp);
12018 layout.endUpdate();
12022 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
12023 * @param {Object} config configuration options
12025 Roo.LayoutDialog = function(el, cfg){
12028 if (typeof(cfg) == 'undefined') {
12029 config = Roo.apply({}, el);
12030 // not sure why we use documentElement here.. - it should always be body.
12031 // IE7 borks horribly if we use documentElement.
12032 // webkit also does not like documentElement - it creates a body element...
12033 el = Roo.get( document.body || document.documentElement ).createChild();
12034 //config.autoCreate = true;
12038 config.autoTabs = false;
12039 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
12040 this.body.setStyle({overflow:"hidden", position:"relative"});
12041 this.layout = new Roo.BorderLayout(this.body.dom, config);
12042 this.layout.monitorWindowResize = false;
12043 this.el.addClass("x-dlg-auto-layout");
12044 // fix case when center region overwrites center function
12045 this.center = Roo.BasicDialog.prototype.center;
12046 this.on("show", this.layout.layout, this.layout, true);
12047 if (config.items) {
12048 var xitems = config.items;
12049 delete config.items;
12050 Roo.each(xitems, this.addxtype, this);
12055 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
12059 * @cfg {Roo.LayoutRegion} east
12062 * @cfg {Roo.LayoutRegion} west
12065 * @cfg {Roo.LayoutRegion} south
12068 * @cfg {Roo.LayoutRegion} north
12071 * @cfg {Roo.LayoutRegion} center
12074 * @cfg {Roo.Button} buttons[] Bottom buttons..
12079 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
12082 endUpdate : function(){
12083 this.layout.endUpdate();
12087 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
12090 beginUpdate : function(){
12091 this.layout.beginUpdate();
12095 * Get the BorderLayout for this dialog
12096 * @return {Roo.BorderLayout}
12098 getLayout : function(){
12099 return this.layout;
12102 showEl : function(){
12103 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
12105 this.layout.layout();
12110 // Use the syncHeightBeforeShow config option to control this automatically
12111 syncBodyHeight : function(){
12112 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
12113 if(this.layout){this.layout.layout();}
12117 * Add an xtype element (actually adds to the layout.)
12118 * @return {Object} xdata xtype object data.
12121 addxtype : function(c) {
12122 return this.layout.addxtype(c);
12126 * Ext JS Library 1.1.1
12127 * Copyright(c) 2006-2007, Ext JS, LLC.
12129 * Originally Released Under LGPL - original licence link has changed is not relivant.
12132 * <script type="text/javascript">
12136 * @class Roo.MessageBox
12138 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
12142 Roo.Msg.alert('Status', 'Changes saved successfully.');
12144 // Prompt for user data:
12145 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
12147 // process text value...
12151 // Show a dialog using config options:
12153 title:'Save Changes?',
12154 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
12155 buttons: Roo.Msg.YESNOCANCEL,
12162 Roo.MessageBox = function(){
12163 var dlg, opt, mask, waitTimer;
12164 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
12165 var buttons, activeTextEl, bwidth;
12168 var handleButton = function(button){
12170 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
12174 var handleHide = function(){
12175 if(opt && opt.cls){
12176 dlg.el.removeClass(opt.cls);
12179 Roo.TaskMgr.stop(waitTimer);
12185 var updateButtons = function(b){
12188 buttons["ok"].hide();
12189 buttons["cancel"].hide();
12190 buttons["yes"].hide();
12191 buttons["no"].hide();
12192 dlg.footer.dom.style.display = 'none';
12195 dlg.footer.dom.style.display = '';
12196 for(var k in buttons){
12197 if(typeof buttons[k] != "function"){
12200 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
12201 width += buttons[k].el.getWidth()+15;
12211 var handleEsc = function(d, k, e){
12212 if(opt && opt.closable !== false){
12222 * Returns a reference to the underlying {@link Roo.BasicDialog} element
12223 * @return {Roo.BasicDialog} The BasicDialog element
12225 getDialog : function(){
12227 dlg = new Roo.BasicDialog("x-msg-box", {
12232 constraintoviewport:false,
12234 collapsible : false,
12237 width:400, height:100,
12238 buttonAlign:"center",
12239 closeClick : function(){
12240 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
12241 handleButton("no");
12243 handleButton("cancel");
12248 dlg.on("hide", handleHide);
12250 dlg.addKeyListener(27, handleEsc);
12252 var bt = this.buttonText;
12253 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
12254 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
12255 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
12256 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
12257 bodyEl = dlg.body.createChild({
12259 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>'
12261 msgEl = bodyEl.dom.firstChild;
12262 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
12263 textboxEl.enableDisplayMode();
12264 textboxEl.addKeyListener([10,13], function(){
12265 if(dlg.isVisible() && opt && opt.buttons){
12266 if(opt.buttons.ok){
12267 handleButton("ok");
12268 }else if(opt.buttons.yes){
12269 handleButton("yes");
12273 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
12274 textareaEl.enableDisplayMode();
12275 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
12276 progressEl.enableDisplayMode();
12277 var pf = progressEl.dom.firstChild;
12279 pp = Roo.get(pf.firstChild);
12280 pp.setHeight(pf.offsetHeight);
12288 * Updates the message box body text
12289 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
12290 * the XHTML-compliant non-breaking space character '&#160;')
12291 * @return {Roo.MessageBox} This message box
12293 updateText : function(text){
12294 if(!dlg.isVisible() && !opt.width){
12295 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
12297 msgEl.innerHTML = text || ' ';
12299 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
12300 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
12302 Math.min(opt.width || cw , this.maxWidth),
12303 Math.max(opt.minWidth || this.minWidth, bwidth)
12306 activeTextEl.setWidth(w);
12308 if(dlg.isVisible()){
12309 dlg.fixedcenter = false;
12311 // to big, make it scroll. = But as usual stupid IE does not support
12314 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
12315 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
12316 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
12318 bodyEl.dom.style.height = '';
12319 bodyEl.dom.style.overflowY = '';
12322 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
12324 bodyEl.dom.style.overflowX = '';
12327 dlg.setContentSize(w, bodyEl.getHeight());
12328 if(dlg.isVisible()){
12329 dlg.fixedcenter = true;
12335 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
12336 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
12337 * @param {Number} value Any number between 0 and 1 (e.g., .5)
12338 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
12339 * @return {Roo.MessageBox} This message box
12341 updateProgress : function(value, text){
12343 this.updateText(text);
12345 if (pp) { // weird bug on my firefox - for some reason this is not defined
12346 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
12352 * Returns true if the message box is currently displayed
12353 * @return {Boolean} True if the message box is visible, else false
12355 isVisible : function(){
12356 return dlg && dlg.isVisible();
12360 * Hides the message box if it is displayed
12363 if(this.isVisible()){
12369 * Displays a new message box, or reinitializes an existing message box, based on the config options
12370 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
12371 * The following config object properties are supported:
12373 Property Type Description
12374 ---------- --------------- ------------------------------------------------------------------------------------
12375 animEl String/Element An id or Element from which the message box should animate as it opens and
12376 closes (defaults to undefined)
12377 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
12378 cancel:'Bar'}), or false to not show any buttons (defaults to false)
12379 closable Boolean False to hide the top-right close button (defaults to true). Note that
12380 progress and wait dialogs will ignore this property and always hide the
12381 close button as they can only be closed programmatically.
12382 cls String A custom CSS class to apply to the message box element
12383 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
12384 displayed (defaults to 75)
12385 fn Function A callback function to execute after closing the dialog. The arguments to the
12386 function will be btn (the name of the button that was clicked, if applicable,
12387 e.g. "ok"), and text (the value of the active text field, if applicable).
12388 Progress and wait dialogs will ignore this option since they do not respond to
12389 user actions and can only be closed programmatically, so any required function
12390 should be called by the same code after it closes the dialog.
12391 icon String A CSS class that provides a background image to be used as an icon for
12392 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
12393 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
12394 minWidth Number The minimum width in pixels of the message box (defaults to 100)
12395 modal Boolean False to allow user interaction with the page while the message box is
12396 displayed (defaults to true)
12397 msg String A string that will replace the existing message box body text (defaults
12398 to the XHTML-compliant non-breaking space character ' ')
12399 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
12400 progress Boolean True to display a progress bar (defaults to false)
12401 progressText String The text to display inside the progress bar if progress = true (defaults to '')
12402 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
12403 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
12404 title String The title text
12405 value String The string value to set into the active textbox element if displayed
12406 wait Boolean True to display a progress bar (defaults to false)
12407 width Number The width of the dialog in pixels
12414 msg: 'Please enter your address:',
12416 buttons: Roo.MessageBox.OKCANCEL,
12419 animEl: 'addAddressBtn'
12422 * @param {Object} config Configuration options
12423 * @return {Roo.MessageBox} This message box
12425 show : function(options)
12428 // this causes nightmares if you show one dialog after another
12429 // especially on callbacks..
12431 if(this.isVisible()){
12434 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
12435 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
12436 Roo.log("New Dialog Message:" + options.msg )
12437 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
12438 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
12441 var d = this.getDialog();
12443 d.setTitle(opt.title || " ");
12444 d.close.setDisplayed(opt.closable !== false);
12445 activeTextEl = textboxEl;
12446 opt.prompt = opt.prompt || (opt.multiline ? true : false);
12451 textareaEl.setHeight(typeof opt.multiline == "number" ?
12452 opt.multiline : this.defaultTextHeight);
12453 activeTextEl = textareaEl;
12462 progressEl.setDisplayed(opt.progress === true);
12463 this.updateProgress(0);
12464 activeTextEl.dom.value = opt.value || "";
12466 dlg.setDefaultButton(activeTextEl);
12468 var bs = opt.buttons;
12471 db = buttons["ok"];
12472 }else if(bs && bs.yes){
12473 db = buttons["yes"];
12475 dlg.setDefaultButton(db);
12477 bwidth = updateButtons(opt.buttons);
12478 this.updateText(opt.msg);
12480 d.el.addClass(opt.cls);
12482 d.proxyDrag = opt.proxyDrag === true;
12483 d.modal = opt.modal !== false;
12484 d.mask = opt.modal !== false ? mask : false;
12485 if(!d.isVisible()){
12486 // force it to the end of the z-index stack so it gets a cursor in FF
12487 document.body.appendChild(dlg.el.dom);
12488 d.animateTarget = null;
12489 d.show(options.animEl);
12496 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
12497 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
12498 * and closing the message box when the process is complete.
12499 * @param {String} title The title bar text
12500 * @param {String} msg The message box body text
12501 * @return {Roo.MessageBox} This message box
12503 progress : function(title, msg){
12510 minWidth: this.minProgressWidth,
12517 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
12518 * If a callback function is passed it will be called after the user clicks the button, and the
12519 * id of the button that was clicked will be passed as the only parameter to the callback
12520 * (could also be the top-right close button).
12521 * @param {String} title The title bar text
12522 * @param {String} msg The message box body text
12523 * @param {Function} fn (optional) The callback function invoked after the message box is closed
12524 * @param {Object} scope (optional) The scope of the callback function
12525 * @return {Roo.MessageBox} This message box
12527 alert : function(title, msg, fn, scope){
12540 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
12541 * interaction while waiting for a long-running process to complete that does not have defined intervals.
12542 * You are responsible for closing the message box when the process is complete.
12543 * @param {String} msg The message box body text
12544 * @param {String} title (optional) The title bar text
12545 * @return {Roo.MessageBox} This message box
12547 wait : function(msg, title){
12558 waitTimer = Roo.TaskMgr.start({
12560 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
12568 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
12569 * If a callback function is passed it will be called after the user clicks either button, and the id of the
12570 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
12571 * @param {String} title The title bar text
12572 * @param {String} msg The message box body text
12573 * @param {Function} fn (optional) The callback function invoked after the message box is closed
12574 * @param {Object} scope (optional) The scope of the callback function
12575 * @return {Roo.MessageBox} This message box
12577 confirm : function(title, msg, fn, scope){
12581 buttons: this.YESNO,
12590 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
12591 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
12592 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
12593 * (could also be the top-right close button) and the text that was entered will be passed as the two
12594 * parameters to the callback.
12595 * @param {String} title The title bar text
12596 * @param {String} msg The message box body text
12597 * @param {Function} fn (optional) The callback function invoked after the message box is closed
12598 * @param {Object} scope (optional) The scope of the callback function
12599 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
12600 * property, or the height in pixels to create the textbox (defaults to false / single-line)
12601 * @return {Roo.MessageBox} This message box
12603 prompt : function(title, msg, fn, scope, multiline){
12607 buttons: this.OKCANCEL,
12612 multiline: multiline,
12619 * Button config that displays a single OK button
12624 * Button config that displays Yes and No buttons
12627 YESNO : {yes:true, no:true},
12629 * Button config that displays OK and Cancel buttons
12632 OKCANCEL : {ok:true, cancel:true},
12634 * Button config that displays Yes, No and Cancel buttons
12637 YESNOCANCEL : {yes:true, no:true, cancel:true},
12640 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
12643 defaultTextHeight : 75,
12645 * The maximum width in pixels of the message box (defaults to 600)
12650 * The minimum width in pixels of the message box (defaults to 100)
12655 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
12656 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
12659 minProgressWidth : 250,
12661 * An object containing the default button text strings that can be overriden for localized language support.
12662 * Supported properties are: ok, cancel, yes and no.
12663 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
12676 * Shorthand for {@link Roo.MessageBox}
12678 Roo.Msg = Roo.MessageBox;/*
12680 * Ext JS Library 1.1.1
12681 * Copyright(c) 2006-2007, Ext JS, LLC.
12683 * Originally Released Under LGPL - original licence link has changed is not relivant.
12686 * <script type="text/javascript">
12689 * @class Roo.QuickTips
12690 * Provides attractive and customizable tooltips for any element.
12693 Roo.QuickTips = function(){
12694 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
12695 var ce, bd, xy, dd;
12696 var visible = false, disabled = true, inited = false;
12697 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
12699 var onOver = function(e){
12703 var t = e.getTarget();
12704 if(!t || t.nodeType !== 1 || t == document || t == document.body){
12707 if(ce && t == ce.el){
12708 clearTimeout(hideProc);
12711 if(t && tagEls[t.id]){
12712 tagEls[t.id].el = t;
12713 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
12716 var ttp, et = Roo.fly(t);
12717 var ns = cfg.namespace;
12718 if(tm.interceptTitles && t.title){
12721 t.removeAttribute("title");
12722 e.preventDefault();
12724 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
12727 showProc = show.defer(tm.showDelay, tm, [{
12729 text: ttp.replace(/\\n/g,'<br/>'),
12730 width: et.getAttributeNS(ns, cfg.width),
12731 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
12732 title: et.getAttributeNS(ns, cfg.title),
12733 cls: et.getAttributeNS(ns, cfg.cls)
12738 var onOut = function(e){
12739 clearTimeout(showProc);
12740 var t = e.getTarget();
12741 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
12742 hideProc = setTimeout(hide, tm.hideDelay);
12746 var onMove = function(e){
12752 if(tm.trackMouse && ce){
12757 var onDown = function(e){
12758 clearTimeout(showProc);
12759 clearTimeout(hideProc);
12761 if(tm.hideOnClick){
12764 tm.enable.defer(100, tm);
12769 var getPad = function(){
12770 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
12773 var show = function(o){
12777 clearTimeout(dismissProc);
12779 if(removeCls){ // in case manually hidden
12780 el.removeClass(removeCls);
12784 el.addClass(ce.cls);
12785 removeCls = ce.cls;
12788 tipTitle.update(ce.title);
12791 tipTitle.update('');
12794 el.dom.style.width = tm.maxWidth+'px';
12795 //tipBody.dom.style.width = '';
12796 tipBodyText.update(o.text);
12797 var p = getPad(), w = ce.width;
12799 var td = tipBodyText.dom;
12800 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
12801 if(aw > tm.maxWidth){
12803 }else if(aw < tm.minWidth){
12809 //tipBody.setWidth(w);
12810 el.setWidth(parseInt(w, 10) + p);
12811 if(ce.autoHide === false){
12812 close.setDisplayed(true);
12817 close.setDisplayed(false);
12823 el.avoidY = xy[1]-18;
12828 el.setStyle("visibility", "visible");
12829 el.fadeIn({callback: afterShow});
12835 var afterShow = function(){
12839 if(tm.autoDismiss && ce.autoHide !== false){
12840 dismissProc = setTimeout(hide, tm.autoDismissDelay);
12845 var hide = function(noanim){
12846 clearTimeout(dismissProc);
12847 clearTimeout(hideProc);
12849 if(el.isVisible()){
12851 if(noanim !== true && tm.animate){
12852 el.fadeOut({callback: afterHide});
12859 var afterHide = function(){
12862 el.removeClass(removeCls);
12869 * @cfg {Number} minWidth
12870 * The minimum width of the quick tip (defaults to 40)
12874 * @cfg {Number} maxWidth
12875 * The maximum width of the quick tip (defaults to 300)
12879 * @cfg {Boolean} interceptTitles
12880 * True to automatically use the element's DOM title value if available (defaults to false)
12882 interceptTitles : false,
12884 * @cfg {Boolean} trackMouse
12885 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
12887 trackMouse : false,
12889 * @cfg {Boolean} hideOnClick
12890 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
12892 hideOnClick : true,
12894 * @cfg {Number} showDelay
12895 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
12899 * @cfg {Number} hideDelay
12900 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
12904 * @cfg {Boolean} autoHide
12905 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
12906 * Used in conjunction with hideDelay.
12911 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
12912 * (defaults to true). Used in conjunction with autoDismissDelay.
12914 autoDismiss : true,
12917 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
12919 autoDismissDelay : 5000,
12921 * @cfg {Boolean} animate
12922 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
12927 * @cfg {String} title
12928 * Title text to display (defaults to ''). This can be any valid HTML markup.
12932 * @cfg {String} text
12933 * Body text to display (defaults to ''). This can be any valid HTML markup.
12937 * @cfg {String} cls
12938 * A CSS class to apply to the base quick tip element (defaults to '').
12942 * @cfg {Number} width
12943 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
12944 * minWidth or maxWidth.
12949 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
12950 * or display QuickTips in a page.
12953 tm = Roo.QuickTips;
12954 cfg = tm.tagConfig;
12956 if(!Roo.isReady){ // allow calling of init() before onReady
12957 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
12960 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
12961 el.fxDefaults = {stopFx: true};
12962 // maximum custom styling
12963 //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>');
12964 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>');
12965 tipTitle = el.child('h3');
12966 tipTitle.enableDisplayMode("block");
12967 tipBody = el.child('div.x-tip-bd');
12968 tipBodyText = el.child('div.x-tip-bd-inner');
12969 //bdLeft = el.child('div.x-tip-bd-left');
12970 //bdRight = el.child('div.x-tip-bd-right');
12971 close = el.child('div.x-tip-close');
12972 close.enableDisplayMode("block");
12973 close.on("click", hide);
12974 var d = Roo.get(document);
12975 d.on("mousedown", onDown);
12976 d.on("mouseover", onOver);
12977 d.on("mouseout", onOut);
12978 d.on("mousemove", onMove);
12979 esc = d.addKeyListener(27, hide);
12982 dd = el.initDD("default", null, {
12983 onDrag : function(){
12987 dd.setHandleElId(tipTitle.id);
12996 * Configures a new quick tip instance and assigns it to a target element. The following config options
12999 Property Type Description
13000 ---------- --------------------- ------------------------------------------------------------------------
13001 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
13003 * @param {Object} config The config object
13005 register : function(config){
13006 var cs = config instanceof Array ? config : arguments;
13007 for(var i = 0, len = cs.length; i < len; i++) {
13009 var target = c.target;
13011 if(target instanceof Array){
13012 for(var j = 0, jlen = target.length; j < jlen; j++){
13013 tagEls[target[j]] = c;
13016 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
13023 * Removes this quick tip from its element and destroys it.
13024 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
13026 unregister : function(el){
13027 delete tagEls[Roo.id(el)];
13031 * Enable this quick tip.
13033 enable : function(){
13034 if(inited && disabled){
13036 if(locks.length < 1){
13043 * Disable this quick tip.
13045 disable : function(){
13047 clearTimeout(showProc);
13048 clearTimeout(hideProc);
13049 clearTimeout(dismissProc);
13057 * Returns true if the quick tip is enabled, else false.
13059 isEnabled : function(){
13065 namespace : "roo", // was ext?? this may break..
13066 alt_namespace : "ext",
13067 attribute : "qtip",
13077 // backwards compat
13078 Roo.QuickTips.tips = Roo.QuickTips.register;/*
13080 * Ext JS Library 1.1.1
13081 * Copyright(c) 2006-2007, Ext JS, LLC.
13083 * Originally Released Under LGPL - original licence link has changed is not relivant.
13086 * <script type="text/javascript">
13091 * @class Roo.tree.TreePanel
13092 * @extends Roo.data.Tree
13093 * @cfg {Roo.tree.TreeNode} root The root node
13094 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
13095 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
13096 * @cfg {Boolean} enableDD true to enable drag and drop
13097 * @cfg {Boolean} enableDrag true to enable just drag
13098 * @cfg {Boolean} enableDrop true to enable just drop
13099 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
13100 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
13101 * @cfg {String} ddGroup The DD group this TreePanel belongs to
13102 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
13103 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
13104 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
13105 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
13106 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
13107 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
13108 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
13109 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
13110 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
13111 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
13112 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
13113 * @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>
13114 * @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>
13117 * @param {String/HTMLElement/Element} el The container element
13118 * @param {Object} config
13120 Roo.tree.TreePanel = function(el, config){
13122 var loader = false;
13124 root = config.root;
13125 delete config.root;
13127 if (config.loader) {
13128 loader = config.loader;
13129 delete config.loader;
13132 Roo.apply(this, config);
13133 Roo.tree.TreePanel.superclass.constructor.call(this);
13134 this.el = Roo.get(el);
13135 this.el.addClass('x-tree');
13136 //console.log(root);
13138 this.setRootNode( Roo.factory(root, Roo.tree));
13141 this.loader = Roo.factory(loader, Roo.tree);
13144 * Read-only. The id of the container element becomes this TreePanel's id.
13146 this.id = this.el.id;
13149 * @event beforeload
13150 * Fires before a node is loaded, return false to cancel
13151 * @param {Node} node The node being loaded
13153 "beforeload" : true,
13156 * Fires when a node is loaded
13157 * @param {Node} node The node that was loaded
13161 * @event textchange
13162 * Fires when the text for a node is changed
13163 * @param {Node} node The node
13164 * @param {String} text The new text
13165 * @param {String} oldText The old text
13167 "textchange" : true,
13169 * @event beforeexpand
13170 * Fires before a node is expanded, return false to cancel.
13171 * @param {Node} node The node
13172 * @param {Boolean} deep
13173 * @param {Boolean} anim
13175 "beforeexpand" : true,
13177 * @event beforecollapse
13178 * Fires before a node is collapsed, return false to cancel.
13179 * @param {Node} node The node
13180 * @param {Boolean} deep
13181 * @param {Boolean} anim
13183 "beforecollapse" : true,
13186 * Fires when a node is expanded
13187 * @param {Node} node The node
13191 * @event disabledchange
13192 * Fires when the disabled status of a node changes
13193 * @param {Node} node The node
13194 * @param {Boolean} disabled
13196 "disabledchange" : true,
13199 * Fires when a node is collapsed
13200 * @param {Node} node The node
13204 * @event beforeclick
13205 * Fires before click processing on a node. Return false to cancel the default action.
13206 * @param {Node} node The node
13207 * @param {Roo.EventObject} e The event object
13209 "beforeclick":true,
13211 * @event checkchange
13212 * Fires when a node with a checkbox's checked property changes
13213 * @param {Node} this This node
13214 * @param {Boolean} checked
13216 "checkchange":true,
13219 * Fires when a node is clicked
13220 * @param {Node} node The node
13221 * @param {Roo.EventObject} e The event object
13226 * Fires when a node is double clicked
13227 * @param {Node} node The node
13228 * @param {Roo.EventObject} e The event object
13232 * @event contextmenu
13233 * Fires when a node is right clicked
13234 * @param {Node} node The node
13235 * @param {Roo.EventObject} e The event object
13237 "contextmenu":true,
13239 * @event beforechildrenrendered
13240 * Fires right before the child nodes for a node are rendered
13241 * @param {Node} node The node
13243 "beforechildrenrendered":true,
13246 * Fires when a node starts being dragged
13247 * @param {Roo.tree.TreePanel} this
13248 * @param {Roo.tree.TreeNode} node
13249 * @param {event} e The raw browser event
13251 "startdrag" : true,
13254 * Fires when a drag operation is complete
13255 * @param {Roo.tree.TreePanel} this
13256 * @param {Roo.tree.TreeNode} node
13257 * @param {event} e The raw browser event
13262 * Fires when a dragged node is dropped on a valid DD target
13263 * @param {Roo.tree.TreePanel} this
13264 * @param {Roo.tree.TreeNode} node
13265 * @param {DD} dd The dd it was dropped on
13266 * @param {event} e The raw browser event
13270 * @event beforenodedrop
13271 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
13272 * passed to handlers has the following properties:<br />
13273 * <ul style="padding:5px;padding-left:16px;">
13274 * <li>tree - The TreePanel</li>
13275 * <li>target - The node being targeted for the drop</li>
13276 * <li>data - The drag data from the drag source</li>
13277 * <li>point - The point of the drop - append, above or below</li>
13278 * <li>source - The drag source</li>
13279 * <li>rawEvent - Raw mouse event</li>
13280 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
13281 * to be inserted by setting them on this object.</li>
13282 * <li>cancel - Set this to true to cancel the drop.</li>
13284 * @param {Object} dropEvent
13286 "beforenodedrop" : true,
13289 * Fires after a DD object is dropped on a node in this tree. The dropEvent
13290 * passed to handlers has the following properties:<br />
13291 * <ul style="padding:5px;padding-left:16px;">
13292 * <li>tree - The TreePanel</li>
13293 * <li>target - The node being targeted for the drop</li>
13294 * <li>data - The drag data from the drag source</li>
13295 * <li>point - The point of the drop - append, above or below</li>
13296 * <li>source - The drag source</li>
13297 * <li>rawEvent - Raw mouse event</li>
13298 * <li>dropNode - Dropped node(s).</li>
13300 * @param {Object} dropEvent
13304 * @event nodedragover
13305 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
13306 * passed to handlers has the following properties:<br />
13307 * <ul style="padding:5px;padding-left:16px;">
13308 * <li>tree - The TreePanel</li>
13309 * <li>target - The node being targeted for the drop</li>
13310 * <li>data - The drag data from the drag source</li>
13311 * <li>point - The point of the drop - append, above or below</li>
13312 * <li>source - The drag source</li>
13313 * <li>rawEvent - Raw mouse event</li>
13314 * <li>dropNode - Drop node(s) provided by the source.</li>
13315 * <li>cancel - Set this to true to signal drop not allowed.</li>
13317 * @param {Object} dragOverEvent
13319 "nodedragover" : true,
13321 * @event appendnode
13322 * Fires when append node to the tree
13323 * @param {Roo.tree.TreePanel} this
13324 * @param {Roo.tree.TreeNode} node
13325 * @param {Number} index The index of the newly appended node
13327 "appendnode" : true
13330 if(this.singleExpand){
13331 this.on("beforeexpand", this.restrictExpand, this);
13334 this.editor.tree = this;
13335 this.editor = Roo.factory(this.editor, Roo.tree);
13338 if (this.selModel) {
13339 this.selModel = Roo.factory(this.selModel, Roo.tree);
13343 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
13344 rootVisible : true,
13345 animate: Roo.enableFx,
13348 hlDrop : Roo.enableFx,
13352 rendererTip: false,
13354 restrictExpand : function(node){
13355 var p = node.parentNode;
13357 if(p.expandedChild && p.expandedChild.parentNode == p){
13358 p.expandedChild.collapse();
13360 p.expandedChild = node;
13364 // private override
13365 setRootNode : function(node){
13366 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
13367 if(!this.rootVisible){
13368 node.ui = new Roo.tree.RootTreeNodeUI(node);
13374 * Returns the container element for this TreePanel
13376 getEl : function(){
13381 * Returns the default TreeLoader for this TreePanel
13383 getLoader : function(){
13384 return this.loader;
13390 expandAll : function(){
13391 this.root.expand(true);
13395 * Collapse all nodes
13397 collapseAll : function(){
13398 this.root.collapse(true);
13402 * Returns the selection model used by this TreePanel
13404 getSelectionModel : function(){
13405 if(!this.selModel){
13406 this.selModel = new Roo.tree.DefaultSelectionModel();
13408 return this.selModel;
13412 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
13413 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
13414 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
13417 getChecked : function(a, startNode){
13418 startNode = startNode || this.root;
13420 var f = function(){
13421 if(this.attributes.checked){
13422 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
13425 startNode.cascade(f);
13430 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
13431 * @param {String} path
13432 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
13433 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
13434 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
13436 expandPath : function(path, attr, callback){
13437 attr = attr || "id";
13438 var keys = path.split(this.pathSeparator);
13439 var curNode = this.root;
13440 if(curNode.attributes[attr] != keys[1]){ // invalid root
13442 callback(false, null);
13447 var f = function(){
13448 if(++index == keys.length){
13450 callback(true, curNode);
13454 var c = curNode.findChild(attr, keys[index]);
13457 callback(false, curNode);
13462 c.expand(false, false, f);
13464 curNode.expand(false, false, f);
13468 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
13469 * @param {String} path
13470 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
13471 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
13472 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
13474 selectPath : function(path, attr, callback){
13475 attr = attr || "id";
13476 var keys = path.split(this.pathSeparator);
13477 var v = keys.pop();
13478 if(keys.length > 0){
13479 var f = function(success, node){
13480 if(success && node){
13481 var n = node.findChild(attr, v);
13487 }else if(callback){
13488 callback(false, n);
13492 callback(false, n);
13496 this.expandPath(keys.join(this.pathSeparator), attr, f);
13498 this.root.select();
13500 callback(true, this.root);
13505 getTreeEl : function(){
13510 * Trigger rendering of this TreePanel
13512 render : function(){
13513 if (this.innerCt) {
13514 return this; // stop it rendering more than once!!
13517 this.innerCt = this.el.createChild({tag:"ul",
13518 cls:"x-tree-root-ct " +
13519 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
13521 if(this.containerScroll){
13522 Roo.dd.ScrollManager.register(this.el);
13524 if((this.enableDD || this.enableDrop) && !this.dropZone){
13526 * The dropZone used by this tree if drop is enabled
13527 * @type Roo.tree.TreeDropZone
13529 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
13530 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
13533 if((this.enableDD || this.enableDrag) && !this.dragZone){
13535 * The dragZone used by this tree if drag is enabled
13536 * @type Roo.tree.TreeDragZone
13538 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
13539 ddGroup: this.ddGroup || "TreeDD",
13540 scroll: this.ddScroll
13543 this.getSelectionModel().init(this);
13545 Roo.log("ROOT not set in tree");
13548 this.root.render();
13549 if(!this.rootVisible){
13550 this.root.renderChildren();
13556 * Ext JS Library 1.1.1
13557 * Copyright(c) 2006-2007, Ext JS, LLC.
13559 * Originally Released Under LGPL - original licence link has changed is not relivant.
13562 * <script type="text/javascript">
13567 * @class Roo.tree.DefaultSelectionModel
13568 * @extends Roo.util.Observable
13569 * The default single selection for a TreePanel.
13570 * @param {Object} cfg Configuration
13572 Roo.tree.DefaultSelectionModel = function(cfg){
13573 this.selNode = null;
13579 * @event selectionchange
13580 * Fires when the selected node changes
13581 * @param {DefaultSelectionModel} this
13582 * @param {TreeNode} node the new selection
13584 "selectionchange" : true,
13587 * @event beforeselect
13588 * Fires before the selected node changes, return false to cancel the change
13589 * @param {DefaultSelectionModel} this
13590 * @param {TreeNode} node the new selection
13591 * @param {TreeNode} node the old selection
13593 "beforeselect" : true
13596 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
13599 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
13600 init : function(tree){
13602 tree.getTreeEl().on("keydown", this.onKeyDown, this);
13603 tree.on("click", this.onNodeClick, this);
13606 onNodeClick : function(node, e){
13607 if (e.ctrlKey && this.selNode == node) {
13608 this.unselect(node);
13616 * @param {TreeNode} node The node to select
13617 * @return {TreeNode} The selected node
13619 select : function(node){
13620 var last = this.selNode;
13621 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
13623 last.ui.onSelectedChange(false);
13625 this.selNode = node;
13626 node.ui.onSelectedChange(true);
13627 this.fireEvent("selectionchange", this, node, last);
13634 * @param {TreeNode} node The node to unselect
13636 unselect : function(node){
13637 if(this.selNode == node){
13638 this.clearSelections();
13643 * Clear all selections
13645 clearSelections : function(){
13646 var n = this.selNode;
13648 n.ui.onSelectedChange(false);
13649 this.selNode = null;
13650 this.fireEvent("selectionchange", this, null);
13656 * Get the selected node
13657 * @return {TreeNode} The selected node
13659 getSelectedNode : function(){
13660 return this.selNode;
13664 * Returns true if the node is selected
13665 * @param {TreeNode} node The node to check
13666 * @return {Boolean}
13668 isSelected : function(node){
13669 return this.selNode == node;
13673 * Selects the node above the selected node in the tree, intelligently walking the nodes
13674 * @return TreeNode The new selection
13676 selectPrevious : function(){
13677 var s = this.selNode || this.lastSelNode;
13681 var ps = s.previousSibling;
13683 if(!ps.isExpanded() || ps.childNodes.length < 1){
13684 return this.select(ps);
13686 var lc = ps.lastChild;
13687 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
13690 return this.select(lc);
13692 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
13693 return this.select(s.parentNode);
13699 * Selects the node above the selected node in the tree, intelligently walking the nodes
13700 * @return TreeNode The new selection
13702 selectNext : function(){
13703 var s = this.selNode || this.lastSelNode;
13707 if(s.firstChild && s.isExpanded()){
13708 return this.select(s.firstChild);
13709 }else if(s.nextSibling){
13710 return this.select(s.nextSibling);
13711 }else if(s.parentNode){
13713 s.parentNode.bubble(function(){
13714 if(this.nextSibling){
13715 newS = this.getOwnerTree().selModel.select(this.nextSibling);
13724 onKeyDown : function(e){
13725 var s = this.selNode || this.lastSelNode;
13726 // undesirable, but required
13731 var k = e.getKey();
13739 this.selectPrevious();
13742 e.preventDefault();
13743 if(s.hasChildNodes()){
13744 if(!s.isExpanded()){
13746 }else if(s.firstChild){
13747 this.select(s.firstChild, e);
13752 e.preventDefault();
13753 if(s.hasChildNodes() && s.isExpanded()){
13755 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
13756 this.select(s.parentNode, e);
13764 * @class Roo.tree.MultiSelectionModel
13765 * @extends Roo.util.Observable
13766 * Multi selection for a TreePanel.
13767 * @param {Object} cfg Configuration
13769 Roo.tree.MultiSelectionModel = function(){
13770 this.selNodes = [];
13774 * @event selectionchange
13775 * Fires when the selected nodes change
13776 * @param {MultiSelectionModel} this
13777 * @param {Array} nodes Array of the selected nodes
13779 "selectionchange" : true
13781 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
13785 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
13786 init : function(tree){
13788 tree.getTreeEl().on("keydown", this.onKeyDown, this);
13789 tree.on("click", this.onNodeClick, this);
13792 onNodeClick : function(node, e){
13793 this.select(node, e, e.ctrlKey);
13798 * @param {TreeNode} node The node to select
13799 * @param {EventObject} e (optional) An event associated with the selection
13800 * @param {Boolean} keepExisting True to retain existing selections
13801 * @return {TreeNode} The selected node
13803 select : function(node, e, keepExisting){
13804 if(keepExisting !== true){
13805 this.clearSelections(true);
13807 if(this.isSelected(node)){
13808 this.lastSelNode = node;
13811 this.selNodes.push(node);
13812 this.selMap[node.id] = node;
13813 this.lastSelNode = node;
13814 node.ui.onSelectedChange(true);
13815 this.fireEvent("selectionchange", this, this.selNodes);
13821 * @param {TreeNode} node The node to unselect
13823 unselect : function(node){
13824 if(this.selMap[node.id]){
13825 node.ui.onSelectedChange(false);
13826 var sn = this.selNodes;
13829 index = sn.indexOf(node);
13831 for(var i = 0, len = sn.length; i < len; i++){
13839 this.selNodes.splice(index, 1);
13841 delete this.selMap[node.id];
13842 this.fireEvent("selectionchange", this, this.selNodes);
13847 * Clear all selections
13849 clearSelections : function(suppressEvent){
13850 var sn = this.selNodes;
13852 for(var i = 0, len = sn.length; i < len; i++){
13853 sn[i].ui.onSelectedChange(false);
13855 this.selNodes = [];
13857 if(suppressEvent !== true){
13858 this.fireEvent("selectionchange", this, this.selNodes);
13864 * Returns true if the node is selected
13865 * @param {TreeNode} node The node to check
13866 * @return {Boolean}
13868 isSelected : function(node){
13869 return this.selMap[node.id] ? true : false;
13873 * Returns an array of the selected nodes
13876 getSelectedNodes : function(){
13877 return this.selNodes;
13880 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
13882 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
13884 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
13887 * Ext JS Library 1.1.1
13888 * Copyright(c) 2006-2007, Ext JS, LLC.
13890 * Originally Released Under LGPL - original licence link has changed is not relivant.
13893 * <script type="text/javascript">
13897 * @class Roo.tree.TreeNode
13898 * @extends Roo.data.Node
13899 * @cfg {String} text The text for this node
13900 * @cfg {Boolean} expanded true to start the node expanded
13901 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
13902 * @cfg {Boolean} allowDrop false if this node cannot be drop on
13903 * @cfg {Boolean} disabled true to start the node disabled
13904 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
13905 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
13906 * @cfg {String} cls A css class to be added to the node
13907 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
13908 * @cfg {String} href URL of the link used for the node (defaults to #)
13909 * @cfg {String} hrefTarget target frame for the link
13910 * @cfg {String} qtip An Ext QuickTip for the node
13911 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
13912 * @cfg {Boolean} singleClickExpand True for single click expand on this node
13913 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
13914 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
13915 * (defaults to undefined with no checkbox rendered)
13917 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
13919 Roo.tree.TreeNode = function(attributes){
13920 attributes = attributes || {};
13921 if(typeof attributes == "string"){
13922 attributes = {text: attributes};
13924 this.childrenRendered = false;
13925 this.rendered = false;
13926 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
13927 this.expanded = attributes.expanded === true;
13928 this.isTarget = attributes.isTarget !== false;
13929 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
13930 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
13933 * Read-only. The text for this node. To change it use setText().
13936 this.text = attributes.text;
13938 * True if this node is disabled.
13941 this.disabled = attributes.disabled === true;
13945 * @event textchange
13946 * Fires when the text for this node is changed
13947 * @param {Node} this This node
13948 * @param {String} text The new text
13949 * @param {String} oldText The old text
13951 "textchange" : true,
13953 * @event beforeexpand
13954 * Fires before this node is expanded, return false to cancel.
13955 * @param {Node} this This node
13956 * @param {Boolean} deep
13957 * @param {Boolean} anim
13959 "beforeexpand" : true,
13961 * @event beforecollapse
13962 * Fires before this node is collapsed, return false to cancel.
13963 * @param {Node} this This node
13964 * @param {Boolean} deep
13965 * @param {Boolean} anim
13967 "beforecollapse" : true,
13970 * Fires when this node is expanded
13971 * @param {Node} this This node
13975 * @event disabledchange
13976 * Fires when the disabled status of this node changes
13977 * @param {Node} this This node
13978 * @param {Boolean} disabled
13980 "disabledchange" : true,
13983 * Fires when this node is collapsed
13984 * @param {Node} this This node
13988 * @event beforeclick
13989 * Fires before click processing. Return false to cancel the default action.
13990 * @param {Node} this This node
13991 * @param {Roo.EventObject} e The event object
13993 "beforeclick":true,
13995 * @event checkchange
13996 * Fires when a node with a checkbox's checked property changes
13997 * @param {Node} this This node
13998 * @param {Boolean} checked
14000 "checkchange":true,
14003 * Fires when this node is clicked
14004 * @param {Node} this This node
14005 * @param {Roo.EventObject} e The event object
14010 * Fires when this node is double clicked
14011 * @param {Node} this This node
14012 * @param {Roo.EventObject} e The event object
14016 * @event contextmenu
14017 * Fires when this node is right clicked
14018 * @param {Node} this This node
14019 * @param {Roo.EventObject} e The event object
14021 "contextmenu":true,
14023 * @event beforechildrenrendered
14024 * Fires right before the child nodes for this node are rendered
14025 * @param {Node} this This node
14027 "beforechildrenrendered":true
14030 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
14033 * Read-only. The UI for this node
14036 this.ui = new uiClass(this);
14038 // finally support items[]
14039 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
14044 Roo.each(this.attributes.items, function(c) {
14045 this.appendChild(Roo.factory(c,Roo.Tree));
14047 delete this.attributes.items;
14052 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
14053 preventHScroll: true,
14055 * Returns true if this node is expanded
14056 * @return {Boolean}
14058 isExpanded : function(){
14059 return this.expanded;
14063 * Returns the UI object for this node
14064 * @return {TreeNodeUI}
14066 getUI : function(){
14070 // private override
14071 setFirstChild : function(node){
14072 var of = this.firstChild;
14073 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
14074 if(this.childrenRendered && of && node != of){
14075 of.renderIndent(true, true);
14078 this.renderIndent(true, true);
14082 // private override
14083 setLastChild : function(node){
14084 var ol = this.lastChild;
14085 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
14086 if(this.childrenRendered && ol && node != ol){
14087 ol.renderIndent(true, true);
14090 this.renderIndent(true, true);
14094 // these methods are overridden to provide lazy rendering support
14095 // private override
14096 appendChild : function()
14098 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
14099 if(node && this.childrenRendered){
14102 this.ui.updateExpandIcon();
14106 // private override
14107 removeChild : function(node){
14108 this.ownerTree.getSelectionModel().unselect(node);
14109 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
14110 // if it's been rendered remove dom node
14111 if(this.childrenRendered){
14114 if(this.childNodes.length < 1){
14115 this.collapse(false, false);
14117 this.ui.updateExpandIcon();
14119 if(!this.firstChild) {
14120 this.childrenRendered = false;
14125 // private override
14126 insertBefore : function(node, refNode){
14127 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
14128 if(newNode && refNode && this.childrenRendered){
14131 this.ui.updateExpandIcon();
14136 * Sets the text for this node
14137 * @param {String} text
14139 setText : function(text){
14140 var oldText = this.text;
14142 this.attributes.text = text;
14143 if(this.rendered){ // event without subscribing
14144 this.ui.onTextChange(this, text, oldText);
14146 this.fireEvent("textchange", this, text, oldText);
14150 * Triggers selection of this node
14152 select : function(){
14153 this.getOwnerTree().getSelectionModel().select(this);
14157 * Triggers deselection of this node
14159 unselect : function(){
14160 this.getOwnerTree().getSelectionModel().unselect(this);
14164 * Returns true if this node is selected
14165 * @return {Boolean}
14167 isSelected : function(){
14168 return this.getOwnerTree().getSelectionModel().isSelected(this);
14172 * Expand this node.
14173 * @param {Boolean} deep (optional) True to expand all children as well
14174 * @param {Boolean} anim (optional) false to cancel the default animation
14175 * @param {Function} callback (optional) A callback to be called when
14176 * expanding this node completes (does not wait for deep expand to complete).
14177 * Called with 1 parameter, this node.
14179 expand : function(deep, anim, callback){
14180 if(!this.expanded){
14181 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
14184 if(!this.childrenRendered){
14185 this.renderChildren();
14187 this.expanded = true;
14189 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
14190 this.ui.animExpand(function(){
14191 this.fireEvent("expand", this);
14192 if(typeof callback == "function"){
14196 this.expandChildNodes(true);
14198 }.createDelegate(this));
14202 this.fireEvent("expand", this);
14203 if(typeof callback == "function"){
14208 if(typeof callback == "function"){
14213 this.expandChildNodes(true);
14217 isHiddenRoot : function(){
14218 return this.isRoot && !this.getOwnerTree().rootVisible;
14222 * Collapse this node.
14223 * @param {Boolean} deep (optional) True to collapse all children as well
14224 * @param {Boolean} anim (optional) false to cancel the default animation
14226 collapse : function(deep, anim){
14227 if(this.expanded && !this.isHiddenRoot()){
14228 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
14231 this.expanded = false;
14232 if((this.getOwnerTree().animate && anim !== false) || anim){
14233 this.ui.animCollapse(function(){
14234 this.fireEvent("collapse", this);
14236 this.collapseChildNodes(true);
14238 }.createDelegate(this));
14241 this.ui.collapse();
14242 this.fireEvent("collapse", this);
14246 var cs = this.childNodes;
14247 for(var i = 0, len = cs.length; i < len; i++) {
14248 cs[i].collapse(true, false);
14254 delayedExpand : function(delay){
14255 if(!this.expandProcId){
14256 this.expandProcId = this.expand.defer(delay, this);
14261 cancelExpand : function(){
14262 if(this.expandProcId){
14263 clearTimeout(this.expandProcId);
14265 this.expandProcId = false;
14269 * Toggles expanded/collapsed state of the node
14271 toggle : function(){
14280 * Ensures all parent nodes are expanded
14282 ensureVisible : function(callback){
14283 var tree = this.getOwnerTree();
14284 tree.expandPath(this.parentNode.getPath(), false, function(){
14285 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
14286 Roo.callback(callback);
14287 }.createDelegate(this));
14291 * Expand all child nodes
14292 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
14294 expandChildNodes : function(deep){
14295 var cs = this.childNodes;
14296 for(var i = 0, len = cs.length; i < len; i++) {
14297 cs[i].expand(deep);
14302 * Collapse all child nodes
14303 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
14305 collapseChildNodes : function(deep){
14306 var cs = this.childNodes;
14307 for(var i = 0, len = cs.length; i < len; i++) {
14308 cs[i].collapse(deep);
14313 * Disables this node
14315 disable : function(){
14316 this.disabled = true;
14318 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
14319 this.ui.onDisableChange(this, true);
14321 this.fireEvent("disabledchange", this, true);
14325 * Enables this node
14327 enable : function(){
14328 this.disabled = false;
14329 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
14330 this.ui.onDisableChange(this, false);
14332 this.fireEvent("disabledchange", this, false);
14336 renderChildren : function(suppressEvent){
14337 if(suppressEvent !== false){
14338 this.fireEvent("beforechildrenrendered", this);
14340 var cs = this.childNodes;
14341 for(var i = 0, len = cs.length; i < len; i++){
14342 cs[i].render(true);
14344 this.childrenRendered = true;
14348 sort : function(fn, scope){
14349 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
14350 if(this.childrenRendered){
14351 var cs = this.childNodes;
14352 for(var i = 0, len = cs.length; i < len; i++){
14353 cs[i].render(true);
14359 render : function(bulkRender){
14360 this.ui.render(bulkRender);
14361 if(!this.rendered){
14362 this.rendered = true;
14364 this.expanded = false;
14365 this.expand(false, false);
14371 renderIndent : function(deep, refresh){
14373 this.ui.childIndent = null;
14375 this.ui.renderIndent();
14376 if(deep === true && this.childrenRendered){
14377 var cs = this.childNodes;
14378 for(var i = 0, len = cs.length; i < len; i++){
14379 cs[i].renderIndent(true, refresh);
14385 * Ext JS Library 1.1.1
14386 * Copyright(c) 2006-2007, Ext JS, LLC.
14388 * Originally Released Under LGPL - original licence link has changed is not relivant.
14391 * <script type="text/javascript">
14395 * @class Roo.tree.AsyncTreeNode
14396 * @extends Roo.tree.TreeNode
14397 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
14399 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
14401 Roo.tree.AsyncTreeNode = function(config){
14402 this.loaded = false;
14403 this.loading = false;
14404 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
14406 * @event beforeload
14407 * Fires before this node is loaded, return false to cancel
14408 * @param {Node} this This node
14410 this.addEvents({'beforeload':true, 'load': true});
14413 * Fires when this node is loaded
14414 * @param {Node} this This node
14417 * The loader used by this node (defaults to using the tree's defined loader)
14422 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
14423 expand : function(deep, anim, callback){
14424 if(this.loading){ // if an async load is already running, waiting til it's done
14426 var f = function(){
14427 if(!this.loading){ // done loading
14428 clearInterval(timer);
14429 this.expand(deep, anim, callback);
14431 }.createDelegate(this);
14432 timer = setInterval(f, 200);
14436 if(this.fireEvent("beforeload", this) === false){
14439 this.loading = true;
14440 this.ui.beforeLoad(this);
14441 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
14443 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
14447 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
14451 * Returns true if this node is currently loading
14452 * @return {Boolean}
14454 isLoading : function(){
14455 return this.loading;
14458 loadComplete : function(deep, anim, callback){
14459 this.loading = false;
14460 this.loaded = true;
14461 this.ui.afterLoad(this);
14462 this.fireEvent("load", this);
14463 this.expand(deep, anim, callback);
14467 * Returns true if this node has been loaded
14468 * @return {Boolean}
14470 isLoaded : function(){
14471 return this.loaded;
14474 hasChildNodes : function(){
14475 if(!this.isLeaf() && !this.loaded){
14478 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
14483 * Trigger a reload for this node
14484 * @param {Function} callback
14486 reload : function(callback){
14487 this.collapse(false, false);
14488 while(this.firstChild){
14489 this.removeChild(this.firstChild);
14491 this.childrenRendered = false;
14492 this.loaded = false;
14493 if(this.isHiddenRoot()){
14494 this.expanded = false;
14496 this.expand(false, false, callback);
14500 * Ext JS Library 1.1.1
14501 * Copyright(c) 2006-2007, Ext JS, LLC.
14503 * Originally Released Under LGPL - original licence link has changed is not relivant.
14506 * <script type="text/javascript">
14510 * @class Roo.tree.TreeNodeUI
14512 * @param {Object} node The node to render
14513 * The TreeNode UI implementation is separate from the
14514 * tree implementation. Unless you are customizing the tree UI,
14515 * you should never have to use this directly.
14517 Roo.tree.TreeNodeUI = function(node){
14519 this.rendered = false;
14520 this.animating = false;
14521 this.emptyIcon = Roo.BLANK_IMAGE_URL;
14524 Roo.tree.TreeNodeUI.prototype = {
14525 removeChild : function(node){
14527 this.ctNode.removeChild(node.ui.getEl());
14531 beforeLoad : function(){
14532 this.addClass("x-tree-node-loading");
14535 afterLoad : function(){
14536 this.removeClass("x-tree-node-loading");
14539 onTextChange : function(node, text, oldText){
14541 this.textNode.innerHTML = text;
14545 onDisableChange : function(node, state){
14546 this.disabled = state;
14548 this.addClass("x-tree-node-disabled");
14550 this.removeClass("x-tree-node-disabled");
14554 onSelectedChange : function(state){
14557 this.addClass("x-tree-selected");
14560 this.removeClass("x-tree-selected");
14564 onMove : function(tree, node, oldParent, newParent, index, refNode){
14565 this.childIndent = null;
14567 var targetNode = newParent.ui.getContainer();
14568 if(!targetNode){//target not rendered
14569 this.holder = document.createElement("div");
14570 this.holder.appendChild(this.wrap);
14573 var insertBefore = refNode ? refNode.ui.getEl() : null;
14575 targetNode.insertBefore(this.wrap, insertBefore);
14577 targetNode.appendChild(this.wrap);
14579 this.node.renderIndent(true);
14583 addClass : function(cls){
14585 Roo.fly(this.elNode).addClass(cls);
14589 removeClass : function(cls){
14591 Roo.fly(this.elNode).removeClass(cls);
14595 remove : function(){
14597 this.holder = document.createElement("div");
14598 this.holder.appendChild(this.wrap);
14602 fireEvent : function(){
14603 return this.node.fireEvent.apply(this.node, arguments);
14606 initEvents : function(){
14607 this.node.on("move", this.onMove, this);
14608 var E = Roo.EventManager;
14609 var a = this.anchor;
14611 var el = Roo.fly(a, '_treeui');
14613 if(Roo.isOpera){ // opera render bug ignores the CSS
14614 el.setStyle("text-decoration", "none");
14617 el.on("click", this.onClick, this);
14618 el.on("dblclick", this.onDblClick, this);
14621 Roo.EventManager.on(this.checkbox,
14622 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
14625 el.on("contextmenu", this.onContextMenu, this);
14627 var icon = Roo.fly(this.iconNode);
14628 icon.on("click", this.onClick, this);
14629 icon.on("dblclick", this.onDblClick, this);
14630 icon.on("contextmenu", this.onContextMenu, this);
14631 E.on(this.ecNode, "click", this.ecClick, this, true);
14633 if(this.node.disabled){
14634 this.addClass("x-tree-node-disabled");
14636 if(this.node.hidden){
14637 this.addClass("x-tree-node-disabled");
14639 var ot = this.node.getOwnerTree();
14640 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
14641 if(dd && (!this.node.isRoot || ot.rootVisible)){
14642 Roo.dd.Registry.register(this.elNode, {
14644 handles: this.getDDHandles(),
14650 getDDHandles : function(){
14651 return [this.iconNode, this.textNode];
14656 this.wrap.style.display = "none";
14662 this.wrap.style.display = "";
14666 onContextMenu : function(e){
14667 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
14668 e.preventDefault();
14670 this.fireEvent("contextmenu", this.node, e);
14674 onClick : function(e){
14679 if(this.fireEvent("beforeclick", this.node, e) !== false){
14680 if(!this.disabled && this.node.attributes.href){
14681 this.fireEvent("click", this.node, e);
14684 e.preventDefault();
14689 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
14690 this.node.toggle();
14693 this.fireEvent("click", this.node, e);
14699 onDblClick : function(e){
14700 e.preventDefault();
14705 this.toggleCheck();
14707 if(!this.animating && this.node.hasChildNodes()){
14708 this.node.toggle();
14710 this.fireEvent("dblclick", this.node, e);
14713 onCheckChange : function(){
14714 var checked = this.checkbox.checked;
14715 this.node.attributes.checked = checked;
14716 this.fireEvent('checkchange', this.node, checked);
14719 ecClick : function(e){
14720 if(!this.animating && this.node.hasChildNodes()){
14721 this.node.toggle();
14725 startDrop : function(){
14726 this.dropping = true;
14729 // delayed drop so the click event doesn't get fired on a drop
14730 endDrop : function(){
14731 setTimeout(function(){
14732 this.dropping = false;
14733 }.createDelegate(this), 50);
14736 expand : function(){
14737 this.updateExpandIcon();
14738 this.ctNode.style.display = "";
14741 focus : function(){
14742 if(!this.node.preventHScroll){
14743 try{this.anchor.focus();
14745 }else if(!Roo.isIE){
14747 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
14748 var l = noscroll.scrollLeft;
14749 this.anchor.focus();
14750 noscroll.scrollLeft = l;
14755 toggleCheck : function(value){
14756 var cb = this.checkbox;
14758 cb.checked = (value === undefined ? !cb.checked : value);
14764 this.anchor.blur();
14768 animExpand : function(callback){
14769 var ct = Roo.get(this.ctNode);
14771 if(!this.node.hasChildNodes()){
14772 this.updateExpandIcon();
14773 this.ctNode.style.display = "";
14774 Roo.callback(callback);
14777 this.animating = true;
14778 this.updateExpandIcon();
14781 callback : function(){
14782 this.animating = false;
14783 Roo.callback(callback);
14786 duration: this.node.ownerTree.duration || .25
14790 highlight : function(){
14791 var tree = this.node.getOwnerTree();
14792 Roo.fly(this.wrap).highlight(
14793 tree.hlColor || "C3DAF9",
14794 {endColor: tree.hlBaseColor}
14798 collapse : function(){
14799 this.updateExpandIcon();
14800 this.ctNode.style.display = "none";
14803 animCollapse : function(callback){
14804 var ct = Roo.get(this.ctNode);
14805 ct.enableDisplayMode('block');
14808 this.animating = true;
14809 this.updateExpandIcon();
14812 callback : function(){
14813 this.animating = false;
14814 Roo.callback(callback);
14817 duration: this.node.ownerTree.duration || .25
14821 getContainer : function(){
14822 return this.ctNode;
14825 getEl : function(){
14829 appendDDGhost : function(ghostNode){
14830 ghostNode.appendChild(this.elNode.cloneNode(true));
14833 getDDRepairXY : function(){
14834 return Roo.lib.Dom.getXY(this.iconNode);
14837 onRender : function(){
14841 render : function(bulkRender){
14842 var n = this.node, a = n.attributes;
14843 var targetNode = n.parentNode ?
14844 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
14846 if(!this.rendered){
14847 this.rendered = true;
14849 this.renderElements(n, a, targetNode, bulkRender);
14852 if(this.textNode.setAttributeNS){
14853 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
14855 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
14858 this.textNode.setAttribute("ext:qtip", a.qtip);
14860 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
14863 }else if(a.qtipCfg){
14864 a.qtipCfg.target = Roo.id(this.textNode);
14865 Roo.QuickTips.register(a.qtipCfg);
14868 if(!this.node.expanded){
14869 this.updateExpandIcon();
14872 if(bulkRender === true) {
14873 targetNode.appendChild(this.wrap);
14878 renderElements : function(n, a, targetNode, bulkRender)
14880 // add some indent caching, this helps performance when rendering a large tree
14881 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14882 var t = n.getOwnerTree();
14883 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
14884 if (typeof(n.attributes.html) != 'undefined') {
14885 txt = n.attributes.html;
14887 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
14888 var cb = typeof a.checked == 'boolean';
14889 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14890 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
14891 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
14892 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
14893 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
14894 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
14895 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
14896 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
14897 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
14898 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14901 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14902 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14903 n.nextSibling.ui.getEl(), buf.join(""));
14905 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14908 this.elNode = this.wrap.childNodes[0];
14909 this.ctNode = this.wrap.childNodes[1];
14910 var cs = this.elNode.childNodes;
14911 this.indentNode = cs[0];
14912 this.ecNode = cs[1];
14913 this.iconNode = cs[2];
14916 this.checkbox = cs[3];
14919 this.anchor = cs[index];
14920 this.textNode = cs[index].firstChild;
14923 getAnchor : function(){
14924 return this.anchor;
14927 getTextEl : function(){
14928 return this.textNode;
14931 getIconEl : function(){
14932 return this.iconNode;
14935 isChecked : function(){
14936 return this.checkbox ? this.checkbox.checked : false;
14939 updateExpandIcon : function(){
14941 var n = this.node, c1, c2;
14942 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
14943 var hasChild = n.hasChildNodes();
14947 c1 = "x-tree-node-collapsed";
14948 c2 = "x-tree-node-expanded";
14951 c1 = "x-tree-node-expanded";
14952 c2 = "x-tree-node-collapsed";
14955 this.removeClass("x-tree-node-leaf");
14956 this.wasLeaf = false;
14958 if(this.c1 != c1 || this.c2 != c2){
14959 Roo.fly(this.elNode).replaceClass(c1, c2);
14960 this.c1 = c1; this.c2 = c2;
14963 // this changes non-leafs into leafs if they have no children.
14964 // it's not very rational behaviour..
14966 if(!this.wasLeaf && this.node.leaf){
14967 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
14970 this.wasLeaf = true;
14973 var ecc = "x-tree-ec-icon "+cls;
14974 if(this.ecc != ecc){
14975 this.ecNode.className = ecc;
14981 getChildIndent : function(){
14982 if(!this.childIndent){
14986 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
14988 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
14990 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
14995 this.childIndent = buf.join("");
14997 return this.childIndent;
15000 renderIndent : function(){
15003 var p = this.node.parentNode;
15005 indent = p.ui.getChildIndent();
15007 if(this.indentMarkup != indent){ // don't rerender if not required
15008 this.indentNode.innerHTML = indent;
15009 this.indentMarkup = indent;
15011 this.updateExpandIcon();
15016 Roo.tree.RootTreeNodeUI = function(){
15017 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
15019 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
15020 render : function(){
15021 if(!this.rendered){
15022 var targetNode = this.node.ownerTree.innerCt.dom;
15023 this.node.expanded = true;
15024 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
15025 this.wrap = this.ctNode = targetNode.firstChild;
15028 collapse : function(){
15030 expand : function(){
15034 * Ext JS Library 1.1.1
15035 * Copyright(c) 2006-2007, Ext JS, LLC.
15037 * Originally Released Under LGPL - original licence link has changed is not relivant.
15040 * <script type="text/javascript">
15043 * @class Roo.tree.TreeLoader
15044 * @extends Roo.util.Observable
15045 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
15046 * nodes from a specified URL. The response must be a javascript Array definition
15047 * who's elements are node definition objects. eg:
15052 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
15053 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
15060 * The old style respose with just an array is still supported, but not recommended.
15063 * A server request is sent, and child nodes are loaded only when a node is expanded.
15064 * The loading node's id is passed to the server under the parameter name "node" to
15065 * enable the server to produce the correct child nodes.
15067 * To pass extra parameters, an event handler may be attached to the "beforeload"
15068 * event, and the parameters specified in the TreeLoader's baseParams property:
15070 myTreeLoader.on("beforeload", function(treeLoader, node) {
15071 this.baseParams.category = node.attributes.category;
15076 * This would pass an HTTP parameter called "category" to the server containing
15077 * the value of the Node's "category" attribute.
15079 * Creates a new Treeloader.
15080 * @param {Object} config A config object containing config properties.
15082 Roo.tree.TreeLoader = function(config){
15083 this.baseParams = {};
15084 this.requestMethod = "POST";
15085 Roo.apply(this, config);
15090 * @event beforeload
15091 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
15092 * @param {Object} This TreeLoader object.
15093 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
15094 * @param {Object} callback The callback function specified in the {@link #load} call.
15099 * Fires when the node has been successfuly loaded.
15100 * @param {Object} This TreeLoader object.
15101 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
15102 * @param {Object} response The response object containing the data from the server.
15106 * @event loadexception
15107 * Fires if the network request failed.
15108 * @param {Object} This TreeLoader object.
15109 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
15110 * @param {Object} response The response object containing the data from the server.
15112 loadexception : true,
15115 * Fires before a node is created, enabling you to return custom Node types
15116 * @param {Object} This TreeLoader object.
15117 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
15122 Roo.tree.TreeLoader.superclass.constructor.call(this);
15125 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
15127 * @cfg {String} dataUrl The URL from which to request a Json string which
15128 * specifies an array of node definition object representing the child nodes
15132 * @cfg {String} requestMethod either GET or POST
15133 * defaults to POST (due to BC)
15137 * @cfg {Object} baseParams (optional) An object containing properties which
15138 * specify HTTP parameters to be passed to each request for child nodes.
15141 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
15142 * created by this loader. If the attributes sent by the server have an attribute in this object,
15143 * they take priority.
15146 * @cfg {Object} uiProviders (optional) An object containing properties which
15148 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
15149 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
15150 * <i>uiProvider</i> attribute of a returned child node is a string rather
15151 * than a reference to a TreeNodeUI implementation, this that string value
15152 * is used as a property name in the uiProviders object. You can define the provider named
15153 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
15158 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
15159 * child nodes before loading.
15161 clearOnLoad : true,
15164 * @cfg {String} root (optional) Default to false. Use this to read data from an object
15165 * property on loading, rather than expecting an array. (eg. more compatible to a standard
15166 * Grid query { data : [ .....] }
15171 * @cfg {String} queryParam (optional)
15172 * Name of the query as it will be passed on the querystring (defaults to 'node')
15173 * eg. the request will be ?node=[id]
15180 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
15181 * This is called automatically when a node is expanded, but may be used to reload
15182 * a node (or append new children if the {@link #clearOnLoad} option is false.)
15183 * @param {Roo.tree.TreeNode} node
15184 * @param {Function} callback
15186 load : function(node, callback){
15187 if(this.clearOnLoad){
15188 while(node.firstChild){
15189 node.removeChild(node.firstChild);
15192 if(node.attributes.children){ // preloaded json children
15193 var cs = node.attributes.children;
15194 for(var i = 0, len = cs.length; i < len; i++){
15195 node.appendChild(this.createNode(cs[i]));
15197 if(typeof callback == "function"){
15200 }else if(this.dataUrl){
15201 this.requestData(node, callback);
15205 getParams: function(node){
15206 var buf = [], bp = this.baseParams;
15207 for(var key in bp){
15208 if(typeof bp[key] != "function"){
15209 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
15212 var n = this.queryParam === false ? 'node' : this.queryParam;
15213 buf.push(n + "=", encodeURIComponent(node.id));
15214 return buf.join("");
15217 requestData : function(node, callback){
15218 if(this.fireEvent("beforeload", this, node, callback) !== false){
15219 this.transId = Roo.Ajax.request({
15220 method:this.requestMethod,
15221 url: this.dataUrl||this.url,
15222 success: this.handleResponse,
15223 failure: this.handleFailure,
15225 argument: {callback: callback, node: node},
15226 params: this.getParams(node)
15229 // if the load is cancelled, make sure we notify
15230 // the node that we are done
15231 if(typeof callback == "function"){
15237 isLoading : function(){
15238 return this.transId ? true : false;
15241 abort : function(){
15242 if(this.isLoading()){
15243 Roo.Ajax.abort(this.transId);
15248 createNode : function(attr)
15250 // apply baseAttrs, nice idea Corey!
15251 if(this.baseAttrs){
15252 Roo.applyIf(attr, this.baseAttrs);
15254 if(this.applyLoader !== false){
15255 attr.loader = this;
15257 // uiProvider = depreciated..
15259 if(typeof(attr.uiProvider) == 'string'){
15260 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
15261 /** eval:var:attr */ eval(attr.uiProvider);
15263 if(typeof(this.uiProviders['default']) != 'undefined') {
15264 attr.uiProvider = this.uiProviders['default'];
15267 this.fireEvent('create', this, attr);
15269 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
15271 new Roo.tree.TreeNode(attr) :
15272 new Roo.tree.AsyncTreeNode(attr));
15275 processResponse : function(response, node, callback)
15277 var json = response.responseText;
15280 var o = Roo.decode(json);
15282 if (this.root === false && typeof(o.success) != undefined) {
15283 this.root = 'data'; // the default behaviour for list like data..
15286 if (this.root !== false && !o.success) {
15287 // it's a failure condition.
15288 var a = response.argument;
15289 this.fireEvent("loadexception", this, a.node, response);
15290 Roo.log("Load failed - should have a handler really");
15296 if (this.root !== false) {
15300 for(var i = 0, len = o.length; i < len; i++){
15301 var n = this.createNode(o[i]);
15303 node.appendChild(n);
15306 if(typeof callback == "function"){
15307 callback(this, node);
15310 this.handleFailure(response);
15314 handleResponse : function(response){
15315 this.transId = false;
15316 var a = response.argument;
15317 this.processResponse(response, a.node, a.callback);
15318 this.fireEvent("load", this, a.node, response);
15321 handleFailure : function(response)
15323 // should handle failure better..
15324 this.transId = false;
15325 var a = response.argument;
15326 this.fireEvent("loadexception", this, a.node, response);
15327 if(typeof a.callback == "function"){
15328 a.callback(this, a.node);
15333 * Ext JS Library 1.1.1
15334 * Copyright(c) 2006-2007, Ext JS, LLC.
15336 * Originally Released Under LGPL - original licence link has changed is not relivant.
15339 * <script type="text/javascript">
15343 * @class Roo.tree.TreeFilter
15344 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
15345 * @param {TreePanel} tree
15346 * @param {Object} config (optional)
15348 Roo.tree.TreeFilter = function(tree, config){
15350 this.filtered = {};
15351 Roo.apply(this, config);
15354 Roo.tree.TreeFilter.prototype = {
15361 * Filter the data by a specific attribute.
15362 * @param {String/RegExp} value Either string that the attribute value
15363 * should start with or a RegExp to test against the attribute
15364 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
15365 * @param {TreeNode} startNode (optional) The node to start the filter at.
15367 filter : function(value, attr, startNode){
15368 attr = attr || "text";
15370 if(typeof value == "string"){
15371 var vlen = value.length;
15372 // auto clear empty filter
15373 if(vlen == 0 && this.clearBlank){
15377 value = value.toLowerCase();
15379 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
15381 }else if(value.exec){ // regex?
15383 return value.test(n.attributes[attr]);
15386 throw 'Illegal filter type, must be string or regex';
15388 this.filterBy(f, null, startNode);
15392 * Filter by a function. The passed function will be called with each
15393 * node in the tree (or from the startNode). If the function returns true, the node is kept
15394 * otherwise it is filtered. If a node is filtered, its children are also filtered.
15395 * @param {Function} fn The filter function
15396 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
15398 filterBy : function(fn, scope, startNode){
15399 startNode = startNode || this.tree.root;
15400 if(this.autoClear){
15403 var af = this.filtered, rv = this.reverse;
15404 var f = function(n){
15405 if(n == startNode){
15411 var m = fn.call(scope || n, n);
15419 startNode.cascade(f);
15422 if(typeof id != "function"){
15424 if(n && n.parentNode){
15425 n.parentNode.removeChild(n);
15433 * Clears the current filter. Note: with the "remove" option
15434 * set a filter cannot be cleared.
15436 clear : function(){
15438 var af = this.filtered;
15440 if(typeof id != "function"){
15447 this.filtered = {};
15452 * Ext JS Library 1.1.1
15453 * Copyright(c) 2006-2007, Ext JS, LLC.
15455 * Originally Released Under LGPL - original licence link has changed is not relivant.
15458 * <script type="text/javascript">
15463 * @class Roo.tree.TreeSorter
15464 * Provides sorting of nodes in a TreePanel
15466 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
15467 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
15468 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
15469 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
15470 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
15471 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
15473 * @param {TreePanel} tree
15474 * @param {Object} config
15476 Roo.tree.TreeSorter = function(tree, config){
15477 Roo.apply(this, config);
15478 tree.on("beforechildrenrendered", this.doSort, this);
15479 tree.on("append", this.updateSort, this);
15480 tree.on("insert", this.updateSort, this);
15482 var dsc = this.dir && this.dir.toLowerCase() == "desc";
15483 var p = this.property || "text";
15484 var sortType = this.sortType;
15485 var fs = this.folderSort;
15486 var cs = this.caseSensitive === true;
15487 var leafAttr = this.leafAttr || 'leaf';
15489 this.sortFn = function(n1, n2){
15491 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
15494 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
15498 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
15499 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
15501 return dsc ? +1 : -1;
15503 return dsc ? -1 : +1;
15510 Roo.tree.TreeSorter.prototype = {
15511 doSort : function(node){
15512 node.sort(this.sortFn);
15515 compareNodes : function(n1, n2){
15516 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
15519 updateSort : function(tree, node){
15520 if(node.childrenRendered){
15521 this.doSort.defer(1, this, [node]);
15526 * Ext JS Library 1.1.1
15527 * Copyright(c) 2006-2007, Ext JS, LLC.
15529 * Originally Released Under LGPL - original licence link has changed is not relivant.
15532 * <script type="text/javascript">
15535 if(Roo.dd.DropZone){
15537 Roo.tree.TreeDropZone = function(tree, config){
15538 this.allowParentInsert = false;
15539 this.allowContainerDrop = false;
15540 this.appendOnly = false;
15541 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
15543 this.lastInsertClass = "x-tree-no-status";
15544 this.dragOverData = {};
15547 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
15548 ddGroup : "TreeDD",
15551 expandDelay : 1000,
15553 expandNode : function(node){
15554 if(node.hasChildNodes() && !node.isExpanded()){
15555 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
15559 queueExpand : function(node){
15560 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
15563 cancelExpand : function(){
15564 if(this.expandProcId){
15565 clearTimeout(this.expandProcId);
15566 this.expandProcId = false;
15570 isValidDropPoint : function(n, pt, dd, e, data){
15571 if(!n || !data){ return false; }
15572 var targetNode = n.node;
15573 var dropNode = data.node;
15574 // default drop rules
15575 if(!(targetNode && targetNode.isTarget && pt)){
15578 if(pt == "append" && targetNode.allowChildren === false){
15581 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
15584 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
15587 // reuse the object
15588 var overEvent = this.dragOverData;
15589 overEvent.tree = this.tree;
15590 overEvent.target = targetNode;
15591 overEvent.data = data;
15592 overEvent.point = pt;
15593 overEvent.source = dd;
15594 overEvent.rawEvent = e;
15595 overEvent.dropNode = dropNode;
15596 overEvent.cancel = false;
15597 var result = this.tree.fireEvent("nodedragover", overEvent);
15598 return overEvent.cancel === false && result !== false;
15601 getDropPoint : function(e, n, dd)
15605 return tn.allowChildren !== false ? "append" : false; // always append for root
15607 var dragEl = n.ddel;
15608 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
15609 var y = Roo.lib.Event.getPageY(e);
15610 //var noAppend = tn.allowChildren === false || tn.isLeaf();
15612 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
15613 var noAppend = tn.allowChildren === false;
15614 if(this.appendOnly || tn.parentNode.allowChildren === false){
15615 return noAppend ? false : "append";
15617 var noBelow = false;
15618 if(!this.allowParentInsert){
15619 noBelow = tn.hasChildNodes() && tn.isExpanded();
15621 var q = (b - t) / (noAppend ? 2 : 3);
15622 if(y >= t && y < (t + q)){
15624 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
15631 onNodeEnter : function(n, dd, e, data)
15633 this.cancelExpand();
15636 onNodeOver : function(n, dd, e, data)
15639 var pt = this.getDropPoint(e, n, dd);
15642 // auto node expand check
15643 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
15644 this.queueExpand(node);
15645 }else if(pt != "append"){
15646 this.cancelExpand();
15649 // set the insert point style on the target node
15650 var returnCls = this.dropNotAllowed;
15651 if(this.isValidDropPoint(n, pt, dd, e, data)){
15656 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
15657 cls = "x-tree-drag-insert-above";
15658 }else if(pt == "below"){
15659 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
15660 cls = "x-tree-drag-insert-below";
15662 returnCls = "x-tree-drop-ok-append";
15663 cls = "x-tree-drag-append";
15665 if(this.lastInsertClass != cls){
15666 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
15667 this.lastInsertClass = cls;
15674 onNodeOut : function(n, dd, e, data){
15676 this.cancelExpand();
15677 this.removeDropIndicators(n);
15680 onNodeDrop : function(n, dd, e, data){
15681 var point = this.getDropPoint(e, n, dd);
15682 var targetNode = n.node;
15683 targetNode.ui.startDrop();
15684 if(!this.isValidDropPoint(n, point, dd, e, data)){
15685 targetNode.ui.endDrop();
15688 // first try to find the drop node
15689 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
15692 target: targetNode,
15697 dropNode: dropNode,
15700 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
15701 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
15702 targetNode.ui.endDrop();
15705 // allow target changing
15706 targetNode = dropEvent.target;
15707 if(point == "append" && !targetNode.isExpanded()){
15708 targetNode.expand(false, null, function(){
15709 this.completeDrop(dropEvent);
15710 }.createDelegate(this));
15712 this.completeDrop(dropEvent);
15717 completeDrop : function(de){
15718 var ns = de.dropNode, p = de.point, t = de.target;
15719 if(!(ns instanceof Array)){
15723 for(var i = 0, len = ns.length; i < len; i++){
15726 t.parentNode.insertBefore(n, t);
15727 }else if(p == "below"){
15728 t.parentNode.insertBefore(n, t.nextSibling);
15734 if(this.tree.hlDrop){
15738 this.tree.fireEvent("nodedrop", de);
15741 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
15742 if(this.tree.hlDrop){
15743 dropNode.ui.focus();
15744 dropNode.ui.highlight();
15746 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
15749 getTree : function(){
15753 removeDropIndicators : function(n){
15756 Roo.fly(el).removeClass([
15757 "x-tree-drag-insert-above",
15758 "x-tree-drag-insert-below",
15759 "x-tree-drag-append"]);
15760 this.lastInsertClass = "_noclass";
15764 beforeDragDrop : function(target, e, id){
15765 this.cancelExpand();
15769 afterRepair : function(data){
15770 if(data && Roo.enableFx){
15771 data.node.ui.highlight();
15781 * Ext JS Library 1.1.1
15782 * Copyright(c) 2006-2007, Ext JS, LLC.
15784 * Originally Released Under LGPL - original licence link has changed is not relivant.
15787 * <script type="text/javascript">
15791 if(Roo.dd.DragZone){
15792 Roo.tree.TreeDragZone = function(tree, config){
15793 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
15797 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
15798 ddGroup : "TreeDD",
15800 onBeforeDrag : function(data, e){
15802 return n && n.draggable && !n.disabled;
15806 onInitDrag : function(e){
15807 var data = this.dragData;
15808 this.tree.getSelectionModel().select(data.node);
15809 this.proxy.update("");
15810 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
15811 this.tree.fireEvent("startdrag", this.tree, data.node, e);
15814 getRepairXY : function(e, data){
15815 return data.node.ui.getDDRepairXY();
15818 onEndDrag : function(data, e){
15819 this.tree.fireEvent("enddrag", this.tree, data.node, e);
15824 onValidDrop : function(dd, e, id){
15825 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
15829 beforeInvalidDrop : function(e, id){
15830 // this scrolls the original position back into view
15831 var sm = this.tree.getSelectionModel();
15832 sm.clearSelections();
15833 sm.select(this.dragData.node);
15838 * Ext JS Library 1.1.1
15839 * Copyright(c) 2006-2007, Ext JS, LLC.
15841 * Originally Released Under LGPL - original licence link has changed is not relivant.
15844 * <script type="text/javascript">
15847 * @class Roo.tree.TreeEditor
15848 * @extends Roo.Editor
15849 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
15850 * as the editor field.
15852 * @param {Object} config (used to be the tree panel.)
15853 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
15855 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
15856 * @cfg {Roo.form.TextField} field [required] The field configuration
15860 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
15863 if (oldconfig) { // old style..
15864 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
15867 tree = config.tree;
15868 config.field = config.field || {};
15869 config.field.xtype = 'TextField';
15870 field = Roo.factory(config.field, Roo.form);
15872 config = config || {};
15877 * @event beforenodeedit
15878 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
15879 * false from the handler of this event.
15880 * @param {Editor} this
15881 * @param {Roo.tree.Node} node
15883 "beforenodeedit" : true
15887 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
15891 tree.on('beforeclick', this.beforeNodeClick, this);
15892 tree.getTreeEl().on('mousedown', this.hide, this);
15893 this.on('complete', this.updateNode, this);
15894 this.on('beforestartedit', this.fitToTree, this);
15895 this.on('startedit', this.bindScroll, this, {delay:10});
15896 this.on('specialkey', this.onSpecialKey, this);
15899 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
15901 * @cfg {String} alignment
15902 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
15908 * @cfg {Boolean} hideEl
15909 * True to hide the bound element while the editor is displayed (defaults to false)
15913 * @cfg {String} cls
15914 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
15916 cls: "x-small-editor x-tree-editor",
15918 * @cfg {Boolean} shim
15919 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
15925 * @cfg {Number} maxWidth
15926 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
15927 * the containing tree element's size, it will be automatically limited for you to the container width, taking
15928 * scroll and client offsets into account prior to each edit.
15935 fitToTree : function(ed, el){
15936 var td = this.tree.getTreeEl().dom, nd = el.dom;
15937 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
15938 td.scrollLeft = nd.offsetLeft;
15942 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
15943 this.setSize(w, '');
15945 return this.fireEvent('beforenodeedit', this, this.editNode);
15950 triggerEdit : function(node){
15951 this.completeEdit();
15952 this.editNode = node;
15953 this.startEdit(node.ui.textNode, node.text);
15957 bindScroll : function(){
15958 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
15962 beforeNodeClick : function(node, e){
15963 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
15964 this.lastClick = new Date();
15965 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
15967 this.triggerEdit(node);
15974 updateNode : function(ed, value){
15975 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
15976 this.editNode.setText(value);
15980 onHide : function(){
15981 Roo.tree.TreeEditor.superclass.onHide.call(this);
15983 this.editNode.ui.focus();
15988 onSpecialKey : function(field, e){
15989 var k = e.getKey();
15993 }else if(k == e.ENTER && !e.hasModifier()){
15995 this.completeEdit();
15998 });//<Script type="text/javascript">
16001 * Ext JS Library 1.1.1
16002 * Copyright(c) 2006-2007, Ext JS, LLC.
16004 * Originally Released Under LGPL - original licence link has changed is not relivant.
16007 * <script type="text/javascript">
16011 * Not documented??? - probably should be...
16014 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
16015 //focus: Roo.emptyFn, // prevent odd scrolling behavior
16017 renderElements : function(n, a, targetNode, bulkRender){
16018 //consel.log("renderElements?");
16019 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
16021 var t = n.getOwnerTree();
16022 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
16024 var cols = t.columns;
16025 var bw = t.borderWidth;
16027 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
16028 var cb = typeof a.checked == "boolean";
16029 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
16030 var colcls = 'x-t-' + tid + '-c0';
16032 '<li class="x-tree-node">',
16035 '<div class="x-tree-node-el ', a.cls,'">',
16037 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
16040 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
16041 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
16042 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
16043 (a.icon ? ' x-tree-node-inline-icon' : ''),
16044 (a.iconCls ? ' '+a.iconCls : ''),
16045 '" unselectable="on" />',
16046 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
16047 (a.checked ? 'checked="checked" />' : ' />')) : ''),
16049 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
16050 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
16051 '<span unselectable="on" qtip="' + tx + '">',
16055 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
16056 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
16058 for(var i = 1, len = cols.length; i < len; i++){
16060 colcls = 'x-t-' + tid + '-c' +i;
16061 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
16062 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
16063 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
16069 '<div class="x-clear"></div></div>',
16070 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
16073 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
16074 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
16075 n.nextSibling.ui.getEl(), buf.join(""));
16077 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
16079 var el = this.wrap.firstChild;
16081 this.elNode = el.firstChild;
16082 this.ranchor = el.childNodes[1];
16083 this.ctNode = this.wrap.childNodes[1];
16084 var cs = el.firstChild.childNodes;
16085 this.indentNode = cs[0];
16086 this.ecNode = cs[1];
16087 this.iconNode = cs[2];
16090 this.checkbox = cs[3];
16093 this.anchor = cs[index];
16095 this.textNode = cs[index].firstChild;
16097 //el.on("click", this.onClick, this);
16098 //el.on("dblclick", this.onDblClick, this);
16101 // console.log(this);
16103 initEvents : function(){
16104 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
16107 var a = this.ranchor;
16109 var el = Roo.get(a);
16111 if(Roo.isOpera){ // opera render bug ignores the CSS
16112 el.setStyle("text-decoration", "none");
16115 el.on("click", this.onClick, this);
16116 el.on("dblclick", this.onDblClick, this);
16117 el.on("contextmenu", this.onContextMenu, this);
16121 /*onSelectedChange : function(state){
16124 this.addClass("x-tree-selected");
16127 this.removeClass("x-tree-selected");
16130 addClass : function(cls){
16132 Roo.fly(this.elRow).addClass(cls);
16138 removeClass : function(cls){
16140 Roo.fly(this.elRow).removeClass(cls);
16146 });//<Script type="text/javascript">
16150 * Ext JS Library 1.1.1
16151 * Copyright(c) 2006-2007, Ext JS, LLC.
16153 * Originally Released Under LGPL - original licence link has changed is not relivant.
16156 * <script type="text/javascript">
16161 * @class Roo.tree.ColumnTree
16162 * @extends Roo.tree.TreePanel
16163 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
16164 * @cfg {int} borderWidth compined right/left border allowance
16166 * @param {String/HTMLElement/Element} el The container element
16167 * @param {Object} config
16169 Roo.tree.ColumnTree = function(el, config)
16171 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
16175 * Fire this event on a container when it resizes
16176 * @param {int} w Width
16177 * @param {int} h Height
16181 this.on('resize', this.onResize, this);
16184 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
16188 borderWidth: Roo.isBorderBox ? 0 : 2,
16191 render : function(){
16192 // add the header.....
16194 Roo.tree.ColumnTree.superclass.render.apply(this);
16196 this.el.addClass('x-column-tree');
16198 this.headers = this.el.createChild(
16199 {cls:'x-tree-headers'},this.innerCt.dom);
16201 var cols = this.columns, c;
16202 var totalWidth = 0;
16204 var len = cols.length;
16205 for(var i = 0; i < len; i++){
16207 totalWidth += c.width;
16208 this.headEls.push(this.headers.createChild({
16209 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
16211 cls:'x-tree-hd-text',
16214 style:'width:'+(c.width-this.borderWidth)+'px;'
16217 this.headers.createChild({cls:'x-clear'});
16218 // prevent floats from wrapping when clipped
16219 this.headers.setWidth(totalWidth);
16220 //this.innerCt.setWidth(totalWidth);
16221 this.innerCt.setStyle({ overflow: 'auto' });
16222 this.onResize(this.width, this.height);
16226 onResize : function(w,h)
16231 this.innerCt.setWidth(this.width);
16232 this.innerCt.setHeight(this.height-20);
16235 var cols = this.columns, c;
16236 var totalWidth = 0;
16238 var len = cols.length;
16239 for(var i = 0; i < len; i++){
16241 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
16242 // it's the expander..
16243 expEl = this.headEls[i];
16246 totalWidth += c.width;
16250 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
16252 this.headers.setWidth(w-20);
16261 * Ext JS Library 1.1.1
16262 * Copyright(c) 2006-2007, Ext JS, LLC.
16264 * Originally Released Under LGPL - original licence link has changed is not relivant.
16267 * <script type="text/javascript">
16271 * @class Roo.menu.Menu
16272 * @extends Roo.util.Observable
16273 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
16274 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
16275 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
16277 * Creates a new Menu
16278 * @param {Object} config Configuration options
16280 Roo.menu.Menu = function(config){
16282 Roo.menu.Menu.superclass.constructor.call(this, config);
16284 this.id = this.id || Roo.id();
16287 * @event beforeshow
16288 * Fires before this menu is displayed
16289 * @param {Roo.menu.Menu} this
16293 * @event beforehide
16294 * Fires before this menu is hidden
16295 * @param {Roo.menu.Menu} this
16300 * Fires after this menu is displayed
16301 * @param {Roo.menu.Menu} this
16306 * Fires after this menu is hidden
16307 * @param {Roo.menu.Menu} this
16312 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
16313 * @param {Roo.menu.Menu} this
16314 * @param {Roo.menu.Item} menuItem The menu item that was clicked
16315 * @param {Roo.EventObject} e
16320 * Fires when the mouse is hovering over this menu
16321 * @param {Roo.menu.Menu} this
16322 * @param {Roo.EventObject} e
16323 * @param {Roo.menu.Item} menuItem The menu item that was clicked
16328 * Fires when the mouse exits this menu
16329 * @param {Roo.menu.Menu} this
16330 * @param {Roo.EventObject} e
16331 * @param {Roo.menu.Item} menuItem The menu item that was clicked
16336 * Fires when a menu item contained in this menu is clicked
16337 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
16338 * @param {Roo.EventObject} e
16342 if (this.registerMenu) {
16343 Roo.menu.MenuMgr.register(this);
16346 var mis = this.items;
16347 this.items = new Roo.util.MixedCollection();
16349 this.add.apply(this, mis);
16353 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
16355 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
16359 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
16360 * for bottom-right shadow (defaults to "sides")
16364 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
16365 * this menu (defaults to "tl-tr?")
16367 subMenuAlign : "tl-tr?",
16369 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
16370 * relative to its element of origin (defaults to "tl-bl?")
16372 defaultAlign : "tl-bl?",
16374 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
16376 allowOtherMenus : false,
16378 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
16380 registerMenu : true,
16385 render : function(){
16389 var el = this.el = new Roo.Layer({
16391 shadow:this.shadow,
16393 parentEl: this.parentEl || document.body,
16397 this.keyNav = new Roo.menu.MenuNav(this);
16400 el.addClass("x-menu-plain");
16403 el.addClass(this.cls);
16405 // generic focus element
16406 this.focusEl = el.createChild({
16407 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
16409 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
16410 //disabling touch- as it's causing issues ..
16411 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
16412 ul.on('click' , this.onClick, this);
16415 ul.on("mouseover", this.onMouseOver, this);
16416 ul.on("mouseout", this.onMouseOut, this);
16417 this.items.each(function(item){
16422 var li = document.createElement("li");
16423 li.className = "x-menu-list-item";
16424 ul.dom.appendChild(li);
16425 item.render(li, this);
16432 autoWidth : function(){
16433 var el = this.el, ul = this.ul;
16437 var w = this.width;
16440 }else if(Roo.isIE){
16441 el.setWidth(this.minWidth);
16442 var t = el.dom.offsetWidth; // force recalc
16443 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
16448 delayAutoWidth : function(){
16451 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
16453 this.awTask.delay(20);
16458 findTargetItem : function(e){
16459 var t = e.getTarget(".x-menu-list-item", this.ul, true);
16460 if(t && t.menuItemId){
16461 return this.items.get(t.menuItemId);
16466 onClick : function(e){
16467 Roo.log("menu.onClick");
16468 var t = this.findTargetItem(e);
16473 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
16474 if(t == this.activeItem && t.shouldDeactivate(e)){
16475 this.activeItem.deactivate();
16476 delete this.activeItem;
16480 this.setActiveItem(t, true);
16488 this.fireEvent("click", this, t, e);
16492 setActiveItem : function(item, autoExpand){
16493 if(item != this.activeItem){
16494 if(this.activeItem){
16495 this.activeItem.deactivate();
16497 this.activeItem = item;
16498 item.activate(autoExpand);
16499 }else if(autoExpand){
16505 tryActivate : function(start, step){
16506 var items = this.items;
16507 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
16508 var item = items.get(i);
16509 if(!item.disabled && item.canActivate){
16510 this.setActiveItem(item, false);
16518 onMouseOver : function(e){
16520 if(t = this.findTargetItem(e)){
16521 if(t.canActivate && !t.disabled){
16522 this.setActiveItem(t, true);
16525 this.fireEvent("mouseover", this, e, t);
16529 onMouseOut : function(e){
16531 if(t = this.findTargetItem(e)){
16532 if(t == this.activeItem && t.shouldDeactivate(e)){
16533 this.activeItem.deactivate();
16534 delete this.activeItem;
16537 this.fireEvent("mouseout", this, e, t);
16541 * Read-only. Returns true if the menu is currently displayed, else false.
16544 isVisible : function(){
16545 return this.el && !this.hidden;
16549 * Displays this menu relative to another element
16550 * @param {String/HTMLElement/Roo.Element} element The element to align to
16551 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
16552 * the element (defaults to this.defaultAlign)
16553 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
16555 show : function(el, pos, parentMenu){
16556 this.parentMenu = parentMenu;
16560 this.fireEvent("beforeshow", this);
16561 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
16565 * Displays this menu at a specific xy position
16566 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
16567 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
16569 showAt : function(xy, parentMenu, /* private: */_e){
16570 this.parentMenu = parentMenu;
16575 this.fireEvent("beforeshow", this);
16576 xy = this.el.adjustForConstraints(xy);
16580 this.hidden = false;
16582 this.fireEvent("show", this);
16585 focus : function(){
16587 this.doFocus.defer(50, this);
16591 doFocus : function(){
16593 this.focusEl.focus();
16598 * Hides this menu and optionally all parent menus
16599 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
16601 hide : function(deep){
16602 if(this.el && this.isVisible()){
16603 this.fireEvent("beforehide", this);
16604 if(this.activeItem){
16605 this.activeItem.deactivate();
16606 this.activeItem = null;
16609 this.hidden = true;
16610 this.fireEvent("hide", this);
16612 if(deep === true && this.parentMenu){
16613 this.parentMenu.hide(true);
16618 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
16619 * Any of the following are valid:
16621 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
16622 * <li>An HTMLElement object which will be converted to a menu item</li>
16623 * <li>A menu item config object that will be created as a new menu item</li>
16624 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
16625 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
16630 var menu = new Roo.menu.Menu();
16632 // Create a menu item to add by reference
16633 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
16635 // Add a bunch of items at once using different methods.
16636 // Only the last item added will be returned.
16637 var item = menu.add(
16638 menuItem, // add existing item by ref
16639 'Dynamic Item', // new TextItem
16640 '-', // new separator
16641 { text: 'Config Item' } // new item by config
16644 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
16645 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
16648 var a = arguments, l = a.length, item;
16649 for(var i = 0; i < l; i++){
16651 if ((typeof(el) == "object") && el.xtype && el.xns) {
16652 el = Roo.factory(el, Roo.menu);
16655 if(el.render){ // some kind of Item
16656 item = this.addItem(el);
16657 }else if(typeof el == "string"){ // string
16658 if(el == "separator" || el == "-"){
16659 item = this.addSeparator();
16661 item = this.addText(el);
16663 }else if(el.tagName || el.el){ // element
16664 item = this.addElement(el);
16665 }else if(typeof el == "object"){ // must be menu item config?
16666 item = this.addMenuItem(el);
16673 * Returns this menu's underlying {@link Roo.Element} object
16674 * @return {Roo.Element} The element
16676 getEl : function(){
16684 * Adds a separator bar to the menu
16685 * @return {Roo.menu.Item} The menu item that was added
16687 addSeparator : function(){
16688 return this.addItem(new Roo.menu.Separator());
16692 * Adds an {@link Roo.Element} object to the menu
16693 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
16694 * @return {Roo.menu.Item} The menu item that was added
16696 addElement : function(el){
16697 return this.addItem(new Roo.menu.BaseItem(el));
16701 * Adds an existing object based on {@link Roo.menu.Item} to the menu
16702 * @param {Roo.menu.Item} item The menu item to add
16703 * @return {Roo.menu.Item} The menu item that was added
16705 addItem : function(item){
16706 this.items.add(item);
16708 var li = document.createElement("li");
16709 li.className = "x-menu-list-item";
16710 this.ul.dom.appendChild(li);
16711 item.render(li, this);
16712 this.delayAutoWidth();
16718 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
16719 * @param {Object} config A MenuItem config object
16720 * @return {Roo.menu.Item} The menu item that was added
16722 addMenuItem : function(config){
16723 if(!(config instanceof Roo.menu.Item)){
16724 if(typeof config.checked == "boolean"){ // must be check menu item config?
16725 config = new Roo.menu.CheckItem(config);
16727 config = new Roo.menu.Item(config);
16730 return this.addItem(config);
16734 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
16735 * @param {String} text The text to display in the menu item
16736 * @return {Roo.menu.Item} The menu item that was added
16738 addText : function(text){
16739 return this.addItem(new Roo.menu.TextItem({ text : text }));
16743 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
16744 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
16745 * @param {Roo.menu.Item} item The menu item to add
16746 * @return {Roo.menu.Item} The menu item that was added
16748 insert : function(index, item){
16749 this.items.insert(index, item);
16751 var li = document.createElement("li");
16752 li.className = "x-menu-list-item";
16753 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
16754 item.render(li, this);
16755 this.delayAutoWidth();
16761 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
16762 * @param {Roo.menu.Item} item The menu item to remove
16764 remove : function(item){
16765 this.items.removeKey(item.id);
16770 * Removes and destroys all items in the menu
16772 removeAll : function(){
16774 while(f = this.items.first()){
16780 // MenuNav is a private utility class used internally by the Menu
16781 Roo.menu.MenuNav = function(menu){
16782 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
16783 this.scope = this.menu = menu;
16786 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
16787 doRelay : function(e, h){
16788 var k = e.getKey();
16789 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
16790 this.menu.tryActivate(0, 1);
16793 return h.call(this.scope || this, e, this.menu);
16796 up : function(e, m){
16797 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
16798 m.tryActivate(m.items.length-1, -1);
16802 down : function(e, m){
16803 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
16804 m.tryActivate(0, 1);
16808 right : function(e, m){
16810 m.activeItem.expandMenu(true);
16814 left : function(e, m){
16816 if(m.parentMenu && m.parentMenu.activeItem){
16817 m.parentMenu.activeItem.activate();
16821 enter : function(e, m){
16823 e.stopPropagation();
16824 m.activeItem.onClick(e);
16825 m.fireEvent("click", this, m.activeItem);
16831 * Ext JS Library 1.1.1
16832 * Copyright(c) 2006-2007, Ext JS, LLC.
16834 * Originally Released Under LGPL - original licence link has changed is not relivant.
16837 * <script type="text/javascript">
16841 * @class Roo.menu.MenuMgr
16842 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
16845 Roo.menu.MenuMgr = function(){
16846 var menus, active, groups = {}, attached = false, lastShow = new Date();
16848 // private - called when first menu is created
16851 active = new Roo.util.MixedCollection();
16852 Roo.get(document).addKeyListener(27, function(){
16853 if(active.length > 0){
16860 function hideAll(){
16861 if(active && active.length > 0){
16862 var c = active.clone();
16863 c.each(function(m){
16870 function onHide(m){
16872 if(active.length < 1){
16873 Roo.get(document).un("mousedown", onMouseDown);
16879 function onShow(m){
16880 var last = active.last();
16881 lastShow = new Date();
16884 Roo.get(document).on("mousedown", onMouseDown);
16888 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
16889 m.parentMenu.activeChild = m;
16890 }else if(last && last.isVisible()){
16891 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
16896 function onBeforeHide(m){
16898 m.activeChild.hide();
16900 if(m.autoHideTimer){
16901 clearTimeout(m.autoHideTimer);
16902 delete m.autoHideTimer;
16907 function onBeforeShow(m){
16908 var pm = m.parentMenu;
16909 if(!pm && !m.allowOtherMenus){
16911 }else if(pm && pm.activeChild && active != m){
16912 pm.activeChild.hide();
16917 function onMouseDown(e){
16918 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
16924 function onBeforeCheck(mi, state){
16926 var g = groups[mi.group];
16927 for(var i = 0, l = g.length; i < l; i++){
16929 g[i].setChecked(false);
16938 * Hides all menus that are currently visible
16940 hideAll : function(){
16945 register : function(menu){
16949 menus[menu.id] = menu;
16950 menu.on("beforehide", onBeforeHide);
16951 menu.on("hide", onHide);
16952 menu.on("beforeshow", onBeforeShow);
16953 menu.on("show", onShow);
16954 var g = menu.group;
16955 if(g && menu.events["checkchange"]){
16959 groups[g].push(menu);
16960 menu.on("checkchange", onCheck);
16965 * Returns a {@link Roo.menu.Menu} object
16966 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
16967 * be used to generate and return a new Menu instance.
16969 get : function(menu){
16970 if(typeof menu == "string"){ // menu id
16971 return menus[menu];
16972 }else if(menu.events){ // menu instance
16974 }else if(typeof menu.length == 'number'){ // array of menu items?
16975 return new Roo.menu.Menu({items:menu});
16976 }else{ // otherwise, must be a config
16977 return new Roo.menu.Menu(menu);
16982 unregister : function(menu){
16983 delete menus[menu.id];
16984 menu.un("beforehide", onBeforeHide);
16985 menu.un("hide", onHide);
16986 menu.un("beforeshow", onBeforeShow);
16987 menu.un("show", onShow);
16988 var g = menu.group;
16989 if(g && menu.events["checkchange"]){
16990 groups[g].remove(menu);
16991 menu.un("checkchange", onCheck);
16996 registerCheckable : function(menuItem){
16997 var g = menuItem.group;
17002 groups[g].push(menuItem);
17003 menuItem.on("beforecheckchange", onBeforeCheck);
17008 unregisterCheckable : function(menuItem){
17009 var g = menuItem.group;
17011 groups[g].remove(menuItem);
17012 menuItem.un("beforecheckchange", onBeforeCheck);
17018 * Ext JS Library 1.1.1
17019 * Copyright(c) 2006-2007, Ext JS, LLC.
17021 * Originally Released Under LGPL - original licence link has changed is not relivant.
17024 * <script type="text/javascript">
17029 * @class Roo.menu.BaseItem
17030 * @extends Roo.Component
17032 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
17033 * management and base configuration options shared by all menu components.
17035 * Creates a new BaseItem
17036 * @param {Object} config Configuration options
17038 Roo.menu.BaseItem = function(config){
17039 Roo.menu.BaseItem.superclass.constructor.call(this, config);
17044 * Fires when this item is clicked
17045 * @param {Roo.menu.BaseItem} this
17046 * @param {Roo.EventObject} e
17051 * Fires when this item is activated
17052 * @param {Roo.menu.BaseItem} this
17056 * @event deactivate
17057 * Fires when this item is deactivated
17058 * @param {Roo.menu.BaseItem} this
17064 this.on("click", this.handler, this.scope, true);
17068 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
17070 * @cfg {Function} handler
17071 * A function that will handle the click event of this menu item (defaults to undefined)
17074 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
17076 canActivate : false,
17079 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
17084 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
17086 activeClass : "x-menu-item-active",
17088 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
17090 hideOnClick : true,
17092 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
17097 ctype: "Roo.menu.BaseItem",
17100 actionMode : "container",
17103 render : function(container, parentMenu){
17104 this.parentMenu = parentMenu;
17105 Roo.menu.BaseItem.superclass.render.call(this, container);
17106 this.container.menuItemId = this.id;
17110 onRender : function(container, position){
17111 this.el = Roo.get(this.el);
17112 container.dom.appendChild(this.el.dom);
17116 onClick : function(e){
17117 if(!this.disabled && this.fireEvent("click", this, e) !== false
17118 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
17119 this.handleClick(e);
17126 activate : function(){
17130 var li = this.container;
17131 li.addClass(this.activeClass);
17132 this.region = li.getRegion().adjust(2, 2, -2, -2);
17133 this.fireEvent("activate", this);
17138 deactivate : function(){
17139 this.container.removeClass(this.activeClass);
17140 this.fireEvent("deactivate", this);
17144 shouldDeactivate : function(e){
17145 return !this.region || !this.region.contains(e.getPoint());
17149 handleClick : function(e){
17150 if(this.hideOnClick){
17151 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
17156 expandMenu : function(autoActivate){
17161 hideMenu : function(){
17166 * Ext JS Library 1.1.1
17167 * Copyright(c) 2006-2007, Ext JS, LLC.
17169 * Originally Released Under LGPL - original licence link has changed is not relivant.
17172 * <script type="text/javascript">
17176 * @class Roo.menu.Adapter
17177 * @extends Roo.menu.BaseItem
17179 * 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.
17180 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
17182 * Creates a new Adapter
17183 * @param {Object} config Configuration options
17185 Roo.menu.Adapter = function(component, config){
17186 Roo.menu.Adapter.superclass.constructor.call(this, config);
17187 this.component = component;
17189 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
17191 canActivate : true,
17194 onRender : function(container, position){
17195 this.component.render(container);
17196 this.el = this.component.getEl();
17200 activate : function(){
17204 this.component.focus();
17205 this.fireEvent("activate", this);
17210 deactivate : function(){
17211 this.fireEvent("deactivate", this);
17215 disable : function(){
17216 this.component.disable();
17217 Roo.menu.Adapter.superclass.disable.call(this);
17221 enable : function(){
17222 this.component.enable();
17223 Roo.menu.Adapter.superclass.enable.call(this);
17227 * Ext JS Library 1.1.1
17228 * Copyright(c) 2006-2007, Ext JS, LLC.
17230 * Originally Released Under LGPL - original licence link has changed is not relivant.
17233 * <script type="text/javascript">
17237 * @class Roo.menu.TextItem
17238 * @extends Roo.menu.BaseItem
17239 * Adds a static text string to a menu, usually used as either a heading or group separator.
17240 * Note: old style constructor with text is still supported.
17243 * Creates a new TextItem
17244 * @param {Object} cfg Configuration
17246 Roo.menu.TextItem = function(cfg){
17247 if (typeof(cfg) == 'string') {
17250 Roo.apply(this,cfg);
17253 Roo.menu.TextItem.superclass.constructor.call(this);
17256 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
17258 * @cfg {String} text Text to show on item.
17263 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
17265 hideOnClick : false,
17267 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
17269 itemCls : "x-menu-text",
17272 onRender : function(){
17273 var s = document.createElement("span");
17274 s.className = this.itemCls;
17275 s.innerHTML = this.text;
17277 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
17281 * Ext JS Library 1.1.1
17282 * Copyright(c) 2006-2007, Ext JS, LLC.
17284 * Originally Released Under LGPL - original licence link has changed is not relivant.
17287 * <script type="text/javascript">
17291 * @class Roo.menu.Separator
17292 * @extends Roo.menu.BaseItem
17293 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
17294 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
17296 * @param {Object} config Configuration options
17298 Roo.menu.Separator = function(config){
17299 Roo.menu.Separator.superclass.constructor.call(this, config);
17302 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
17304 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
17306 itemCls : "x-menu-sep",
17308 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
17310 hideOnClick : false,
17313 onRender : function(li){
17314 var s = document.createElement("span");
17315 s.className = this.itemCls;
17316 s.innerHTML = " ";
17318 li.addClass("x-menu-sep-li");
17319 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
17323 * Ext JS Library 1.1.1
17324 * Copyright(c) 2006-2007, Ext JS, LLC.
17326 * Originally Released Under LGPL - original licence link has changed is not relivant.
17329 * <script type="text/javascript">
17332 * @class Roo.menu.Item
17333 * @extends Roo.menu.BaseItem
17334 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
17335 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
17336 * activation and click handling.
17338 * Creates a new Item
17339 * @param {Object} config Configuration options
17341 Roo.menu.Item = function(config){
17342 Roo.menu.Item.superclass.constructor.call(this, config);
17344 this.menu = Roo.menu.MenuMgr.get(this.menu);
17347 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
17349 * @cfg {Roo.menu.Menu} menu
17353 * @cfg {String} text
17354 * The text to show on the menu item.
17358 * @cfg {String} html to render in menu
17359 * The text to show on the menu item (HTML version).
17363 * @cfg {String} icon
17364 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
17368 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
17370 itemCls : "x-menu-item",
17372 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
17374 canActivate : true,
17376 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
17379 // doc'd in BaseItem
17383 ctype: "Roo.menu.Item",
17386 onRender : function(container, position){
17387 var el = document.createElement("a");
17388 el.hideFocus = true;
17389 el.unselectable = "on";
17390 el.href = this.href || "#";
17391 if(this.hrefTarget){
17392 el.target = this.hrefTarget;
17394 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
17396 var html = this.html.length ? this.html : String.format('{0}',this.text);
17398 el.innerHTML = String.format(
17399 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
17400 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
17402 Roo.menu.Item.superclass.onRender.call(this, container, position);
17406 * Sets the text to display in this menu item
17407 * @param {String} text The text to display
17408 * @param {Boolean} isHTML true to indicate text is pure html.
17410 setText : function(text, isHTML){
17418 var html = this.html.length ? this.html : String.format('{0}',this.text);
17420 this.el.update(String.format(
17421 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
17422 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
17423 this.parentMenu.autoWidth();
17428 handleClick : function(e){
17429 if(!this.href){ // if no link defined, stop the event automatically
17432 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
17436 activate : function(autoExpand){
17437 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
17447 shouldDeactivate : function(e){
17448 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
17449 if(this.menu && this.menu.isVisible()){
17450 return !this.menu.getEl().getRegion().contains(e.getPoint());
17458 deactivate : function(){
17459 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
17464 expandMenu : function(autoActivate){
17465 if(!this.disabled && this.menu){
17466 clearTimeout(this.hideTimer);
17467 delete this.hideTimer;
17468 if(!this.menu.isVisible() && !this.showTimer){
17469 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
17470 }else if (this.menu.isVisible() && autoActivate){
17471 this.menu.tryActivate(0, 1);
17477 deferExpand : function(autoActivate){
17478 delete this.showTimer;
17479 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
17481 this.menu.tryActivate(0, 1);
17486 hideMenu : function(){
17487 clearTimeout(this.showTimer);
17488 delete this.showTimer;
17489 if(!this.hideTimer && this.menu && this.menu.isVisible()){
17490 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
17495 deferHide : function(){
17496 delete this.hideTimer;
17501 * Ext JS Library 1.1.1
17502 * Copyright(c) 2006-2007, Ext JS, LLC.
17504 * Originally Released Under LGPL - original licence link has changed is not relivant.
17507 * <script type="text/javascript">
17511 * @class Roo.menu.CheckItem
17512 * @extends Roo.menu.Item
17513 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
17515 * Creates a new CheckItem
17516 * @param {Object} config Configuration options
17518 Roo.menu.CheckItem = function(config){
17519 Roo.menu.CheckItem.superclass.constructor.call(this, config);
17522 * @event beforecheckchange
17523 * Fires before the checked value is set, providing an opportunity to cancel if needed
17524 * @param {Roo.menu.CheckItem} this
17525 * @param {Boolean} checked The new checked value that will be set
17527 "beforecheckchange" : true,
17529 * @event checkchange
17530 * Fires after the checked value has been set
17531 * @param {Roo.menu.CheckItem} this
17532 * @param {Boolean} checked The checked value that was set
17534 "checkchange" : true
17536 if(this.checkHandler){
17537 this.on('checkchange', this.checkHandler, this.scope);
17540 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
17542 * @cfg {String} group
17543 * All check items with the same group name will automatically be grouped into a single-select
17544 * radio button group (defaults to '')
17547 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
17549 itemCls : "x-menu-item x-menu-check-item",
17551 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
17553 groupClass : "x-menu-group-item",
17556 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
17557 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
17558 * initialized with checked = true will be rendered as checked.
17563 ctype: "Roo.menu.CheckItem",
17566 onRender : function(c){
17567 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
17569 this.el.addClass(this.groupClass);
17571 Roo.menu.MenuMgr.registerCheckable(this);
17573 this.checked = false;
17574 this.setChecked(true, true);
17579 destroy : function(){
17581 Roo.menu.MenuMgr.unregisterCheckable(this);
17583 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
17587 * Set the checked state of this item
17588 * @param {Boolean} checked The new checked value
17589 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
17591 setChecked : function(state, suppressEvent){
17592 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
17593 if(this.container){
17594 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
17596 this.checked = state;
17597 if(suppressEvent !== true){
17598 this.fireEvent("checkchange", this, state);
17604 handleClick : function(e){
17605 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
17606 this.setChecked(!this.checked);
17608 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
17612 * Ext JS Library 1.1.1
17613 * Copyright(c) 2006-2007, Ext JS, LLC.
17615 * Originally Released Under LGPL - original licence link has changed is not relivant.
17618 * <script type="text/javascript">
17622 * @class Roo.menu.DateItem
17623 * @extends Roo.menu.Adapter
17624 * A menu item that wraps the {@link Roo.DatPicker} component.
17626 * Creates a new DateItem
17627 * @param {Object} config Configuration options
17629 Roo.menu.DateItem = function(config){
17630 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
17631 /** The Roo.DatePicker object @type Roo.DatePicker */
17632 this.picker = this.component;
17633 this.addEvents({select: true});
17635 this.picker.on("render", function(picker){
17636 picker.getEl().swallowEvent("click");
17637 picker.container.addClass("x-menu-date-item");
17640 this.picker.on("select", this.onSelect, this);
17643 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
17645 onSelect : function(picker, date){
17646 this.fireEvent("select", this, date, picker);
17647 Roo.menu.DateItem.superclass.handleClick.call(this);
17651 * Ext JS Library 1.1.1
17652 * Copyright(c) 2006-2007, Ext JS, LLC.
17654 * Originally Released Under LGPL - original licence link has changed is not relivant.
17657 * <script type="text/javascript">
17661 * @class Roo.menu.ColorItem
17662 * @extends Roo.menu.Adapter
17663 * A menu item that wraps the {@link Roo.ColorPalette} component.
17665 * Creates a new ColorItem
17666 * @param {Object} config Configuration options
17668 Roo.menu.ColorItem = function(config){
17669 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
17670 /** The Roo.ColorPalette object @type Roo.ColorPalette */
17671 this.palette = this.component;
17672 this.relayEvents(this.palette, ["select"]);
17673 if(this.selectHandler){
17674 this.on('select', this.selectHandler, this.scope);
17677 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
17679 * Ext JS Library 1.1.1
17680 * Copyright(c) 2006-2007, Ext JS, LLC.
17682 * Originally Released Under LGPL - original licence link has changed is not relivant.
17685 * <script type="text/javascript">
17690 * @class Roo.menu.DateMenu
17691 * @extends Roo.menu.Menu
17692 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
17694 * Creates a new DateMenu
17695 * @param {Object} config Configuration options
17697 Roo.menu.DateMenu = function(config){
17698 Roo.menu.DateMenu.superclass.constructor.call(this, config);
17700 var di = new Roo.menu.DateItem(config);
17703 * The {@link Roo.DatePicker} instance for this DateMenu
17706 this.picker = di.picker;
17709 * @param {DatePicker} picker
17710 * @param {Date} date
17712 this.relayEvents(di, ["select"]);
17713 this.on('beforeshow', function(){
17715 this.picker.hideMonthPicker(false);
17719 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
17723 * Ext JS Library 1.1.1
17724 * Copyright(c) 2006-2007, Ext JS, LLC.
17726 * Originally Released Under LGPL - original licence link has changed is not relivant.
17729 * <script type="text/javascript">
17734 * @class Roo.menu.ColorMenu
17735 * @extends Roo.menu.Menu
17736 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
17738 * Creates a new ColorMenu
17739 * @param {Object} config Configuration options
17741 Roo.menu.ColorMenu = function(config){
17742 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
17744 var ci = new Roo.menu.ColorItem(config);
17747 * The {@link Roo.ColorPalette} instance for this ColorMenu
17748 * @type ColorPalette
17750 this.palette = ci.palette;
17753 * @param {ColorPalette} palette
17754 * @param {String} color
17756 this.relayEvents(ci, ["select"]);
17758 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
17760 * Ext JS Library 1.1.1
17761 * Copyright(c) 2006-2007, Ext JS, LLC.
17763 * Originally Released Under LGPL - original licence link has changed is not relivant.
17766 * <script type="text/javascript">
17770 * @class Roo.form.TextItem
17771 * @extends Roo.BoxComponent
17772 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
17774 * Creates a new TextItem
17775 * @param {Object} config Configuration options
17777 Roo.form.TextItem = function(config){
17778 Roo.form.TextItem.superclass.constructor.call(this, config);
17781 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
17784 * @cfg {String} tag the tag for this item (default div)
17788 * @cfg {String} html the content for this item
17792 getAutoCreate : function()
17805 onRender : function(ct, position)
17807 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
17810 var cfg = this.getAutoCreate();
17812 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
17814 if (!cfg.name.length) {
17817 this.el = ct.createChild(cfg, position);
17822 * @param {String} html update the Contents of the element.
17824 setHTML : function(html)
17826 this.fieldEl.dom.innerHTML = html;
17831 * Ext JS Library 1.1.1
17832 * Copyright(c) 2006-2007, Ext JS, LLC.
17834 * Originally Released Under LGPL - original licence link has changed is not relivant.
17837 * <script type="text/javascript">
17841 * @class Roo.form.Field
17842 * @extends Roo.BoxComponent
17843 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
17845 * Creates a new Field
17846 * @param {Object} config Configuration options
17848 Roo.form.Field = function(config){
17849 Roo.form.Field.superclass.constructor.call(this, config);
17852 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
17854 * @cfg {String} fieldLabel Label to use when rendering a form.
17857 * @cfg {String} qtip Mouse over tip
17861 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
17863 invalidClass : "x-form-invalid",
17865 * @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")
17867 invalidText : "The value in this field is invalid",
17869 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
17871 focusClass : "x-form-focus",
17873 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
17874 automatic validation (defaults to "keyup").
17876 validationEvent : "keyup",
17878 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
17880 validateOnBlur : true,
17882 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
17884 validationDelay : 250,
17886 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17887 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
17889 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
17891 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
17893 fieldClass : "x-form-field",
17895 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
17898 ----------- ----------------------------------------------------------------------
17899 qtip Display a quick tip when the user hovers over the field
17900 title Display a default browser title attribute popup
17901 under Add a block div beneath the field containing the error text
17902 side Add an error icon to the right of the field with a popup on hover
17903 [element id] Add the error text directly to the innerHTML of the specified element
17906 msgTarget : 'qtip',
17908 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
17913 * @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.
17918 * @cfg {Boolean} disabled True to disable the field (defaults to false).
17923 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
17925 inputType : undefined,
17928 * @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).
17930 tabIndex : undefined,
17933 isFormField : true,
17938 * @property {Roo.Element} fieldEl
17939 * Element Containing the rendered Field (with label etc.)
17942 * @cfg {Mixed} value A value to initialize this field with.
17947 * @cfg {String} name The field's HTML name attribute.
17950 * @cfg {String} cls A CSS class to apply to the field's underlying element.
17953 loadedValue : false,
17957 initComponent : function(){
17958 Roo.form.Field.superclass.initComponent.call(this);
17962 * Fires when this field receives input focus.
17963 * @param {Roo.form.Field} this
17968 * Fires when this field loses input focus.
17969 * @param {Roo.form.Field} this
17973 * @event specialkey
17974 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
17975 * {@link Roo.EventObject#getKey} to determine which key was pressed.
17976 * @param {Roo.form.Field} this
17977 * @param {Roo.EventObject} e The event object
17982 * Fires just before the field blurs if the field value has changed.
17983 * @param {Roo.form.Field} this
17984 * @param {Mixed} newValue The new value
17985 * @param {Mixed} oldValue The original value
17990 * Fires after the field has been marked as invalid.
17991 * @param {Roo.form.Field} this
17992 * @param {String} msg The validation message
17997 * Fires after the field has been validated with no errors.
17998 * @param {Roo.form.Field} this
18003 * Fires after the key up
18004 * @param {Roo.form.Field} this
18005 * @param {Roo.EventObject} e The event Object
18012 * Returns the name attribute of the field if available
18013 * @return {String} name The field name
18015 getName: function(){
18016 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
18020 onRender : function(ct, position){
18021 Roo.form.Field.superclass.onRender.call(this, ct, position);
18023 var cfg = this.getAutoCreate();
18025 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
18027 if (!cfg.name.length) {
18030 if(this.inputType){
18031 cfg.type = this.inputType;
18033 this.el = ct.createChild(cfg, position);
18035 var type = this.el.dom.type;
18037 if(type == 'password'){
18040 this.el.addClass('x-form-'+type);
18043 this.el.dom.readOnly = true;
18045 if(this.tabIndex !== undefined){
18046 this.el.dom.setAttribute('tabIndex', this.tabIndex);
18049 this.el.addClass([this.fieldClass, this.cls]);
18054 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
18055 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
18056 * @return {Roo.form.Field} this
18058 applyTo : function(target){
18059 this.allowDomMove = false;
18060 this.el = Roo.get(target);
18061 this.render(this.el.dom.parentNode);
18066 initValue : function(){
18067 if(this.value !== undefined){
18068 this.setValue(this.value);
18069 }else if(this.el.dom.value.length > 0){
18070 this.setValue(this.el.dom.value);
18075 * Returns true if this field has been changed since it was originally loaded and is not disabled.
18076 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
18078 isDirty : function() {
18079 if(this.disabled) {
18082 return String(this.getValue()) !== String(this.originalValue);
18086 * stores the current value in loadedValue
18088 resetHasChanged : function()
18090 this.loadedValue = String(this.getValue());
18093 * checks the current value against the 'loaded' value.
18094 * Note - will return false if 'resetHasChanged' has not been called first.
18096 hasChanged : function()
18098 if(this.disabled || this.readOnly) {
18101 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
18107 afterRender : function(){
18108 Roo.form.Field.superclass.afterRender.call(this);
18113 fireKey : function(e){
18114 //Roo.log('field ' + e.getKey());
18115 if(e.isNavKeyPress()){
18116 this.fireEvent("specialkey", this, e);
18121 * Resets the current field value to the originally loaded value and clears any validation messages
18123 reset : function(){
18124 this.setValue(this.resetValue);
18125 this.originalValue = this.getValue();
18126 this.clearInvalid();
18130 initEvents : function(){
18131 // safari killled keypress - so keydown is now used..
18132 this.el.on("keydown" , this.fireKey, this);
18133 this.el.on("focus", this.onFocus, this);
18134 this.el.on("blur", this.onBlur, this);
18135 this.el.relayEvent('keyup', this);
18137 // reference to original value for reset
18138 this.originalValue = this.getValue();
18139 this.resetValue = this.getValue();
18143 onFocus : function(){
18144 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
18145 this.el.addClass(this.focusClass);
18147 if(!this.hasFocus){
18148 this.hasFocus = true;
18149 this.startValue = this.getValue();
18150 this.fireEvent("focus", this);
18154 beforeBlur : Roo.emptyFn,
18157 onBlur : function(){
18159 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
18160 this.el.removeClass(this.focusClass);
18162 this.hasFocus = false;
18163 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
18166 var v = this.getValue();
18167 if(String(v) !== String(this.startValue)){
18168 this.fireEvent('change', this, v, this.startValue);
18170 this.fireEvent("blur", this);
18174 * Returns whether or not the field value is currently valid
18175 * @param {Boolean} preventMark True to disable marking the field invalid
18176 * @return {Boolean} True if the value is valid, else false
18178 isValid : function(preventMark){
18182 var restore = this.preventMark;
18183 this.preventMark = preventMark === true;
18184 var v = this.validateValue(this.processValue(this.getRawValue()));
18185 this.preventMark = restore;
18190 * Validates the field value
18191 * @return {Boolean} True if the value is valid, else false
18193 validate : function(){
18194 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
18195 this.clearInvalid();
18201 processValue : function(value){
18206 // Subclasses should provide the validation implementation by overriding this
18207 validateValue : function(value){
18212 * Mark this field as invalid
18213 * @param {String} msg The validation message
18215 markInvalid : function(msg){
18216 if(!this.rendered || this.preventMark){ // not rendered
18220 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
18222 obj.el.addClass(this.invalidClass);
18223 msg = msg || this.invalidText;
18224 switch(this.msgTarget){
18226 obj.el.dom.qtip = msg;
18227 obj.el.dom.qclass = 'x-form-invalid-tip';
18228 if(Roo.QuickTips){ // fix for floating editors interacting with DND
18229 Roo.QuickTips.enable();
18233 this.el.dom.title = msg;
18237 var elp = this.el.findParent('.x-form-element', 5, true);
18238 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
18239 this.errorEl.setWidth(elp.getWidth(true)-20);
18241 this.errorEl.update(msg);
18242 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
18245 if(!this.errorIcon){
18246 var elp = this.el.findParent('.x-form-element', 5, true);
18247 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
18249 this.alignErrorIcon();
18250 this.errorIcon.dom.qtip = msg;
18251 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
18252 this.errorIcon.show();
18253 this.on('resize', this.alignErrorIcon, this);
18256 var t = Roo.getDom(this.msgTarget);
18258 t.style.display = this.msgDisplay;
18261 this.fireEvent('invalid', this, msg);
18265 alignErrorIcon : function(){
18266 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
18270 * Clear any invalid styles/messages for this field
18272 clearInvalid : function(){
18273 if(!this.rendered || this.preventMark){ // not rendered
18276 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
18278 obj.el.removeClass(this.invalidClass);
18279 switch(this.msgTarget){
18281 obj.el.dom.qtip = '';
18284 this.el.dom.title = '';
18288 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
18292 if(this.errorIcon){
18293 this.errorIcon.dom.qtip = '';
18294 this.errorIcon.hide();
18295 this.un('resize', this.alignErrorIcon, this);
18299 var t = Roo.getDom(this.msgTarget);
18301 t.style.display = 'none';
18304 this.fireEvent('valid', this);
18308 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
18309 * @return {Mixed} value The field value
18311 getRawValue : function(){
18312 var v = this.el.getValue();
18318 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
18319 * @return {Mixed} value The field value
18321 getValue : function(){
18322 var v = this.el.getValue();
18328 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
18329 * @param {Mixed} value The value to set
18331 setRawValue : function(v){
18332 return this.el.dom.value = (v === null || v === undefined ? '' : v);
18336 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
18337 * @param {Mixed} value The value to set
18339 setValue : function(v){
18342 this.el.dom.value = (v === null || v === undefined ? '' : v);
18347 adjustSize : function(w, h){
18348 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
18349 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
18353 adjustWidth : function(tag, w){
18354 tag = tag.toLowerCase();
18355 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
18356 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
18357 if(tag == 'input'){
18360 if(tag == 'textarea'){
18363 }else if(Roo.isOpera){
18364 if(tag == 'input'){
18367 if(tag == 'textarea'){
18377 // anything other than normal should be considered experimental
18378 Roo.form.Field.msgFx = {
18380 show: function(msgEl, f){
18381 msgEl.setDisplayed('block');
18384 hide : function(msgEl, f){
18385 msgEl.setDisplayed(false).update('');
18390 show: function(msgEl, f){
18391 msgEl.slideIn('t', {stopFx:true});
18394 hide : function(msgEl, f){
18395 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
18400 show: function(msgEl, f){
18401 msgEl.fixDisplay();
18402 msgEl.alignTo(f.el, 'tl-tr');
18403 msgEl.slideIn('l', {stopFx:true});
18406 hide : function(msgEl, f){
18407 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
18412 * Ext JS Library 1.1.1
18413 * Copyright(c) 2006-2007, Ext JS, LLC.
18415 * Originally Released Under LGPL - original licence link has changed is not relivant.
18418 * <script type="text/javascript">
18423 * @class Roo.form.TextField
18424 * @extends Roo.form.Field
18425 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
18426 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
18428 * Creates a new TextField
18429 * @param {Object} config Configuration options
18431 Roo.form.TextField = function(config){
18432 Roo.form.TextField.superclass.constructor.call(this, config);
18436 * Fires when the autosize function is triggered. The field may or may not have actually changed size
18437 * according to the default logic, but this event provides a hook for the developer to apply additional
18438 * logic at runtime to resize the field if needed.
18439 * @param {Roo.form.Field} this This text field
18440 * @param {Number} width The new field width
18446 Roo.extend(Roo.form.TextField, Roo.form.Field, {
18448 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
18452 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
18456 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
18460 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
18464 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
18468 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
18470 disableKeyFilter : false,
18472 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
18476 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
18480 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
18482 maxLength : Number.MAX_VALUE,
18484 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
18486 minLengthText : "The minimum length for this field is {0}",
18488 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
18490 maxLengthText : "The maximum length for this field is {0}",
18492 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
18494 selectOnFocus : false,
18496 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
18498 allowLeadingSpace : false,
18500 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
18502 blankText : "This field is required",
18504 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
18505 * If available, this function will be called only after the basic validators all return true, and will be passed the
18506 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
18510 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
18511 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
18512 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
18516 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
18520 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
18526 initEvents : function()
18528 if (this.emptyText) {
18529 this.el.attr('placeholder', this.emptyText);
18532 Roo.form.TextField.superclass.initEvents.call(this);
18533 if(this.validationEvent == 'keyup'){
18534 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
18535 this.el.on('keyup', this.filterValidation, this);
18537 else if(this.validationEvent !== false){
18538 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
18541 if(this.selectOnFocus){
18542 this.on("focus", this.preFocus, this);
18544 if (!this.allowLeadingSpace) {
18545 this.on('blur', this.cleanLeadingSpace, this);
18548 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
18549 this.el.on("keypress", this.filterKeys, this);
18552 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
18553 this.el.on("click", this.autoSize, this);
18555 if(this.el.is('input[type=password]') && Roo.isSafari){
18556 this.el.on('keydown', this.SafariOnKeyDown, this);
18560 processValue : function(value){
18561 if(this.stripCharsRe){
18562 var newValue = value.replace(this.stripCharsRe, '');
18563 if(newValue !== value){
18564 this.setRawValue(newValue);
18571 filterValidation : function(e){
18572 if(!e.isNavKeyPress()){
18573 this.validationTask.delay(this.validationDelay);
18578 onKeyUp : function(e){
18579 if(!e.isNavKeyPress()){
18583 // private - clean the leading white space
18584 cleanLeadingSpace : function(e)
18586 if ( this.inputType == 'file') {
18590 this.setValue((this.getValue() + '').replace(/^\s+/,''));
18593 * Resets the current field value to the originally-loaded value and clears any validation messages.
18596 reset : function(){
18597 Roo.form.TextField.superclass.reset.call(this);
18601 preFocus : function(){
18603 if(this.selectOnFocus){
18604 this.el.dom.select();
18610 filterKeys : function(e){
18611 var k = e.getKey();
18612 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
18615 var c = e.getCharCode(), cc = String.fromCharCode(c);
18616 if(Roo.isIE && (e.isSpecialKey() || !cc)){
18619 if(!this.maskRe.test(cc)){
18624 setValue : function(v){
18626 Roo.form.TextField.superclass.setValue.apply(this, arguments);
18632 * Validates a value according to the field's validation rules and marks the field as invalid
18633 * if the validation fails
18634 * @param {Mixed} value The value to validate
18635 * @return {Boolean} True if the value is valid, else false
18637 validateValue : function(value){
18638 if(value.length < 1) { // if it's blank
18639 if(this.allowBlank){
18640 this.clearInvalid();
18643 this.markInvalid(this.blankText);
18647 if(value.length < this.minLength){
18648 this.markInvalid(String.format(this.minLengthText, this.minLength));
18651 if(value.length > this.maxLength){
18652 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
18656 var vt = Roo.form.VTypes;
18657 if(!vt[this.vtype](value, this)){
18658 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
18662 if(typeof this.validator == "function"){
18663 var msg = this.validator(value);
18665 this.markInvalid(msg);
18669 if(this.regex && !this.regex.test(value)){
18670 this.markInvalid(this.regexText);
18677 * Selects text in this field
18678 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
18679 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
18681 selectText : function(start, end){
18682 var v = this.getRawValue();
18684 start = start === undefined ? 0 : start;
18685 end = end === undefined ? v.length : end;
18686 var d = this.el.dom;
18687 if(d.setSelectionRange){
18688 d.setSelectionRange(start, end);
18689 }else if(d.createTextRange){
18690 var range = d.createTextRange();
18691 range.moveStart("character", start);
18692 range.moveEnd("character", v.length-end);
18699 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
18700 * This only takes effect if grow = true, and fires the autosize event.
18702 autoSize : function(){
18703 if(!this.grow || !this.rendered){
18707 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
18710 var v = el.dom.value;
18711 var d = document.createElement('div');
18712 d.appendChild(document.createTextNode(v));
18716 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
18717 this.el.setWidth(w);
18718 this.fireEvent("autosize", this, w);
18722 SafariOnKeyDown : function(event)
18724 // this is a workaround for a password hang bug on chrome/ webkit.
18726 var isSelectAll = false;
18728 if(this.el.dom.selectionEnd > 0){
18729 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
18731 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
18732 event.preventDefault();
18737 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
18739 event.preventDefault();
18740 // this is very hacky as keydown always get's upper case.
18742 var cc = String.fromCharCode(event.getCharCode());
18745 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
18753 * Ext JS Library 1.1.1
18754 * Copyright(c) 2006-2007, Ext JS, LLC.
18756 * Originally Released Under LGPL - original licence link has changed is not relivant.
18759 * <script type="text/javascript">
18763 * @class Roo.form.Hidden
18764 * @extends Roo.form.TextField
18765 * Simple Hidden element used on forms
18767 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
18770 * Creates a new Hidden form element.
18771 * @param {Object} config Configuration options
18776 // easy hidden field...
18777 Roo.form.Hidden = function(config){
18778 Roo.form.Hidden.superclass.constructor.call(this, config);
18781 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
18783 inputType: 'hidden',
18786 labelSeparator: '',
18788 itemCls : 'x-form-item-display-none'
18796 * Ext JS Library 1.1.1
18797 * Copyright(c) 2006-2007, Ext JS, LLC.
18799 * Originally Released Under LGPL - original licence link has changed is not relivant.
18802 * <script type="text/javascript">
18806 * @class Roo.form.TriggerField
18807 * @extends Roo.form.TextField
18808 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
18809 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
18810 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
18811 * for which you can provide a custom implementation. For example:
18813 var trigger = new Roo.form.TriggerField();
18814 trigger.onTriggerClick = myTriggerFn;
18815 trigger.applyTo('my-field');
18818 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
18819 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
18820 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18821 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
18823 * Create a new TriggerField.
18824 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
18825 * to the base TextField)
18827 Roo.form.TriggerField = function(config){
18828 this.mimicing = false;
18829 Roo.form.TriggerField.superclass.constructor.call(this, config);
18832 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
18834 * @cfg {String} triggerClass A CSS class to apply to the trigger
18837 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
18838 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
18840 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
18842 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
18846 /** @cfg {Boolean} grow @hide */
18847 /** @cfg {Number} growMin @hide */
18848 /** @cfg {Number} growMax @hide */
18854 autoSize: Roo.emptyFn,
18858 deferHeight : true,
18861 actionMode : 'wrap',
18863 onResize : function(w, h){
18864 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
18865 if(typeof w == 'number'){
18866 var x = w - this.trigger.getWidth();
18867 this.el.setWidth(this.adjustWidth('input', x));
18868 this.trigger.setStyle('left', x+'px');
18873 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18876 getResizeEl : function(){
18881 getPositionEl : function(){
18886 alignErrorIcon : function(){
18887 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
18891 onRender : function(ct, position){
18892 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
18893 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
18894 this.trigger = this.wrap.createChild(this.triggerConfig ||
18895 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
18896 if(this.hideTrigger){
18897 this.trigger.setDisplayed(false);
18899 this.initTrigger();
18901 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
18906 initTrigger : function(){
18907 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
18908 this.trigger.addClassOnOver('x-form-trigger-over');
18909 this.trigger.addClassOnClick('x-form-trigger-click');
18913 onDestroy : function(){
18915 this.trigger.removeAllListeners();
18916 this.trigger.remove();
18919 this.wrap.remove();
18921 Roo.form.TriggerField.superclass.onDestroy.call(this);
18925 onFocus : function(){
18926 Roo.form.TriggerField.superclass.onFocus.call(this);
18927 if(!this.mimicing){
18928 this.wrap.addClass('x-trigger-wrap-focus');
18929 this.mimicing = true;
18930 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
18931 if(this.monitorTab){
18932 this.el.on("keydown", this.checkTab, this);
18938 checkTab : function(e){
18939 if(e.getKey() == e.TAB){
18940 this.triggerBlur();
18945 onBlur : function(){
18950 mimicBlur : function(e, t){
18951 if(!this.wrap.contains(t) && this.validateBlur()){
18952 this.triggerBlur();
18957 triggerBlur : function(){
18958 this.mimicing = false;
18959 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
18960 if(this.monitorTab){
18961 this.el.un("keydown", this.checkTab, this);
18963 this.wrap.removeClass('x-trigger-wrap-focus');
18964 Roo.form.TriggerField.superclass.onBlur.call(this);
18968 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
18969 validateBlur : function(e, t){
18974 onDisable : function(){
18975 Roo.form.TriggerField.superclass.onDisable.call(this);
18977 this.wrap.addClass('x-item-disabled');
18982 onEnable : function(){
18983 Roo.form.TriggerField.superclass.onEnable.call(this);
18985 this.wrap.removeClass('x-item-disabled');
18990 onShow : function(){
18991 var ae = this.getActionEl();
18994 ae.dom.style.display = '';
18995 ae.dom.style.visibility = 'visible';
19001 onHide : function(){
19002 var ae = this.getActionEl();
19003 ae.dom.style.display = 'none';
19007 * The function that should handle the trigger's click event. This method does nothing by default until overridden
19008 * by an implementing function.
19010 * @param {EventObject} e
19012 onTriggerClick : Roo.emptyFn
19015 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
19016 // to be extended by an implementing class. For an example of implementing this class, see the custom
19017 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
19018 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
19019 initComponent : function(){
19020 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
19022 this.triggerConfig = {
19023 tag:'span', cls:'x-form-twin-triggers', cn:[
19024 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
19025 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
19029 getTrigger : function(index){
19030 return this.triggers[index];
19033 initTrigger : function(){
19034 var ts = this.trigger.select('.x-form-trigger', true);
19035 this.wrap.setStyle('overflow', 'hidden');
19036 var triggerField = this;
19037 ts.each(function(t, all, index){
19038 t.hide = function(){
19039 var w = triggerField.wrap.getWidth();
19040 this.dom.style.display = 'none';
19041 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
19043 t.show = function(){
19044 var w = triggerField.wrap.getWidth();
19045 this.dom.style.display = '';
19046 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
19048 var triggerIndex = 'Trigger'+(index+1);
19050 if(this['hide'+triggerIndex]){
19051 t.dom.style.display = 'none';
19053 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
19054 t.addClassOnOver('x-form-trigger-over');
19055 t.addClassOnClick('x-form-trigger-click');
19057 this.triggers = ts.elements;
19060 onTrigger1Click : Roo.emptyFn,
19061 onTrigger2Click : Roo.emptyFn
19064 * Ext JS Library 1.1.1
19065 * Copyright(c) 2006-2007, Ext JS, LLC.
19067 * Originally Released Under LGPL - original licence link has changed is not relivant.
19070 * <script type="text/javascript">
19074 * @class Roo.form.TextArea
19075 * @extends Roo.form.TextField
19076 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
19077 * support for auto-sizing.
19079 * Creates a new TextArea
19080 * @param {Object} config Configuration options
19082 Roo.form.TextArea = function(config){
19083 Roo.form.TextArea.superclass.constructor.call(this, config);
19084 // these are provided exchanges for backwards compat
19085 // minHeight/maxHeight were replaced by growMin/growMax to be
19086 // compatible with TextField growing config values
19087 if(this.minHeight !== undefined){
19088 this.growMin = this.minHeight;
19090 if(this.maxHeight !== undefined){
19091 this.growMax = this.maxHeight;
19095 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
19097 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
19101 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
19105 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
19106 * in the field (equivalent to setting overflow: hidden, defaults to false)
19108 preventScrollbars: false,
19110 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
19111 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
19115 onRender : function(ct, position){
19117 this.defaultAutoCreate = {
19119 style:"width:300px;height:60px;",
19120 autocomplete: "new-password"
19123 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
19125 this.textSizeEl = Roo.DomHelper.append(document.body, {
19126 tag: "pre", cls: "x-form-grow-sizer"
19128 if(this.preventScrollbars){
19129 this.el.setStyle("overflow", "hidden");
19131 this.el.setHeight(this.growMin);
19135 onDestroy : function(){
19136 if(this.textSizeEl){
19137 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
19139 Roo.form.TextArea.superclass.onDestroy.call(this);
19143 onKeyUp : function(e){
19144 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
19150 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
19151 * This only takes effect if grow = true, and fires the autosize event if the height changes.
19153 autoSize : function(){
19154 if(!this.grow || !this.textSizeEl){
19158 var v = el.dom.value;
19159 var ts = this.textSizeEl;
19162 ts.appendChild(document.createTextNode(v));
19165 Roo.fly(ts).setWidth(this.el.getWidth());
19167 v = "  ";
19170 v = v.replace(/\n/g, '<p> </p>');
19172 v += " \n ";
19175 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
19176 if(h != this.lastHeight){
19177 this.lastHeight = h;
19178 this.el.setHeight(h);
19179 this.fireEvent("autosize", this, h);
19184 * Ext JS Library 1.1.1
19185 * Copyright(c) 2006-2007, Ext JS, LLC.
19187 * Originally Released Under LGPL - original licence link has changed is not relivant.
19190 * <script type="text/javascript">
19195 * @class Roo.form.NumberField
19196 * @extends Roo.form.TextField
19197 * Numeric text field that provides automatic keystroke filtering and numeric validation.
19199 * Creates a new NumberField
19200 * @param {Object} config Configuration options
19202 Roo.form.NumberField = function(config){
19203 Roo.form.NumberField.superclass.constructor.call(this, config);
19206 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
19208 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
19210 fieldClass: "x-form-field x-form-num-field",
19212 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
19214 allowDecimals : true,
19216 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
19218 decimalSeparator : ".",
19220 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
19222 decimalPrecision : 2,
19224 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
19226 allowNegative : true,
19228 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
19230 minValue : Number.NEGATIVE_INFINITY,
19232 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
19234 maxValue : Number.MAX_VALUE,
19236 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
19238 minText : "The minimum value for this field is {0}",
19240 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
19242 maxText : "The maximum value for this field is {0}",
19244 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
19245 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
19247 nanText : "{0} is not a valid number",
19250 initEvents : function(){
19251 Roo.form.NumberField.superclass.initEvents.call(this);
19252 var allowed = "0123456789";
19253 if(this.allowDecimals){
19254 allowed += this.decimalSeparator;
19256 if(this.allowNegative){
19259 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
19260 var keyPress = function(e){
19261 var k = e.getKey();
19262 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
19265 var c = e.getCharCode();
19266 if(allowed.indexOf(String.fromCharCode(c)) === -1){
19270 this.el.on("keypress", keyPress, this);
19274 validateValue : function(value){
19275 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
19278 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
19281 var num = this.parseValue(value);
19283 this.markInvalid(String.format(this.nanText, value));
19286 if(num < this.minValue){
19287 this.markInvalid(String.format(this.minText, this.minValue));
19290 if(num > this.maxValue){
19291 this.markInvalid(String.format(this.maxText, this.maxValue));
19297 getValue : function(){
19298 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
19302 parseValue : function(value){
19303 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
19304 return isNaN(value) ? '' : value;
19308 fixPrecision : function(value){
19309 var nan = isNaN(value);
19310 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
19311 return nan ? '' : value;
19313 return parseFloat(value).toFixed(this.decimalPrecision);
19316 setValue : function(v){
19317 v = this.fixPrecision(v);
19318 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
19322 decimalPrecisionFcn : function(v){
19323 return Math.floor(v);
19326 beforeBlur : function(){
19327 var v = this.parseValue(this.getRawValue());
19334 * Ext JS Library 1.1.1
19335 * Copyright(c) 2006-2007, Ext JS, LLC.
19337 * Originally Released Under LGPL - original licence link has changed is not relivant.
19340 * <script type="text/javascript">
19344 * @class Roo.form.DateField
19345 * @extends Roo.form.TriggerField
19346 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
19348 * Create a new DateField
19349 * @param {Object} config
19351 Roo.form.DateField = function(config)
19353 Roo.form.DateField.superclass.constructor.call(this, config);
19359 * Fires when a date is selected
19360 * @param {Roo.form.DateField} combo This combo box
19361 * @param {Date} date The date selected
19368 if(typeof this.minValue == "string") {
19369 this.minValue = this.parseDate(this.minValue);
19371 if(typeof this.maxValue == "string") {
19372 this.maxValue = this.parseDate(this.maxValue);
19374 this.ddMatch = null;
19375 if(this.disabledDates){
19376 var dd = this.disabledDates;
19378 for(var i = 0; i < dd.length; i++){
19380 if(i != dd.length-1) {
19384 this.ddMatch = new RegExp(re + ")");
19388 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
19390 * @cfg {String} format
19391 * The default date format string which can be overriden for localization support. The format must be
19392 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19396 * @cfg {String} altFormats
19397 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19398 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19400 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19402 * @cfg {Array} disabledDays
19403 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
19405 disabledDays : null,
19407 * @cfg {String} disabledDaysText
19408 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
19410 disabledDaysText : "Disabled",
19412 * @cfg {Array} disabledDates
19413 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
19414 * expression so they are very powerful. Some examples:
19416 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
19417 * <li>["03/08", "09/16"] would disable those days for every year</li>
19418 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
19419 * <li>["03/../2006"] would disable every day in March 2006</li>
19420 * <li>["^03"] would disable every day in every March</li>
19422 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
19423 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
19425 disabledDates : null,
19427 * @cfg {String} disabledDatesText
19428 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
19430 disabledDatesText : "Disabled",
19434 * @cfg {Date/String} zeroValue
19435 * if the date is less that this number, then the field is rendered as empty
19438 zeroValue : '1800-01-01',
19442 * @cfg {Date/String} minValue
19443 * The minimum allowed date. Can be either a Javascript date object or a string date in a
19444 * valid format (defaults to null).
19448 * @cfg {Date/String} maxValue
19449 * The maximum allowed date. Can be either a Javascript date object or a string date in a
19450 * valid format (defaults to null).
19454 * @cfg {String} minText
19455 * The error text to display when the date in the cell is before minValue (defaults to
19456 * 'The date in this field must be after {minValue}').
19458 minText : "The date in this field must be equal to or after {0}",
19460 * @cfg {String} maxText
19461 * The error text to display when the date in the cell is after maxValue (defaults to
19462 * 'The date in this field must be before {maxValue}').
19464 maxText : "The date in this field must be equal to or before {0}",
19466 * @cfg {String} invalidText
19467 * The error text to display when the date in the field is invalid (defaults to
19468 * '{value} is not a valid date - it must be in the format {format}').
19470 invalidText : "{0} is not a valid date - it must be in the format {1}",
19472 * @cfg {String} triggerClass
19473 * An additional CSS class used to style the trigger button. The trigger will always get the
19474 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
19475 * which displays a calendar icon).
19477 triggerClass : 'x-form-date-trigger',
19481 * @cfg {Boolean} useIso
19482 * if enabled, then the date field will use a hidden field to store the
19483 * real value as iso formated date. default (false)
19487 * @cfg {String/Object} autoCreate
19488 * A DomHelper element spec, or true for a default element spec (defaults to
19489 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
19492 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
19495 hiddenField: false,
19497 onRender : function(ct, position)
19499 Roo.form.DateField.superclass.onRender.call(this, ct, position);
19501 //this.el.dom.removeAttribute('name');
19502 Roo.log("Changing name?");
19503 this.el.dom.setAttribute('name', this.name + '____hidden___' );
19504 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
19506 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
19507 // prevent input submission
19508 this.hiddenName = this.name;
19515 validateValue : function(value)
19517 value = this.formatDate(value);
19518 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
19519 Roo.log('super failed');
19522 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
19525 var svalue = value;
19526 value = this.parseDate(value);
19528 Roo.log('parse date failed' + svalue);
19529 this.markInvalid(String.format(this.invalidText, svalue, this.format));
19532 var time = value.getTime();
19533 if(this.minValue && time < this.minValue.getTime()){
19534 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
19537 if(this.maxValue && time > this.maxValue.getTime()){
19538 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
19541 if(this.disabledDays){
19542 var day = value.getDay();
19543 for(var i = 0; i < this.disabledDays.length; i++) {
19544 if(day === this.disabledDays[i]){
19545 this.markInvalid(this.disabledDaysText);
19550 var fvalue = this.formatDate(value);
19551 if(this.ddMatch && this.ddMatch.test(fvalue)){
19552 this.markInvalid(String.format(this.disabledDatesText, fvalue));
19559 // Provides logic to override the default TriggerField.validateBlur which just returns true
19560 validateBlur : function(){
19561 return !this.menu || !this.menu.isVisible();
19564 getName: function()
19566 // returns hidden if it's set..
19567 if (!this.rendered) {return ''};
19568 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19573 * Returns the current date value of the date field.
19574 * @return {Date} The date value
19576 getValue : function(){
19578 return this.hiddenField ?
19579 this.hiddenField.value :
19580 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
19584 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
19585 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
19586 * (the default format used is "m/d/y").
19589 //All of these calls set the same date value (May 4, 2006)
19591 //Pass a date object:
19592 var dt = new Date('5/4/06');
19593 dateField.setValue(dt);
19595 //Pass a date string (default format):
19596 dateField.setValue('5/4/06');
19598 //Pass a date string (custom format):
19599 dateField.format = 'Y-m-d';
19600 dateField.setValue('2006-5-4');
19602 * @param {String/Date} date The date or valid date string
19604 setValue : function(date){
19605 if (this.hiddenField) {
19606 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
19608 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
19609 // make sure the value field is always stored as a date..
19610 this.value = this.parseDate(date);
19616 parseDate : function(value){
19618 if (value instanceof Date) {
19619 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
19626 if(!value || value instanceof Date){
19629 var v = Date.parseDate(value, this.format);
19630 if (!v && this.useIso) {
19631 v = Date.parseDate(value, 'Y-m-d');
19633 if(!v && this.altFormats){
19634 if(!this.altFormatsArray){
19635 this.altFormatsArray = this.altFormats.split("|");
19637 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19638 v = Date.parseDate(value, this.altFormatsArray[i]);
19641 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
19648 formatDate : function(date, fmt){
19649 return (!date || !(date instanceof Date)) ?
19650 date : date.dateFormat(fmt || this.format);
19655 select: function(m, d){
19658 this.fireEvent('select', this, d);
19660 show : function(){ // retain focus styling
19664 this.focus.defer(10, this);
19665 var ml = this.menuListeners;
19666 this.menu.un("select", ml.select, this);
19667 this.menu.un("show", ml.show, this);
19668 this.menu.un("hide", ml.hide, this);
19673 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
19674 onTriggerClick : function(){
19678 if(this.menu == null){
19679 this.menu = new Roo.menu.DateMenu();
19681 Roo.apply(this.menu.picker, {
19682 showClear: this.allowBlank,
19683 minDate : this.minValue,
19684 maxDate : this.maxValue,
19685 disabledDatesRE : this.ddMatch,
19686 disabledDatesText : this.disabledDatesText,
19687 disabledDays : this.disabledDays,
19688 disabledDaysText : this.disabledDaysText,
19689 format : this.useIso ? 'Y-m-d' : this.format,
19690 minText : String.format(this.minText, this.formatDate(this.minValue)),
19691 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
19693 this.menu.on(Roo.apply({}, this.menuListeners, {
19696 this.menu.picker.setValue(this.getValue() || new Date());
19697 this.menu.show(this.el, "tl-bl?");
19700 beforeBlur : function(){
19701 var v = this.parseDate(this.getRawValue());
19711 isDirty : function() {
19712 if(this.disabled) {
19716 if(typeof(this.startValue) === 'undefined'){
19720 return String(this.getValue()) !== String(this.startValue);
19724 cleanLeadingSpace : function(e)
19731 * Ext JS Library 1.1.1
19732 * Copyright(c) 2006-2007, Ext JS, LLC.
19734 * Originally Released Under LGPL - original licence link has changed is not relivant.
19737 * <script type="text/javascript">
19741 * @class Roo.form.MonthField
19742 * @extends Roo.form.TriggerField
19743 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
19745 * Create a new MonthField
19746 * @param {Object} config
19748 Roo.form.MonthField = function(config){
19750 Roo.form.MonthField.superclass.constructor.call(this, config);
19756 * Fires when a date is selected
19757 * @param {Roo.form.MonthFieeld} combo This combo box
19758 * @param {Date} date The date selected
19765 if(typeof this.minValue == "string") {
19766 this.minValue = this.parseDate(this.minValue);
19768 if(typeof this.maxValue == "string") {
19769 this.maxValue = this.parseDate(this.maxValue);
19771 this.ddMatch = null;
19772 if(this.disabledDates){
19773 var dd = this.disabledDates;
19775 for(var i = 0; i < dd.length; i++){
19777 if(i != dd.length-1) {
19781 this.ddMatch = new RegExp(re + ")");
19785 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
19787 * @cfg {String} format
19788 * The default date format string which can be overriden for localization support. The format must be
19789 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19793 * @cfg {String} altFormats
19794 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19795 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19797 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
19799 * @cfg {Array} disabledDays
19800 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
19802 disabledDays : [0,1,2,3,4,5,6],
19804 * @cfg {String} disabledDaysText
19805 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
19807 disabledDaysText : "Disabled",
19809 * @cfg {Array} disabledDates
19810 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
19811 * expression so they are very powerful. Some examples:
19813 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
19814 * <li>["03/08", "09/16"] would disable those days for every year</li>
19815 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
19816 * <li>["03/../2006"] would disable every day in March 2006</li>
19817 * <li>["^03"] would disable every day in every March</li>
19819 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
19820 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
19822 disabledDates : null,
19824 * @cfg {String} disabledDatesText
19825 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
19827 disabledDatesText : "Disabled",
19829 * @cfg {Date/String} minValue
19830 * The minimum allowed date. Can be either a Javascript date object or a string date in a
19831 * valid format (defaults to null).
19835 * @cfg {Date/String} maxValue
19836 * The maximum allowed date. Can be either a Javascript date object or a string date in a
19837 * valid format (defaults to null).
19841 * @cfg {String} minText
19842 * The error text to display when the date in the cell is before minValue (defaults to
19843 * 'The date in this field must be after {minValue}').
19845 minText : "The date in this field must be equal to or after {0}",
19847 * @cfg {String} maxTextf
19848 * The error text to display when the date in the cell is after maxValue (defaults to
19849 * 'The date in this field must be before {maxValue}').
19851 maxText : "The date in this field must be equal to or before {0}",
19853 * @cfg {String} invalidText
19854 * The error text to display when the date in the field is invalid (defaults to
19855 * '{value} is not a valid date - it must be in the format {format}').
19857 invalidText : "{0} is not a valid date - it must be in the format {1}",
19859 * @cfg {String} triggerClass
19860 * An additional CSS class used to style the trigger button. The trigger will always get the
19861 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
19862 * which displays a calendar icon).
19864 triggerClass : 'x-form-date-trigger',
19868 * @cfg {Boolean} useIso
19869 * if enabled, then the date field will use a hidden field to store the
19870 * real value as iso formated date. default (true)
19874 * @cfg {String/Object} autoCreate
19875 * A DomHelper element spec, or true for a default element spec (defaults to
19876 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
19879 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
19882 hiddenField: false,
19884 hideMonthPicker : false,
19886 onRender : function(ct, position)
19888 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
19890 this.el.dom.removeAttribute('name');
19891 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
19893 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
19894 // prevent input submission
19895 this.hiddenName = this.name;
19902 validateValue : function(value)
19904 value = this.formatDate(value);
19905 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
19908 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
19911 var svalue = value;
19912 value = this.parseDate(value);
19914 this.markInvalid(String.format(this.invalidText, svalue, this.format));
19917 var time = value.getTime();
19918 if(this.minValue && time < this.minValue.getTime()){
19919 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
19922 if(this.maxValue && time > this.maxValue.getTime()){
19923 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
19926 /*if(this.disabledDays){
19927 var day = value.getDay();
19928 for(var i = 0; i < this.disabledDays.length; i++) {
19929 if(day === this.disabledDays[i]){
19930 this.markInvalid(this.disabledDaysText);
19936 var fvalue = this.formatDate(value);
19937 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
19938 this.markInvalid(String.format(this.disabledDatesText, fvalue));
19946 // Provides logic to override the default TriggerField.validateBlur which just returns true
19947 validateBlur : function(){
19948 return !this.menu || !this.menu.isVisible();
19952 * Returns the current date value of the date field.
19953 * @return {Date} The date value
19955 getValue : function(){
19959 return this.hiddenField ?
19960 this.hiddenField.value :
19961 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
19965 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
19966 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
19967 * (the default format used is "m/d/y").
19970 //All of these calls set the same date value (May 4, 2006)
19972 //Pass a date object:
19973 var dt = new Date('5/4/06');
19974 monthField.setValue(dt);
19976 //Pass a date string (default format):
19977 monthField.setValue('5/4/06');
19979 //Pass a date string (custom format):
19980 monthField.format = 'Y-m-d';
19981 monthField.setValue('2006-5-4');
19983 * @param {String/Date} date The date or valid date string
19985 setValue : function(date){
19986 Roo.log('month setValue' + date);
19987 // can only be first of month..
19989 var val = this.parseDate(date);
19991 if (this.hiddenField) {
19992 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
19994 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
19995 this.value = this.parseDate(date);
19999 parseDate : function(value){
20000 if(!value || value instanceof Date){
20001 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
20004 var v = Date.parseDate(value, this.format);
20005 if (!v && this.useIso) {
20006 v = Date.parseDate(value, 'Y-m-d');
20010 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
20014 if(!v && this.altFormats){
20015 if(!this.altFormatsArray){
20016 this.altFormatsArray = this.altFormats.split("|");
20018 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20019 v = Date.parseDate(value, this.altFormatsArray[i]);
20026 formatDate : function(date, fmt){
20027 return (!date || !(date instanceof Date)) ?
20028 date : date.dateFormat(fmt || this.format);
20033 select: function(m, d){
20035 this.fireEvent('select', this, d);
20037 show : function(){ // retain focus styling
20041 this.focus.defer(10, this);
20042 var ml = this.menuListeners;
20043 this.menu.un("select", ml.select, this);
20044 this.menu.un("show", ml.show, this);
20045 this.menu.un("hide", ml.hide, this);
20049 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
20050 onTriggerClick : function(){
20054 if(this.menu == null){
20055 this.menu = new Roo.menu.DateMenu();
20059 Roo.apply(this.menu.picker, {
20061 showClear: this.allowBlank,
20062 minDate : this.minValue,
20063 maxDate : this.maxValue,
20064 disabledDatesRE : this.ddMatch,
20065 disabledDatesText : this.disabledDatesText,
20067 format : this.useIso ? 'Y-m-d' : this.format,
20068 minText : String.format(this.minText, this.formatDate(this.minValue)),
20069 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
20072 this.menu.on(Roo.apply({}, this.menuListeners, {
20080 // hide month picker get's called when we called by 'before hide';
20082 var ignorehide = true;
20083 p.hideMonthPicker = function(disableAnim){
20087 if(this.monthPicker){
20088 Roo.log("hideMonthPicker called");
20089 if(disableAnim === true){
20090 this.monthPicker.hide();
20092 this.monthPicker.slideOut('t', {duration:.2});
20093 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
20094 p.fireEvent("select", this, this.value);
20100 Roo.log('picker set value');
20101 Roo.log(this.getValue());
20102 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
20103 m.show(this.el, 'tl-bl?');
20104 ignorehide = false;
20105 // this will trigger hideMonthPicker..
20108 // hidden the day picker
20109 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
20115 p.showMonthPicker.defer(100, p);
20121 beforeBlur : function(){
20122 var v = this.parseDate(this.getRawValue());
20128 /** @cfg {Boolean} grow @hide */
20129 /** @cfg {Number} growMin @hide */
20130 /** @cfg {Number} growMax @hide */
20137 * Ext JS Library 1.1.1
20138 * Copyright(c) 2006-2007, Ext JS, LLC.
20140 * Originally Released Under LGPL - original licence link has changed is not relivant.
20143 * <script type="text/javascript">
20148 * @class Roo.form.ComboBox
20149 * @extends Roo.form.TriggerField
20150 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
20152 * Create a new ComboBox.
20153 * @param {Object} config Configuration options
20155 Roo.form.ComboBox = function(config){
20156 Roo.form.ComboBox.superclass.constructor.call(this, config);
20160 * Fires when the dropdown list is expanded
20161 * @param {Roo.form.ComboBox} combo This combo box
20166 * Fires when the dropdown list is collapsed
20167 * @param {Roo.form.ComboBox} combo This combo box
20171 * @event beforeselect
20172 * Fires before a list item is selected. Return false to cancel the selection.
20173 * @param {Roo.form.ComboBox} combo This combo box
20174 * @param {Roo.data.Record} record The data record returned from the underlying store
20175 * @param {Number} index The index of the selected item in the dropdown list
20177 'beforeselect' : true,
20180 * Fires when a list item is selected
20181 * @param {Roo.form.ComboBox} combo This combo box
20182 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
20183 * @param {Number} index The index of the selected item in the dropdown list
20187 * @event beforequery
20188 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
20189 * The event object passed has these properties:
20190 * @param {Roo.form.ComboBox} combo This combo box
20191 * @param {String} query The query
20192 * @param {Boolean} forceAll true to force "all" query
20193 * @param {Boolean} cancel true to cancel the query
20194 * @param {Object} e The query event object
20196 'beforequery': true,
20199 * Fires when the 'add' icon is pressed (add a listener to enable add button)
20200 * @param {Roo.form.ComboBox} combo This combo box
20205 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
20206 * @param {Roo.form.ComboBox} combo This combo box
20207 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
20213 if(this.transform){
20214 this.allowDomMove = false;
20215 var s = Roo.getDom(this.transform);
20216 if(!this.hiddenName){
20217 this.hiddenName = s.name;
20220 this.mode = 'local';
20221 var d = [], opts = s.options;
20222 for(var i = 0, len = opts.length;i < len; i++){
20224 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
20226 this.value = value;
20228 d.push([value, o.text]);
20230 this.store = new Roo.data.SimpleStore({
20232 fields: ['value', 'text'],
20235 this.valueField = 'value';
20236 this.displayField = 'text';
20238 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
20239 if(!this.lazyRender){
20240 this.target = true;
20241 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
20242 s.parentNode.removeChild(s); // remove it
20243 this.render(this.el.parentNode);
20245 s.parentNode.removeChild(s); // remove it
20250 this.store = Roo.factory(this.store, Roo.data);
20253 this.selectedIndex = -1;
20254 if(this.mode == 'local'){
20255 if(config.queryDelay === undefined){
20256 this.queryDelay = 10;
20258 if(config.minChars === undefined){
20264 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
20266 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
20269 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
20270 * rendering into an Roo.Editor, defaults to false)
20273 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
20274 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
20277 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
20280 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
20281 * the dropdown list (defaults to undefined, with no header element)
20285 * @cfg {String/Roo.Template} tpl The template to use to render the output
20289 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
20291 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
20293 listWidth: undefined,
20295 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
20296 * mode = 'remote' or 'text' if mode = 'local')
20298 displayField: undefined,
20300 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
20301 * mode = 'remote' or 'value' if mode = 'local').
20302 * Note: use of a valueField requires the user make a selection
20303 * in order for a value to be mapped.
20305 valueField: undefined,
20309 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
20310 * field's data value (defaults to the underlying DOM element's name)
20312 hiddenName: undefined,
20314 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
20318 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
20320 selectedClass: 'x-combo-selected',
20322 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
20323 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
20324 * which displays a downward arrow icon).
20326 triggerClass : 'x-form-arrow-trigger',
20328 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
20332 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
20333 * anchor positions (defaults to 'tl-bl')
20335 listAlign: 'tl-bl?',
20337 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
20341 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
20342 * query specified by the allQuery config option (defaults to 'query')
20344 triggerAction: 'query',
20346 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
20347 * (defaults to 4, does not apply if editable = false)
20351 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
20352 * delay (typeAheadDelay) if it matches a known value (defaults to false)
20356 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
20357 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
20361 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
20362 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
20366 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
20367 * when editable = true (defaults to false)
20369 selectOnFocus:false,
20371 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
20373 queryParam: 'query',
20375 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
20376 * when mode = 'remote' (defaults to 'Loading...')
20378 loadingText: 'Loading...',
20380 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
20384 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
20388 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
20389 * traditional select (defaults to true)
20393 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
20397 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
20401 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
20402 * listWidth has a higher value)
20406 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
20407 * allow the user to set arbitrary text into the field (defaults to false)
20409 forceSelection:false,
20411 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
20412 * if typeAhead = true (defaults to 250)
20414 typeAheadDelay : 250,
20416 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
20417 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
20419 valueNotFoundText : undefined,
20421 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
20423 blockFocus : false,
20426 * @cfg {Boolean} disableClear Disable showing of clear button.
20428 disableClear : false,
20430 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
20432 alwaysQuery : false,
20438 // element that contains real text value.. (when hidden is used..)
20441 onRender : function(ct, position)
20443 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
20445 if(this.hiddenName){
20446 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
20448 this.hiddenField.value =
20449 this.hiddenValue !== undefined ? this.hiddenValue :
20450 this.value !== undefined ? this.value : '';
20452 // prevent input submission
20453 this.el.dom.removeAttribute('name');
20459 this.el.dom.setAttribute('autocomplete', 'off');
20462 var cls = 'x-combo-list';
20464 this.list = new Roo.Layer({
20465 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20468 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20469 this.list.setWidth(lw);
20470 this.list.swallowEvent('mousewheel');
20471 this.assetHeight = 0;
20474 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20475 this.assetHeight += this.header.getHeight();
20478 this.innerList = this.list.createChild({cls:cls+'-inner'});
20479 this.innerList.on('mouseover', this.onViewOver, this);
20480 this.innerList.on('mousemove', this.onViewMove, this);
20481 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
20483 if(this.allowBlank && !this.pageSize && !this.disableClear){
20484 this.footer = this.list.createChild({cls:cls+'-ft'});
20485 this.pageTb = new Roo.Toolbar(this.footer);
20489 this.footer = this.list.createChild({cls:cls+'-ft'});
20490 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
20491 {pageSize: this.pageSize});
20495 if (this.pageTb && this.allowBlank && !this.disableClear) {
20497 this.pageTb.add(new Roo.Toolbar.Fill(), {
20498 cls: 'x-btn-icon x-btn-clear',
20500 handler: function()
20503 _this.clearValue();
20504 _this.onSelect(false, -1);
20509 this.assetHeight += this.footer.getHeight();
20514 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
20517 this.view = new Roo.View(this.innerList, this.tpl, {
20520 selectedClass: this.selectedClass
20523 this.view.on('click', this.onViewClick, this);
20525 this.store.on('beforeload', this.onBeforeLoad, this);
20526 this.store.on('load', this.onLoad, this);
20527 this.store.on('loadexception', this.onLoadException, this);
20529 if(this.resizable){
20530 this.resizer = new Roo.Resizable(this.list, {
20531 pinned:true, handles:'se'
20533 this.resizer.on('resize', function(r, w, h){
20534 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
20535 this.listWidth = w;
20536 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
20537 this.restrictHeight();
20539 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
20541 if(!this.editable){
20542 this.editable = true;
20543 this.setEditable(false);
20547 if (typeof(this.events.add.listeners) != 'undefined') {
20549 this.addicon = this.wrap.createChild(
20550 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
20552 this.addicon.on('click', function(e) {
20553 this.fireEvent('add', this);
20556 if (typeof(this.events.edit.listeners) != 'undefined') {
20558 this.editicon = this.wrap.createChild(
20559 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
20560 if (this.addicon) {
20561 this.editicon.setStyle('margin-left', '40px');
20563 this.editicon.on('click', function(e) {
20565 // we fire even if inothing is selected..
20566 this.fireEvent('edit', this, this.lastData );
20576 initEvents : function(){
20577 Roo.form.ComboBox.superclass.initEvents.call(this);
20579 this.keyNav = new Roo.KeyNav(this.el, {
20580 "up" : function(e){
20581 this.inKeyMode = true;
20585 "down" : function(e){
20586 if(!this.isExpanded()){
20587 this.onTriggerClick();
20589 this.inKeyMode = true;
20594 "enter" : function(e){
20595 this.onViewClick();
20599 "esc" : function(e){
20603 "tab" : function(e){
20604 this.onViewClick(false);
20605 this.fireEvent("specialkey", this, e);
20611 doRelay : function(foo, bar, hname){
20612 if(hname == 'down' || this.scope.isExpanded()){
20613 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
20620 this.queryDelay = Math.max(this.queryDelay || 10,
20621 this.mode == 'local' ? 10 : 250);
20622 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
20623 if(this.typeAhead){
20624 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
20626 if(this.editable !== false){
20627 this.el.on("keyup", this.onKeyUp, this);
20629 if(this.forceSelection){
20630 this.on('blur', this.doForce, this);
20634 onDestroy : function(){
20636 this.view.setStore(null);
20637 this.view.el.removeAllListeners();
20638 this.view.el.remove();
20639 this.view.purgeListeners();
20642 this.list.destroy();
20645 this.store.un('beforeload', this.onBeforeLoad, this);
20646 this.store.un('load', this.onLoad, this);
20647 this.store.un('loadexception', this.onLoadException, this);
20649 Roo.form.ComboBox.superclass.onDestroy.call(this);
20653 fireKey : function(e){
20654 if(e.isNavKeyPress() && !this.list.isVisible()){
20655 this.fireEvent("specialkey", this, e);
20660 onResize: function(w, h){
20661 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
20663 if(typeof w != 'number'){
20664 // we do not handle it!?!?
20667 var tw = this.trigger.getWidth();
20668 tw += this.addicon ? this.addicon.getWidth() : 0;
20669 tw += this.editicon ? this.editicon.getWidth() : 0;
20671 this.el.setWidth( this.adjustWidth('input', x));
20673 this.trigger.setStyle('left', x+'px');
20675 if(this.list && this.listWidth === undefined){
20676 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
20677 this.list.setWidth(lw);
20678 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
20686 * Allow or prevent the user from directly editing the field text. If false is passed,
20687 * the user will only be able to select from the items defined in the dropdown list. This method
20688 * is the runtime equivalent of setting the 'editable' config option at config time.
20689 * @param {Boolean} value True to allow the user to directly edit the field text
20691 setEditable : function(value){
20692 if(value == this.editable){
20695 this.editable = value;
20697 this.el.dom.setAttribute('readOnly', true);
20698 this.el.on('mousedown', this.onTriggerClick, this);
20699 this.el.addClass('x-combo-noedit');
20701 this.el.dom.setAttribute('readOnly', false);
20702 this.el.un('mousedown', this.onTriggerClick, this);
20703 this.el.removeClass('x-combo-noedit');
20708 onBeforeLoad : function(){
20709 if(!this.hasFocus){
20712 this.innerList.update(this.loadingText ?
20713 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20714 this.restrictHeight();
20715 this.selectedIndex = -1;
20719 onLoad : function(){
20720 if(!this.hasFocus){
20723 if(this.store.getCount() > 0){
20725 this.restrictHeight();
20726 if(this.lastQuery == this.allQuery){
20728 this.el.dom.select();
20730 if(!this.selectByValue(this.value, true)){
20731 this.select(0, true);
20735 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
20736 this.taTask.delay(this.typeAheadDelay);
20740 this.onEmptyResults();
20745 onLoadException : function()
20748 Roo.log(this.store.reader.jsonData);
20749 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20750 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20756 onTypeAhead : function(){
20757 if(this.store.getCount() > 0){
20758 var r = this.store.getAt(0);
20759 var newValue = r.data[this.displayField];
20760 var len = newValue.length;
20761 var selStart = this.getRawValue().length;
20762 if(selStart != len){
20763 this.setRawValue(newValue);
20764 this.selectText(selStart, newValue.length);
20770 onSelect : function(record, index){
20771 if(this.fireEvent('beforeselect', this, record, index) !== false){
20772 this.setFromData(index > -1 ? record.data : false);
20774 this.fireEvent('select', this, record, index);
20779 * Returns the currently selected field value or empty string if no value is set.
20780 * @return {String} value The selected value
20782 getValue : function(){
20783 if(this.valueField){
20784 return typeof this.value != 'undefined' ? this.value : '';
20786 return Roo.form.ComboBox.superclass.getValue.call(this);
20790 * Clears any text/value currently set in the field
20792 clearValue : function(){
20793 if(this.hiddenField){
20794 this.hiddenField.value = '';
20797 this.setRawValue('');
20798 this.lastSelectionText = '';
20803 * Sets the specified value into the field. If the value finds a match, the corresponding record text
20804 * will be displayed in the field. If the value does not match the data value of an existing item,
20805 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
20806 * Otherwise the field will be blank (although the value will still be set).
20807 * @param {String} value The value to match
20809 setValue : function(v){
20811 if(this.valueField){
20812 var r = this.findRecord(this.valueField, v);
20814 text = r.data[this.displayField];
20815 }else if(this.valueNotFoundText !== undefined){
20816 text = this.valueNotFoundText;
20819 this.lastSelectionText = text;
20820 if(this.hiddenField){
20821 this.hiddenField.value = v;
20823 Roo.form.ComboBox.superclass.setValue.call(this, text);
20827 * @property {Object} the last set data for the element
20832 * Sets the value of the field based on a object which is related to the record format for the store.
20833 * @param {Object} value the value to set as. or false on reset?
20835 setFromData : function(o){
20836 var dv = ''; // display value
20837 var vv = ''; // value value..
20839 if (this.displayField) {
20840 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
20842 // this is an error condition!!!
20843 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
20846 if(this.valueField){
20847 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
20849 if(this.hiddenField){
20850 this.hiddenField.value = vv;
20852 this.lastSelectionText = dv;
20853 Roo.form.ComboBox.superclass.setValue.call(this, dv);
20857 // no hidden field.. - we store the value in 'value', but still display
20858 // display field!!!!
20859 this.lastSelectionText = dv;
20860 Roo.form.ComboBox.superclass.setValue.call(this, dv);
20866 reset : function(){
20867 // overridden so that last data is reset..
20868 this.setValue(this.resetValue);
20869 this.originalValue = this.getValue();
20870 this.clearInvalid();
20871 this.lastData = false;
20873 this.view.clearSelections();
20877 findRecord : function(prop, value){
20879 if(this.store.getCount() > 0){
20880 this.store.each(function(r){
20881 if(r.data[prop] == value){
20891 getName: function()
20893 // returns hidden if it's set..
20894 if (!this.rendered) {return ''};
20895 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20899 onViewMove : function(e, t){
20900 this.inKeyMode = false;
20904 onViewOver : function(e, t){
20905 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
20908 var item = this.view.findItemFromChild(t);
20910 var index = this.view.indexOf(item);
20911 this.select(index, false);
20916 onViewClick : function(doFocus)
20918 var index = this.view.getSelectedIndexes()[0];
20919 var r = this.store.getAt(index);
20921 this.onSelect(r, index);
20923 if(doFocus !== false && !this.blockFocus){
20929 restrictHeight : function(){
20930 this.innerList.dom.style.height = '';
20931 var inner = this.innerList.dom;
20932 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20933 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20934 this.list.beginUpdate();
20935 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
20936 this.list.alignTo(this.el, this.listAlign);
20937 this.list.endUpdate();
20941 onEmptyResults : function(){
20946 * Returns true if the dropdown list is expanded, else false.
20948 isExpanded : function(){
20949 return this.list.isVisible();
20953 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
20954 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
20955 * @param {String} value The data value of the item to select
20956 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
20957 * selected item if it is not currently in view (defaults to true)
20958 * @return {Boolean} True if the value matched an item in the list, else false
20960 selectByValue : function(v, scrollIntoView){
20961 if(v !== undefined && v !== null){
20962 var r = this.findRecord(this.valueField || this.displayField, v);
20964 this.select(this.store.indexOf(r), scrollIntoView);
20972 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
20973 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
20974 * @param {Number} index The zero-based index of the list item to select
20975 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
20976 * selected item if it is not currently in view (defaults to true)
20978 select : function(index, scrollIntoView){
20979 this.selectedIndex = index;
20980 this.view.select(index);
20981 if(scrollIntoView !== false){
20982 var el = this.view.getNode(index);
20984 this.innerList.scrollChildIntoView(el, false);
20990 selectNext : function(){
20991 var ct = this.store.getCount();
20993 if(this.selectedIndex == -1){
20995 }else if(this.selectedIndex < ct-1){
20996 this.select(this.selectedIndex+1);
21002 selectPrev : function(){
21003 var ct = this.store.getCount();
21005 if(this.selectedIndex == -1){
21007 }else if(this.selectedIndex != 0){
21008 this.select(this.selectedIndex-1);
21014 onKeyUp : function(e){
21015 if(this.editable !== false && !e.isSpecialKey()){
21016 this.lastKey = e.getKey();
21017 this.dqTask.delay(this.queryDelay);
21022 validateBlur : function(){
21023 return !this.list || !this.list.isVisible();
21027 initQuery : function(){
21028 this.doQuery(this.getRawValue());
21032 doForce : function(){
21033 if(this.el.dom.value.length > 0){
21034 this.el.dom.value =
21035 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
21041 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
21042 * query allowing the query action to be canceled if needed.
21043 * @param {String} query The SQL query to execute
21044 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
21045 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
21046 * saved in the current store (defaults to false)
21048 doQuery : function(q, forceAll){
21049 if(q === undefined || q === null){
21054 forceAll: forceAll,
21058 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
21062 forceAll = qe.forceAll;
21063 if(forceAll === true || (q.length >= this.minChars)){
21064 if(this.lastQuery != q || this.alwaysQuery){
21065 this.lastQuery = q;
21066 if(this.mode == 'local'){
21067 this.selectedIndex = -1;
21069 this.store.clearFilter();
21071 this.store.filter(this.displayField, q);
21075 this.store.baseParams[this.queryParam] = q;
21077 params: this.getParams(q)
21082 this.selectedIndex = -1;
21089 getParams : function(q){
21091 //p[this.queryParam] = q;
21094 p.limit = this.pageSize;
21100 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
21102 collapse : function(){
21103 if(!this.isExpanded()){
21107 Roo.get(document).un('mousedown', this.collapseIf, this);
21108 Roo.get(document).un('mousewheel', this.collapseIf, this);
21109 if (!this.editable) {
21110 Roo.get(document).un('keydown', this.listKeyPress, this);
21112 this.fireEvent('collapse', this);
21116 collapseIf : function(e){
21117 if(!e.within(this.wrap) && !e.within(this.list)){
21123 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
21125 expand : function(){
21126 if(this.isExpanded() || !this.hasFocus){
21129 this.list.alignTo(this.el, this.listAlign);
21131 Roo.get(document).on('mousedown', this.collapseIf, this);
21132 Roo.get(document).on('mousewheel', this.collapseIf, this);
21133 if (!this.editable) {
21134 Roo.get(document).on('keydown', this.listKeyPress, this);
21137 this.fireEvent('expand', this);
21141 // Implements the default empty TriggerField.onTriggerClick function
21142 onTriggerClick : function(){
21146 if(this.isExpanded()){
21148 if (!this.blockFocus) {
21153 this.hasFocus = true;
21154 if(this.triggerAction == 'all') {
21155 this.doQuery(this.allQuery, true);
21157 this.doQuery(this.getRawValue());
21159 if (!this.blockFocus) {
21164 listKeyPress : function(e)
21166 //Roo.log('listkeypress');
21167 // scroll to first matching element based on key pres..
21168 if (e.isSpecialKey()) {
21171 var k = String.fromCharCode(e.getKey()).toUpperCase();
21174 var csel = this.view.getSelectedNodes();
21175 var cselitem = false;
21177 var ix = this.view.indexOf(csel[0]);
21178 cselitem = this.store.getAt(ix);
21179 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
21185 this.store.each(function(v) {
21187 // start at existing selection.
21188 if (cselitem.id == v.id) {
21194 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
21195 match = this.store.indexOf(v);
21200 if (match === false) {
21201 return true; // no more action?
21204 this.view.select(match);
21205 var sn = Roo.get(this.view.getSelectedNodes()[0]);
21206 sn.scrollIntoView(sn.dom.parentNode, false);
21210 * @cfg {Boolean} grow
21214 * @cfg {Number} growMin
21218 * @cfg {Number} growMax
21226 * Copyright(c) 2010-2012, Roo J Solutions Limited
21233 * @class Roo.form.ComboBoxArray
21234 * @extends Roo.form.TextField
21235 * A facebook style adder... for lists of email / people / countries etc...
21236 * pick multiple items from a combo box, and shows each one.
21238 * Fred [x] Brian [x] [Pick another |v]
21241 * For this to work: it needs various extra information
21242 * - normal combo problay has
21244 * + displayField, valueField
21246 * For our purpose...
21249 * If we change from 'extends' to wrapping...
21256 * Create a new ComboBoxArray.
21257 * @param {Object} config Configuration options
21261 Roo.form.ComboBoxArray = function(config)
21265 * @event beforeremove
21266 * Fires before remove the value from the list
21267 * @param {Roo.form.ComboBoxArray} _self This combo box array
21268 * @param {Roo.form.ComboBoxArray.Item} item removed item
21270 'beforeremove' : true,
21273 * Fires when remove the value from the list
21274 * @param {Roo.form.ComboBoxArray} _self This combo box array
21275 * @param {Roo.form.ComboBoxArray.Item} item removed item
21282 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
21284 this.items = new Roo.util.MixedCollection(false);
21286 // construct the child combo...
21296 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
21299 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
21304 // behavies liek a hiddne field
21305 inputType: 'hidden',
21307 * @cfg {Number} width The width of the box that displays the selected element
21314 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
21318 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
21320 hiddenName : false,
21322 * @cfg {String} seperator The value seperator normally ','
21326 // private the array of items that are displayed..
21328 // private - the hidden field el.
21330 // private - the filed el..
21333 //validateValue : function() { return true; }, // all values are ok!
21334 //onAddClick: function() { },
21336 onRender : function(ct, position)
21339 // create the standard hidden element
21340 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
21343 // give fake names to child combo;
21344 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
21345 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
21347 this.combo = Roo.factory(this.combo, Roo.form);
21348 this.combo.onRender(ct, position);
21349 if (typeof(this.combo.width) != 'undefined') {
21350 this.combo.onResize(this.combo.width,0);
21353 this.combo.initEvents();
21355 // assigned so form know we need to do this..
21356 this.store = this.combo.store;
21357 this.valueField = this.combo.valueField;
21358 this.displayField = this.combo.displayField ;
21361 this.combo.wrap.addClass('x-cbarray-grp');
21363 var cbwrap = this.combo.wrap.createChild(
21364 {tag: 'div', cls: 'x-cbarray-cb'},
21369 this.hiddenEl = this.combo.wrap.createChild({
21370 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
21372 this.el = this.combo.wrap.createChild({
21373 tag: 'input', type:'hidden' , name: this.name, value : ''
21375 // this.el.dom.removeAttribute("name");
21378 this.outerWrap = this.combo.wrap;
21379 this.wrap = cbwrap;
21381 this.outerWrap.setWidth(this.width);
21382 this.outerWrap.dom.removeChild(this.el.dom);
21384 this.wrap.dom.appendChild(this.el.dom);
21385 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
21386 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
21388 this.combo.trigger.setStyle('position','relative');
21389 this.combo.trigger.setStyle('left', '0px');
21390 this.combo.trigger.setStyle('top', '2px');
21392 this.combo.el.setStyle('vertical-align', 'text-bottom');
21394 //this.trigger.setStyle('vertical-align', 'top');
21396 // this should use the code from combo really... on('add' ....)
21400 this.adder = this.outerWrap.createChild(
21401 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
21403 this.adder.on('click', function(e) {
21404 _t.fireEvent('adderclick', this, e);
21408 //this.adder.on('click', this.onAddClick, _t);
21411 this.combo.on('select', function(cb, rec, ix) {
21412 this.addItem(rec.data);
21415 cb.el.dom.value = '';
21416 //cb.lastData = rec.data;
21425 getName: function()
21427 // returns hidden if it's set..
21428 if (!this.rendered) {return ''};
21429 return this.hiddenName ? this.hiddenName : this.name;
21434 onResize: function(w, h){
21437 // not sure if this is needed..
21438 //this.combo.onResize(w,h);
21440 if(typeof w != 'number'){
21441 // we do not handle it!?!?
21444 var tw = this.combo.trigger.getWidth();
21445 tw += this.addicon ? this.addicon.getWidth() : 0;
21446 tw += this.editicon ? this.editicon.getWidth() : 0;
21448 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
21450 this.combo.trigger.setStyle('left', '0px');
21452 if(this.list && this.listWidth === undefined){
21453 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
21454 this.list.setWidth(lw);
21455 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
21462 addItem: function(rec)
21464 var valueField = this.combo.valueField;
21465 var displayField = this.combo.displayField;
21467 if (this.items.indexOfKey(rec[valueField]) > -1) {
21468 //console.log("GOT " + rec.data.id);
21472 var x = new Roo.form.ComboBoxArray.Item({
21473 //id : rec[this.idField],
21475 displayField : displayField ,
21476 tipField : displayField ,
21480 this.items.add(rec[valueField],x);
21481 // add it before the element..
21482 this.updateHiddenEl();
21483 x.render(this.outerWrap, this.wrap.dom);
21484 // add the image handler..
21487 updateHiddenEl : function()
21490 if (!this.hiddenEl) {
21494 var idField = this.combo.valueField;
21496 this.items.each(function(f) {
21497 ar.push(f.data[idField]);
21499 this.hiddenEl.dom.value = ar.join(this.seperator);
21505 this.items.clear();
21507 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
21511 this.el.dom.value = '';
21512 if (this.hiddenEl) {
21513 this.hiddenEl.dom.value = '';
21517 getValue: function()
21519 return this.hiddenEl ? this.hiddenEl.dom.value : '';
21521 setValue: function(v) // not a valid action - must use addItems..
21526 if (this.store.isLocal && (typeof(v) == 'string')) {
21527 // then we can use the store to find the values..
21528 // comma seperated at present.. this needs to allow JSON based encoding..
21529 this.hiddenEl.value = v;
21531 Roo.each(v.split(this.seperator), function(k) {
21532 Roo.log("CHECK " + this.valueField + ',' + k);
21533 var li = this.store.query(this.valueField, k);
21538 add[this.valueField] = k;
21539 add[this.displayField] = li.item(0).data[this.displayField];
21545 if (typeof(v) == 'object' ) {
21546 // then let's assume it's an array of objects..
21547 Roo.each(v, function(l) {
21549 if (typeof(l) == 'string') {
21551 add[this.valueField] = l;
21552 add[this.displayField] = l
21561 setFromData: function(v)
21563 // this recieves an object, if setValues is called.
21565 this.el.dom.value = v[this.displayField];
21566 this.hiddenEl.dom.value = v[this.valueField];
21567 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
21570 var kv = v[this.valueField];
21571 var dv = v[this.displayField];
21572 kv = typeof(kv) != 'string' ? '' : kv;
21573 dv = typeof(dv) != 'string' ? '' : dv;
21576 var keys = kv.split(this.seperator);
21577 var display = dv.split(this.seperator);
21578 for (var i = 0 ; i < keys.length; i++) {
21580 add[this.valueField] = keys[i];
21581 add[this.displayField] = display[i];
21589 * Validates the combox array value
21590 * @return {Boolean} True if the value is valid, else false
21592 validate : function(){
21593 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
21594 this.clearInvalid();
21600 validateValue : function(value){
21601 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
21609 isDirty : function() {
21610 if(this.disabled) {
21615 var d = Roo.decode(String(this.originalValue));
21617 return String(this.getValue()) !== String(this.originalValue);
21620 var originalValue = [];
21622 for (var i = 0; i < d.length; i++){
21623 originalValue.push(d[i][this.valueField]);
21626 return String(this.getValue()) !== String(originalValue.join(this.seperator));
21635 * @class Roo.form.ComboBoxArray.Item
21636 * @extends Roo.BoxComponent
21637 * A selected item in the list
21638 * Fred [x] Brian [x] [Pick another |v]
21641 * Create a new item.
21642 * @param {Object} config Configuration options
21645 Roo.form.ComboBoxArray.Item = function(config) {
21646 config.id = Roo.id();
21647 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
21650 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
21653 displayField : false,
21657 defaultAutoCreate : {
21659 cls: 'x-cbarray-item',
21666 src : Roo.BLANK_IMAGE_URL ,
21674 onRender : function(ct, position)
21676 Roo.form.Field.superclass.onRender.call(this, ct, position);
21679 var cfg = this.getAutoCreate();
21680 this.el = ct.createChild(cfg, position);
21683 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
21685 this.el.child('div').dom.innerHTML = this.cb.renderer ?
21686 this.cb.renderer(this.data) :
21687 String.format('{0}',this.data[this.displayField]);
21690 this.el.child('div').dom.setAttribute('qtip',
21691 String.format('{0}',this.data[this.tipField])
21694 this.el.child('img').on('click', this.remove, this);
21698 remove : function()
21700 if(this.cb.disabled){
21704 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
21705 this.cb.items.remove(this);
21706 this.el.child('img').un('click', this.remove, this);
21708 this.cb.updateHiddenEl();
21710 this.cb.fireEvent('remove', this.cb, this);
21715 * RooJS Library 1.1.1
21716 * Copyright(c) 2008-2011 Alan Knowles
21723 * @class Roo.form.ComboNested
21724 * @extends Roo.form.ComboBox
21725 * A combobox for that allows selection of nested items in a list,
21740 * Create a new ComboNested
21741 * @param {Object} config Configuration options
21743 Roo.form.ComboNested = function(config){
21744 Roo.form.ComboCheck.superclass.constructor.call(this, config);
21745 // should verify some data...
21747 // hiddenName = required..
21748 // displayField = required
21749 // valudField == required
21750 var req= [ 'hiddenName', 'displayField', 'valueField' ];
21752 Roo.each(req, function(e) {
21753 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
21754 throw "Roo.form.ComboNested : missing value for: " + e;
21761 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
21764 * @config {Number} max Number of columns to show
21769 list : null, // the outermost div..
21770 innerLists : null, // the
21774 loadingChildren : false,
21776 onRender : function(ct, position)
21778 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
21780 if(this.hiddenName){
21781 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
21783 this.hiddenField.value =
21784 this.hiddenValue !== undefined ? this.hiddenValue :
21785 this.value !== undefined ? this.value : '';
21787 // prevent input submission
21788 this.el.dom.removeAttribute('name');
21794 this.el.dom.setAttribute('autocomplete', 'off');
21797 var cls = 'x-combo-list';
21799 this.list = new Roo.Layer({
21800 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
21803 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
21804 this.list.setWidth(lw);
21805 this.list.swallowEvent('mousewheel');
21806 this.assetHeight = 0;
21809 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
21810 this.assetHeight += this.header.getHeight();
21812 this.innerLists = [];
21815 for (var i =0 ; i < this.maxColumns; i++) {
21816 this.onRenderList( cls, i);
21819 // always needs footer, as we are going to have an 'OK' button.
21820 this.footer = this.list.createChild({cls:cls+'-ft'});
21821 this.pageTb = new Roo.Toolbar(this.footer);
21826 handler: function()
21832 if ( this.allowBlank && !this.disableClear) {
21834 this.pageTb.add(new Roo.Toolbar.Fill(), {
21835 cls: 'x-btn-icon x-btn-clear',
21837 handler: function()
21840 _this.clearValue();
21841 _this.onSelect(false, -1);
21846 this.assetHeight += this.footer.getHeight();
21850 onRenderList : function ( cls, i)
21853 var lw = Math.floor(
21854 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
21857 this.list.setWidth(lw); // default to '1'
21859 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
21860 //il.on('mouseover', this.onViewOver, this, { list: i });
21861 //il.on('mousemove', this.onViewMove, this, { list: i });
21863 il.setStyle({ 'overflow-x' : 'hidden'});
21866 this.tpl = new Roo.Template({
21867 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
21868 isEmpty: function (value, allValues) {
21870 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
21871 return dl ? 'has-children' : 'no-children'
21876 var store = this.store;
21878 store = new Roo.data.SimpleStore({
21879 //fields : this.store.reader.meta.fields,
21880 reader : this.store.reader,
21884 this.stores[i] = store;
21886 var view = this.views[i] = new Roo.View(
21892 selectedClass: this.selectedClass
21895 view.getEl().setWidth(lw);
21896 view.getEl().setStyle({
21897 position: i < 1 ? 'relative' : 'absolute',
21899 left: (i * lw ) + 'px',
21900 display : i > 0 ? 'none' : 'block'
21902 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
21903 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
21904 //view.on('click', this.onViewClick, this, { list : i });
21906 store.on('beforeload', this.onBeforeLoad, this);
21907 store.on('load', this.onLoad, this, { list : i});
21908 store.on('loadexception', this.onLoadException, this);
21910 // hide the other vies..
21916 restrictHeight : function()
21919 Roo.each(this.innerLists, function(il,i) {
21920 var el = this.views[i].getEl();
21921 el.dom.style.height = '';
21922 var inner = el.dom;
21923 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
21924 // only adjust heights on other ones..
21925 mh = Math.max(h, mh);
21928 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
21929 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
21936 this.list.beginUpdate();
21937 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
21938 this.list.alignTo(this.el, this.listAlign);
21939 this.list.endUpdate();
21944 // -- store handlers..
21946 onBeforeLoad : function()
21948 if(!this.hasFocus){
21951 this.innerLists[0].update(this.loadingText ?
21952 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
21953 this.restrictHeight();
21954 this.selectedIndex = -1;
21957 onLoad : function(a,b,c,d)
21959 if (!this.loadingChildren) {
21960 // then we are loading the top level. - hide the children
21961 for (var i = 1;i < this.views.length; i++) {
21962 this.views[i].getEl().setStyle({ display : 'none' });
21964 var lw = Math.floor(
21965 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
21968 this.list.setWidth(lw); // default to '1'
21972 if(!this.hasFocus){
21976 if(this.store.getCount() > 0) {
21978 this.restrictHeight();
21980 this.onEmptyResults();
21983 if (!this.loadingChildren) {
21984 this.selectActive();
21987 this.stores[1].loadData([]);
21988 this.stores[2].loadData([]);
21997 onLoadException : function()
22000 Roo.log(this.store.reader.jsonData);
22001 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
22002 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
22007 // no cleaning of leading spaces on blur here.
22008 cleanLeadingSpace : function(e) { },
22011 onSelectChange : function (view, sels, opts )
22013 var ix = view.getSelectedIndexes();
22015 if (opts.list > this.maxColumns - 2) {
22016 if (view.store.getCount()< 1) {
22017 this.views[opts.list ].getEl().setStyle({ display : 'none' });
22021 // used to clear ?? but if we are loading unselected
22022 this.setFromData(view.store.getAt(ix[0]).data);
22031 // this get's fired when trigger opens..
22032 // this.setFromData({});
22033 var str = this.stores[opts.list+1];
22034 str.data.clear(); // removeall wihtout the fire events..
22038 var rec = view.store.getAt(ix[0]);
22040 this.setFromData(rec.data);
22041 this.fireEvent('select', this, rec, ix[0]);
22043 var lw = Math.floor(
22045 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
22046 ) / this.maxColumns
22048 this.loadingChildren = true;
22049 this.stores[opts.list+1].loadDataFromChildren( rec );
22050 this.loadingChildren = false;
22051 var dl = this.stores[opts.list+1]. getTotalCount();
22053 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
22055 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
22056 for (var i = opts.list+2; i < this.views.length;i++) {
22057 this.views[i].getEl().setStyle({ display : 'none' });
22060 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
22061 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
22063 if (this.isLoading) {
22064 // this.selectActive(opts.list);
22072 onDoubleClick : function()
22074 this.collapse(); //??
22082 recordToStack : function(store, prop, value, stack)
22084 var cstore = new Roo.data.SimpleStore({
22085 //fields : this.store.reader.meta.fields, // we need array reader.. for
22086 reader : this.store.reader,
22090 var record = false;
22092 if(store.getCount() < 1){
22095 store.each(function(r){
22096 if(r.data[prop] == value){
22101 if (r.data.cn && r.data.cn.length) {
22102 cstore.loadDataFromChildren( r);
22103 var cret = _this.recordToStack(cstore, prop, value, stack);
22104 if (cret !== false) {
22113 if (record == false) {
22116 stack.unshift(srec);
22121 * find the stack of stores that match our value.
22126 selectActive : function ()
22128 // if store is not loaded, then we will need to wait for that to happen first.
22130 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
22131 for (var i = 0; i < stack.length; i++ ) {
22132 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
22144 * Ext JS Library 1.1.1
22145 * Copyright(c) 2006-2007, Ext JS, LLC.
22147 * Originally Released Under LGPL - original licence link has changed is not relivant.
22150 * <script type="text/javascript">
22153 * @class Roo.form.Checkbox
22154 * @extends Roo.form.Field
22155 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
22157 * Creates a new Checkbox
22158 * @param {Object} config Configuration options
22160 Roo.form.Checkbox = function(config){
22161 Roo.form.Checkbox.superclass.constructor.call(this, config);
22165 * Fires when the checkbox is checked or unchecked.
22166 * @param {Roo.form.Checkbox} this This checkbox
22167 * @param {Boolean} checked The new checked value
22173 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
22175 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
22177 focusClass : undefined,
22179 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
22181 fieldClass: "x-form-field",
22183 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
22187 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22188 * {tag: "input", type: "checkbox", autocomplete: "off"})
22190 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
22192 * @cfg {String} boxLabel The text that appears beside the checkbox
22196 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
22200 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22202 valueOff: '0', // value when not checked..
22204 actionMode : 'viewEl',
22207 itemCls : 'x-menu-check-item x-form-item',
22208 groupClass : 'x-menu-group-item',
22209 inputType : 'hidden',
22212 inSetChecked: false, // check that we are not calling self...
22214 inputElement: false, // real input element?
22215 basedOn: false, // ????
22217 isFormField: true, // not sure where this is needed!!!!
22219 onResize : function(){
22220 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
22221 if(!this.boxLabel){
22222 this.el.alignTo(this.wrap, 'c-c');
22226 initEvents : function(){
22227 Roo.form.Checkbox.superclass.initEvents.call(this);
22228 this.el.on("click", this.onClick, this);
22229 this.el.on("change", this.onClick, this);
22233 getResizeEl : function(){
22237 getPositionEl : function(){
22242 onRender : function(ct, position){
22243 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
22245 if(this.inputValue !== undefined){
22246 this.el.dom.value = this.inputValue;
22249 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
22250 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
22251 var viewEl = this.wrap.createChild({
22252 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
22253 this.viewEl = viewEl;
22254 this.wrap.on('click', this.onClick, this);
22256 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
22257 this.el.on('propertychange', this.setFromHidden, this); //ie
22262 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
22263 // viewEl.on('click', this.onClick, this);
22265 //if(this.checked){
22266 this.setChecked(this.checked);
22268 //this.checked = this.el.dom;
22274 initValue : Roo.emptyFn,
22277 * Returns the checked state of the checkbox.
22278 * @return {Boolean} True if checked, else false
22280 getValue : function(){
22282 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
22284 return this.valueOff;
22289 onClick : function(){
22290 if (this.disabled) {
22293 this.setChecked(!this.checked);
22295 //if(this.el.dom.checked != this.checked){
22296 // this.setValue(this.el.dom.checked);
22301 * Sets the checked state of the checkbox.
22302 * On is always based on a string comparison between inputValue and the param.
22303 * @param {Boolean/String} value - the value to set
22304 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
22306 setValue : function(v,suppressEvent){
22309 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
22310 //if(this.el && this.el.dom){
22311 // this.el.dom.checked = this.checked;
22312 // this.el.dom.defaultChecked = this.checked;
22314 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
22315 //this.fireEvent("check", this, this.checked);
22318 setChecked : function(state,suppressEvent)
22320 if (this.inSetChecked) {
22321 this.checked = state;
22327 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
22329 this.checked = state;
22330 if(suppressEvent !== true){
22331 this.fireEvent('check', this, state);
22333 this.inSetChecked = true;
22334 this.el.dom.value = state ? this.inputValue : this.valueOff;
22335 this.inSetChecked = false;
22338 // handle setting of hidden value by some other method!!?!?
22339 setFromHidden: function()
22344 //console.log("SET FROM HIDDEN");
22345 //alert('setFrom hidden');
22346 this.setValue(this.el.dom.value);
22349 onDestroy : function()
22352 Roo.get(this.viewEl).remove();
22355 Roo.form.Checkbox.superclass.onDestroy.call(this);
22358 setBoxLabel : function(str)
22360 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
22365 * Ext JS Library 1.1.1
22366 * Copyright(c) 2006-2007, Ext JS, LLC.
22368 * Originally Released Under LGPL - original licence link has changed is not relivant.
22371 * <script type="text/javascript">
22375 * @class Roo.form.Radio
22376 * @extends Roo.form.Checkbox
22377 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
22378 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
22380 * Creates a new Radio
22381 * @param {Object} config Configuration options
22383 Roo.form.Radio = function(){
22384 Roo.form.Radio.superclass.constructor.apply(this, arguments);
22386 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
22387 inputType: 'radio',
22390 * If this radio is part of a group, it will return the selected value
22393 getGroupValue : function(){
22394 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
22398 onRender : function(ct, position){
22399 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
22401 if(this.inputValue !== undefined){
22402 this.el.dom.value = this.inputValue;
22405 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
22406 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
22407 //var viewEl = this.wrap.createChild({
22408 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
22409 //this.viewEl = viewEl;
22410 //this.wrap.on('click', this.onClick, this);
22412 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
22413 //this.el.on('propertychange', this.setFromHidden, this); //ie
22418 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
22419 // viewEl.on('click', this.onClick, this);
22422 this.el.dom.checked = 'checked' ;
22428 });Roo.rtf = {}; // namespace
22429 Roo.rtf.Hex = function(hex)
22433 Roo.rtf.Paragraph = function(opts)
22435 this.content = []; ///??? is that used?
22436 };Roo.rtf.Span = function(opts)
22438 this.value = opts.value;
22441 Roo.rtf.Group = function(parent)
22443 // we dont want to acutally store parent - it will make debug a nightmare..
22451 Roo.rtf.Group.prototype = {
22455 addContent : function(node) {
22456 // could set styles...
22457 this.content.push(node);
22459 addChild : function(cn)
22463 // only for images really...
22464 toDataURL : function()
22466 var mimetype = false;
22468 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
22469 mimetype = "image/png";
22471 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
22472 mimetype = "image/jpeg";
22475 return 'about:blank'; // ?? error?
22479 var hexstring = this.content[this.content.length-1].value;
22481 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
22482 return String.fromCharCode(parseInt(a, 16));
22487 // this looks like it's normally the {rtf{ .... }}
22488 Roo.rtf.Document = function()
22490 // we dont want to acutally store parent - it will make debug a nightmare..
22496 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
22497 addChild : function(cn)
22501 case 'rtlch': // most content seems to be inside this??
22504 this.rtlch.push(cn);
22507 this[cn.type] = cn;
22512 getElementsByType : function(type)
22515 this._getElementsByType(type, ret, this.cn, 'rtf');
22518 _getElementsByType : function (type, ret, search_array, path)
22520 search_array.forEach(function(n,i) {
22521 if (n.type == type) {
22522 n.path = path + '/' + n.type + ':' + i;
22525 if (n.cn.length > 0) {
22526 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
22533 Roo.rtf.Ctrl = function(opts)
22535 this.value = opts.value;
22536 this.param = opts.param;
22541 * based on this https://github.com/iarna/rtf-parser
22542 * it's really only designed to extract pict from pasted RTF
22546 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
22555 Roo.rtf.Parser = function(text) {
22556 //super({objectMode: true})
22558 this.parserState = this.parseText;
22560 // these are for interpeter...
22562 ///this.parserState = this.parseTop
22563 this.groupStack = [];
22564 this.hexStore = [];
22567 this.groups = []; // where we put the return.
22569 for (var ii = 0; ii < text.length; ++ii) {
22572 if (text[ii] === '\n') {
22578 this.parserState(text[ii]);
22584 Roo.rtf.Parser.prototype = {
22585 text : '', // string being parsed..
22587 controlWordParam : '',
22591 groupStack : false,
22596 row : 1, // reportin?
22600 push : function (el)
22602 var m = 'cmd'+ el.type;
22603 if (typeof(this[m]) == 'undefined') {
22604 Roo.log('invalid cmd:' + el.type);
22610 flushHexStore : function()
22612 if (this.hexStore.length < 1) {
22615 var hexstr = this.hexStore.map(
22620 this.group.addContent( new Roo.rtf.Hex( hexstr ));
22623 this.hexStore.splice(0)
22627 cmdgroupstart : function()
22629 this.flushHexStore();
22631 this.groupStack.push(this.group);
22634 if (this.doc === false) {
22635 this.group = this.doc = new Roo.rtf.Document();
22639 this.group = new Roo.rtf.Group(this.group);
22641 cmdignorable : function()
22643 this.flushHexStore();
22644 this.group.ignorable = true;
22646 cmdendparagraph : function()
22648 this.flushHexStore();
22649 this.group.addContent(new Roo.rtf.Paragraph());
22651 cmdgroupend : function ()
22653 this.flushHexStore();
22654 var endingGroup = this.group;
22657 this.group = this.groupStack.pop();
22659 this.group.addChild(endingGroup);
22664 var doc = this.group || this.doc;
22665 //if (endingGroup instanceof FontTable) {
22666 // doc.fonts = endingGroup.table
22667 //} else if (endingGroup instanceof ColorTable) {
22668 // doc.colors = endingGroup.table
22669 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
22670 if (endingGroup.ignorable === false) {
22672 this.groups.push(endingGroup);
22673 // Roo.log( endingGroup );
22675 //Roo.each(endingGroup.content, function(item)) {
22676 // doc.addContent(item);
22678 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
22681 cmdtext : function (cmd)
22683 this.flushHexStore();
22684 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
22685 //this.group = this.doc
22686 return; // we really don't care about stray text...
22688 this.group.addContent(new Roo.rtf.Span(cmd));
22690 cmdcontrolword : function (cmd)
22692 this.flushHexStore();
22693 if (!this.group.type) {
22694 this.group.type = cmd.value;
22697 this.group.addContent(new Roo.rtf.Ctrl(cmd));
22698 // we actually don't care about ctrl words...
22701 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
22702 if (this[method]) {
22703 this[method](cmd.param)
22705 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
22709 cmdhexchar : function(cmd) {
22710 this.hexStore.push(cmd);
22712 cmderror : function(cmd) {
22718 if (this.text !== '\u0000') this.emitText()
22724 parseText : function(c)
22727 this.parserState = this.parseEscapes;
22728 } else if (c === '{') {
22729 this.emitStartGroup();
22730 } else if (c === '}') {
22731 this.emitEndGroup();
22732 } else if (c === '\x0A' || c === '\x0D') {
22733 // cr/lf are noise chars
22739 parseEscapes: function (c)
22741 if (c === '\\' || c === '{' || c === '}') {
22743 this.parserState = this.parseText;
22745 this.parserState = this.parseControlSymbol;
22746 this.parseControlSymbol(c);
22749 parseControlSymbol: function(c)
22752 this.text += '\u00a0'; // nbsp
22753 this.parserState = this.parseText
22754 } else if (c === '-') {
22755 this.text += '\u00ad'; // soft hyphen
22756 } else if (c === '_') {
22757 this.text += '\u2011'; // non-breaking hyphen
22758 } else if (c === '*') {
22759 this.emitIgnorable();
22760 this.parserState = this.parseText;
22761 } else if (c === "'") {
22762 this.parserState = this.parseHexChar;
22763 } else if (c === '|') { // formula cacter
22764 this.emitFormula();
22765 this.parserState = this.parseText;
22766 } else if (c === ':') { // subentry in an index entry
22767 this.emitIndexSubEntry();
22768 this.parserState = this.parseText;
22769 } else if (c === '\x0a') {
22770 this.emitEndParagraph();
22771 this.parserState = this.parseText;
22772 } else if (c === '\x0d') {
22773 this.emitEndParagraph();
22774 this.parserState = this.parseText;
22776 this.parserState = this.parseControlWord;
22777 this.parseControlWord(c);
22780 parseHexChar: function (c)
22782 if (/^[A-Fa-f0-9]$/.test(c)) {
22784 if (this.hexChar.length >= 2) {
22785 this.emitHexChar();
22786 this.parserState = this.parseText;
22790 this.emitError("Invalid character \"" + c + "\" in hex literal.");
22791 this.parserState = this.parseText;
22794 parseControlWord : function(c)
22797 this.emitControlWord();
22798 this.parserState = this.parseText;
22799 } else if (/^[-\d]$/.test(c)) {
22800 this.parserState = this.parseControlWordParam;
22801 this.controlWordParam += c;
22802 } else if (/^[A-Za-z]$/.test(c)) {
22803 this.controlWord += c;
22805 this.emitControlWord();
22806 this.parserState = this.parseText;
22810 parseControlWordParam : function (c) {
22811 if (/^\d$/.test(c)) {
22812 this.controlWordParam += c;
22813 } else if (c === ' ') {
22814 this.emitControlWord();
22815 this.parserState = this.parseText;
22817 this.emitControlWord();
22818 this.parserState = this.parseText;
22826 emitText : function () {
22827 if (this.text === '') {
22839 emitControlWord : function ()
22842 if (this.controlWord === '') {
22843 // do we want to track this - it seems just to cause problems.
22844 //this.emitError('empty control word');
22847 type: 'controlword',
22848 value: this.controlWord,
22849 param: this.controlWordParam !== '' && Number(this.controlWordParam),
22855 this.controlWord = '';
22856 this.controlWordParam = '';
22858 emitStartGroup : function ()
22862 type: 'groupstart',
22868 emitEndGroup : function ()
22878 emitIgnorable : function ()
22888 emitHexChar : function ()
22893 value: this.hexChar,
22900 emitError : function (message)
22908 char: this.cpos //,
22909 //stack: new Error().stack
22912 emitEndParagraph : function () {
22915 type: 'endparagraph',
22923 Roo.htmleditor = {};
22926 * @class Roo.htmleditor.Filter
22927 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
22928 * @cfg {DomElement} node The node to iterate and filter
22929 * @cfg {boolean|String|Array} tag Tags to replace
22931 * Create a new Filter.
22932 * @param {Object} config Configuration options
22937 Roo.htmleditor.Filter = function(cfg) {
22938 Roo.apply(this.cfg);
22939 // this does not actually call walk as it's really just a abstract class
22943 Roo.htmleditor.Filter.prototype = {
22949 // overrride to do replace comments.
22950 replaceComment : false,
22952 // overrride to do replace or do stuff with tags..
22953 replaceTag : false,
22955 walk : function(dom)
22957 Roo.each( Array.from(dom.childNodes), function( e ) {
22960 case e.nodeType == 8 && this.replaceComment !== false: // comment
22961 this.replaceComment(e);
22964 case e.nodeType != 1: //not a node.
22967 case this.tag === true: // everything
22968 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
22969 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
22970 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
22971 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
22972 if (this.replaceTag && false === this.replaceTag(e)) {
22975 if (e.hasChildNodes()) {
22980 default: // tags .. that do not match.
22981 if (e.hasChildNodes()) {
22991 removeNodeKeepChildren : function( node)
22994 ar = Array.from(node.childNodes);
22995 for (var i = 0; i < ar.length; i++) {
22997 node.removeChild(ar[i]);
22998 // what if we need to walk these???
22999 node.parentNode.insertBefore(ar[i], node);
23002 node.parentNode.removeChild(node);
23007 * @class Roo.htmleditor.FilterAttributes
23008 * clean attributes and styles including http:// etc.. in attribute
23010 * Run a new Attribute Filter
23011 * @param {Object} config Configuration options
23013 Roo.htmleditor.FilterAttributes = function(cfg)
23015 Roo.apply(this, cfg);
23016 this.attrib_black = this.attrib_black || [];
23017 this.attrib_white = this.attrib_white || [];
23019 this.attrib_clean = this.attrib_clean || [];
23020 this.style_white = this.style_white || [];
23021 this.style_black = this.style_black || [];
23022 this.walk(cfg.node);
23025 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
23027 tag: true, // all tags
23029 attrib_black : false, // array
23030 attrib_clean : false,
23031 attrib_white : false,
23033 style_white : false,
23034 style_black : false,
23037 replaceTag : function(node)
23039 if (!node.attributes || !node.attributes.length) {
23043 for (var i = node.attributes.length-1; i > -1 ; i--) {
23044 var a = node.attributes[i];
23046 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
23047 node.removeAttribute(a.name);
23053 if (a.name.toLowerCase().substr(0,2)=='on') {
23054 node.removeAttribute(a.name);
23059 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
23060 node.removeAttribute(a.name);
23063 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
23064 this.cleanAttr(node,a.name,a.value); // fixme..
23067 if (a.name == 'style') {
23068 this.cleanStyle(node,a.name,a.value);
23071 /// clean up MS crap..
23072 // tecnically this should be a list of valid class'es..
23075 if (a.name == 'class') {
23076 if (a.value.match(/^Mso/)) {
23077 node.removeAttribute('class');
23080 if (a.value.match(/^body$/)) {
23081 node.removeAttribute('class');
23091 return true; // clean children
23094 cleanAttr: function(node, n,v)
23097 if (v.match(/^\./) || v.match(/^\//)) {
23100 if (v.match(/^(http|https):\/\//)
23101 || v.match(/^mailto:/)
23102 || v.match(/^ftp:/)
23103 || v.match(/^data:/)
23107 if (v.match(/^#/)) {
23110 if (v.match(/^\{/)) { // allow template editing.
23113 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23114 node.removeAttribute(n);
23117 cleanStyle : function(node, n,v)
23119 if (v.match(/expression/)) { //XSS?? should we even bother..
23120 node.removeAttribute(n);
23124 var parts = v.split(/;/);
23127 Roo.each(parts, function(p) {
23128 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23132 var l = p.split(':').shift().replace(/\s+/g,'');
23133 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23135 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
23139 // only allow 'c whitelisted system attributes'
23140 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
23148 if (clean.length) {
23149 node.setAttribute(n, clean.join(';'));
23151 node.removeAttribute(n);
23160 * @class Roo.htmleditor.FilterBlack
23161 * remove blacklisted elements.
23163 * Run a new Blacklisted Filter
23164 * @param {Object} config Configuration options
23167 Roo.htmleditor.FilterBlack = function(cfg)
23169 Roo.apply(this, cfg);
23170 this.walk(cfg.node);
23173 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
23175 tag : true, // all elements.
23177 replaceTag : function(n)
23179 n.parentNode.removeChild(n);
23183 * @class Roo.htmleditor.FilterComment
23186 * Run a new Comments Filter
23187 * @param {Object} config Configuration options
23189 Roo.htmleditor.FilterComment = function(cfg)
23191 this.walk(cfg.node);
23194 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
23197 replaceComment : function(n)
23199 n.parentNode.removeChild(n);
23202 * @class Roo.htmleditor.FilterKeepChildren
23203 * remove tags but keep children
23205 * Run a new Keep Children Filter
23206 * @param {Object} config Configuration options
23209 Roo.htmleditor.FilterKeepChildren = function(cfg)
23211 Roo.apply(this, cfg);
23212 if (this.tag === false) {
23213 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
23216 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
23217 this.cleanNamespace = true;
23220 this.walk(cfg.node);
23223 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
23225 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
23227 replaceTag : function(node)
23229 // walk children...
23230 //Roo.log(node.tagName);
23231 var ar = Array.from(node.childNodes);
23234 for (var i = 0; i < ar.length; i++) {
23236 if (e.nodeType == 1) {
23238 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
23239 || // array and it matches
23240 (typeof(this.tag) == 'string' && this.tag == e.tagName)
23242 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
23244 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
23246 this.replaceTag(ar[i]); // child is blacklisted as well...
23251 ar = Array.from(node.childNodes);
23252 for (var i = 0; i < ar.length; i++) {
23254 node.removeChild(ar[i]);
23255 // what if we need to walk these???
23256 node.parentNode.insertBefore(ar[i], node);
23257 if (this.tag !== false) {
23262 //Roo.log("REMOVE:" + node.tagName);
23263 node.parentNode.removeChild(node);
23264 return false; // don't walk children
23269 * @class Roo.htmleditor.FilterParagraph
23270 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
23271 * like on 'push' to remove the <p> tags and replace them with line breaks.
23273 * Run a new Paragraph Filter
23274 * @param {Object} config Configuration options
23277 Roo.htmleditor.FilterParagraph = function(cfg)
23279 // no need to apply config.
23280 this.walk(cfg.node);
23283 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
23290 replaceTag : function(node)
23293 if (node.childNodes.length == 1 &&
23294 node.childNodes[0].nodeType == 3 &&
23295 node.childNodes[0].textContent.trim().length < 1
23297 // remove and replace with '<BR>';
23298 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
23299 return false; // no need to walk..
23301 var ar = Array.from(node.childNodes);
23302 for (var i = 0; i < ar.length; i++) {
23303 node.removeChild(ar[i]);
23304 // what if we need to walk these???
23305 node.parentNode.insertBefore(ar[i], node);
23307 // now what about this?
23311 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
23312 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
23313 node.parentNode.removeChild(node);
23320 * @class Roo.htmleditor.FilterSpan
23321 * filter span's with no attributes out..
23323 * Run a new Span Filter
23324 * @param {Object} config Configuration options
23327 Roo.htmleditor.FilterSpan = function(cfg)
23329 // no need to apply config.
23330 this.walk(cfg.node);
23333 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
23339 replaceTag : function(node)
23341 if (node.attributes && node.attributes.length > 0) {
23342 return true; // walk if there are any.
23344 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
23350 * @class Roo.htmleditor.FilterTableWidth
23351 try and remove table width data - as that frequently messes up other stuff.
23353 * was cleanTableWidths.
23355 * Quite often pasting from word etc.. results in tables with column and widths.
23356 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23359 * Run a new Table Filter
23360 * @param {Object} config Configuration options
23363 Roo.htmleditor.FilterTableWidth = function(cfg)
23365 // no need to apply config.
23366 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
23367 this.walk(cfg.node);
23370 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
23375 replaceTag: function(node) {
23379 if (node.hasAttribute('width')) {
23380 node.removeAttribute('width');
23384 if (node.hasAttribute("style")) {
23387 var styles = node.getAttribute("style").split(";");
23389 Roo.each(styles, function(s) {
23390 if (!s.match(/:/)) {
23393 var kv = s.split(":");
23394 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23397 // what ever is left... we allow.
23400 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23401 if (!nstyle.length) {
23402 node.removeAttribute('style');
23406 return true; // continue doing children..
23409 * @class Roo.htmleditor.FilterWord
23410 * try and clean up all the mess that Word generates.
23412 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
23415 * Run a new Span Filter
23416 * @param {Object} config Configuration options
23419 Roo.htmleditor.FilterWord = function(cfg)
23421 // no need to apply config.
23422 this.replaceDocBullets(cfg.node);
23424 this.replaceAname(cfg.node);
23425 // this is disabled as the removal is done by other filters;
23426 // this.walk(cfg.node);
23431 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
23437 * Clean up MS wordisms...
23439 replaceTag : function(node)
23442 // no idea what this does - span with text, replaceds with just text.
23444 node.nodeName == 'SPAN' &&
23445 !node.hasAttributes() &&
23446 node.childNodes.length == 1 &&
23447 node.firstChild.nodeName == "#text"
23449 var textNode = node.firstChild;
23450 node.removeChild(textNode);
23451 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23452 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23454 node.parentNode.insertBefore(textNode, node);
23455 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23456 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23459 node.parentNode.removeChild(node);
23460 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
23465 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23466 node.parentNode.removeChild(node);
23467 return false; // dont do chidlren
23469 //Roo.log(node.tagName);
23470 // remove - but keep children..
23471 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23472 //Roo.log('-- removed');
23473 while (node.childNodes.length) {
23474 var cn = node.childNodes[0];
23475 node.removeChild(cn);
23476 node.parentNode.insertBefore(cn, node);
23477 // move node to parent - and clean it..
23478 if (cn.nodeType == 1) {
23479 this.replaceTag(cn);
23483 node.parentNode.removeChild(node);
23484 /// no need to iterate chidlren = it's got none..
23485 //this.iterateChildren(node, this.cleanWord);
23486 return false; // no need to iterate children.
23489 if (node.className.length) {
23491 var cn = node.className.split(/\W+/);
23493 Roo.each(cn, function(cls) {
23494 if (cls.match(/Mso[a-zA-Z]+/)) {
23499 node.className = cna.length ? cna.join(' ') : '';
23501 node.removeAttribute("class");
23505 if (node.hasAttribute("lang")) {
23506 node.removeAttribute("lang");
23509 if (node.hasAttribute("style")) {
23511 var styles = node.getAttribute("style").split(";");
23513 Roo.each(styles, function(s) {
23514 if (!s.match(/:/)) {
23517 var kv = s.split(":");
23518 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23521 // what ever is left... we allow.
23524 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23525 if (!nstyle.length) {
23526 node.removeAttribute('style');
23529 return true; // do children
23535 styleToObject: function(node)
23537 var styles = (node.getAttribute("style") || '').split(";");
23539 Roo.each(styles, function(s) {
23540 if (!s.match(/:/)) {
23543 var kv = s.split(":");
23545 // what ever is left... we allow.
23546 ret[kv[0].trim()] = kv[1];
23552 replaceAname : function (doc)
23554 // replace all the a/name without..
23555 var aa = Array.from(doc.getElementsByTagName('a'));
23556 for (var i = 0; i < aa.length; i++) {
23558 if (a.hasAttribute("name")) {
23559 a.removeAttribute("name");
23561 if (a.hasAttribute("href")) {
23564 // reparent children.
23565 this.removeNodeKeepChildren(a);
23575 replaceDocBullets : function(doc)
23577 // this is a bit odd - but it appears some indents use ql-indent-1
23578 //Roo.log(doc.innerHTML);
23580 var listpara = doc.getElementsByClassName('MsoListParagraphCxSpFirst');
23581 for( var i = 0; i < listpara.length; i ++) {
23582 listpara.item(i).className = "MsoListParagraph";
23585 listpara = doc.getElementsByClassName('MsoListParagraphCxSpMiddle');
23586 for( var i = 0; i < listpara.length; i ++) {
23587 listpara.item(i).className = "MsoListParagraph";
23589 listpara = doc.getElementsByClassName('MsoListParagraphCxSpLast');
23590 for( var i = 0; i < listpara.length; i ++) {
23591 listpara.item(i).className = "MsoListParagraph";
23593 listpara = doc.getElementsByClassName('ql-indent-1');
23594 for( var i = 0; i < listpara.length; i ++) {
23595 listpara.item(i).className = "MsoListParagraph";
23598 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
23599 var htwo = doc.getElementsByTagName('h2');
23600 for( var i = 0; i < htwo.length; i ++) {
23601 if (htwo.item(i).hasAttribute('style') && htwo.item(i).getAttribute('style').match(/mso-list:/)) {
23602 htwo.item(i).className = "MsoListParagraph";
23605 listpara = doc.getElementsByClassName('MsoNormal');
23606 while(listpara.length) {
23607 if (listpara.item(0).hasAttribute('style') && listpara.item(0).getAttribute('style').match(/mso-list:/)) {
23608 listpara.item(0).className = "MsoListParagraph";
23610 listpara.item(0).className = "MsoNormalx";
23614 listpara = doc.getElementsByClassName('MsoListParagraph');
23617 //Roo.log(doc.innerHTML);
23619 while(listpara.length) {
23621 this.replaceDocBullet(listpara.item(0));
23628 replaceDocBullet : function(p)
23630 // gather all the siblings.
23632 parent = p.parentNode,
23633 doc = parent.ownerDocument,
23636 var listtype = 'ul';
23638 if (ns.nodeType != 1) {
23639 ns = ns.nextSibling;
23642 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
23645 var spans = ns.getElementsByTagName('span');
23646 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
23648 ns = ns.nextSibling;
23650 if (spans.length && spans[0].hasAttribute('style')) {
23651 var style = this.styleToObject(spans[0]);
23652 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
23659 var spans = ns.getElementsByTagName('span');
23660 if (!spans.length) {
23663 var has_list = false;
23664 for(var i = 0; i < spans.length; i++) {
23665 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
23674 ns = ns.nextSibling;
23678 if (!items.length) {
23683 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
23684 parent.insertBefore(ul, p);
23686 var stack = [ ul ];
23687 var last_li = false;
23689 var margin_to_depth = {};
23692 items.forEach(function(n, ipos) {
23693 //Roo.log("got innertHMLT=" + n.innerHTML);
23695 var spans = n.getElementsByTagName('span');
23696 if (!spans.length) {
23697 //Roo.log("No spans found");
23699 parent.removeChild(n);
23702 return; // skip it...
23708 for(var i = 0; i < spans.length; i++) {
23710 style = this.styleToObject(spans[i]);
23711 if (typeof(style['mso-list']) == 'undefined') {
23714 if (listtype == 'ol') {
23715 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
23717 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
23720 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
23721 style = this.styleToObject(n); // mo-list is from the parent node.
23722 if (typeof(style['mso-list']) == 'undefined') {
23723 //Roo.log("parent is missing level");
23725 parent.removeChild(n);
23730 var margin = style['margin-left'];
23731 if (typeof(margin_to_depth[margin]) == 'undefined') {
23733 margin_to_depth[margin] = max_margins;
23735 nlvl = margin_to_depth[margin] ;
23739 var nul = doc.createElement(listtype); // what about number lists...
23741 last_li = doc.createElement('li');
23742 stack[lvl].appendChild(last_li);
23744 last_li.appendChild(nul);
23750 // not starting at 1..
23751 if (!stack[nlvl].hasAttribute("start") && num > 1) {
23752 stack[nlvl].setAttribute("start", num);
23755 var nli = stack[nlvl].appendChild(doc.createElement('li'));
23757 nli.innerHTML = n.innerHTML;
23758 //Roo.log("innerHTML = " + n.innerHTML);
23759 parent.removeChild(n);
23775 * @class Roo.htmleditor.FilterStyleToTag
23776 * part of the word stuff... - certain 'styles' should be converted to tags.
23778 * font-weight: bold -> bold
23779 * ?? super / subscrit etc..
23782 * Run a new style to tag filter.
23783 * @param {Object} config Configuration options
23785 Roo.htmleditor.FilterStyleToTag = function(cfg)
23789 B : [ 'fontWeight' , 'bold'],
23790 I : [ 'fontStyle' , 'italic'],
23791 //pre : [ 'font-style' , 'italic'],
23792 // h1.. h6 ?? font-size?
23793 SUP : [ 'verticalAlign' , 'super' ],
23794 SUB : [ 'verticalAlign' , 'sub' ]
23799 Roo.apply(this, cfg);
23802 this.walk(cfg.node);
23809 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
23811 tag: true, // all tags
23816 replaceTag : function(node)
23820 if (node.getAttribute("style") === null) {
23824 for (var k in this.tags) {
23825 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
23827 node.style.removeProperty(this.tags[k][0]);
23830 if (!inject.length) {
23833 var cn = Array.from(node.childNodes);
23835 Roo.each(inject, function(t) {
23836 var nc = node.ownerDocument.createElement(t);
23837 nn.appendChild(nc);
23840 for(var i = 0;i < cn.length;cn++) {
23841 node.removeChild(cn[i]);
23842 nn.appendChild(cn[i]);
23844 return true /// iterate thru
23848 * @class Roo.htmleditor.FilterLongBr
23849 * BR/BR/BR - keep a maximum of 2...
23851 * Run a new Long BR Filter
23852 * @param {Object} config Configuration options
23855 Roo.htmleditor.FilterLongBr = function(cfg)
23857 // no need to apply config.
23858 this.walk(cfg.node);
23861 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
23868 replaceTag : function(node)
23871 var ps = node.nextSibling;
23872 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
23873 ps = ps.nextSibling;
23876 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
23877 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
23881 if (!ps || ps.nodeType != 1) {
23885 if (!ps || ps.tagName != 'BR') {
23894 if (!node.previousSibling) {
23897 var ps = node.previousSibling;
23899 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
23900 ps = ps.previousSibling;
23902 if (!ps || ps.nodeType != 1) {
23905 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
23906 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
23910 node.parentNode.removeChild(node); // remove me...
23912 return false; // no need to do children
23919 * @class Roo.htmleditor.FilterBlock
23920 * removes id / data-block and contenteditable that are associated with blocks
23921 * usage should be done on a cloned copy of the dom
23923 * Run a new Attribute Filter { node : xxxx }}
23924 * @param {Object} config Configuration options
23926 Roo.htmleditor.FilterBlock = function(cfg)
23928 Roo.apply(this, cfg);
23929 var qa = cfg.node.querySelectorAll;
23930 this.removeAttributes('data-block');
23931 this.removeAttributes('contenteditable');
23932 this.removeAttributes('id');
23936 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
23938 node: true, // all tags
23941 removeAttributes : function(attr)
23943 var ar = this.node.querySelectorAll('*[' + attr + ']');
23944 for (var i =0;i<ar.length;i++) {
23945 ar[i].removeAttribute(attr);
23954 * This is based loosely on tinymce
23955 * @class Roo.htmleditor.TidySerializer
23956 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
23958 * @method Serializer
23959 * @param {Object} settings Name/value settings object.
23963 Roo.htmleditor.TidySerializer = function(settings)
23965 Roo.apply(this, settings);
23967 this.writer = new Roo.htmleditor.TidyWriter(settings);
23972 Roo.htmleditor.TidySerializer.prototype = {
23975 * @param {boolean} inner do the inner of the node.
23982 * Serializes the specified node into a string.
23985 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
23986 * @method serialize
23987 * @param {DomElement} node Node instance to serialize.
23988 * @return {String} String with HTML based on DOM tree.
23990 serialize : function(node) {
23992 // = settings.validate;
23993 var writer = this.writer;
23997 3: function(node) {
23999 writer.text(node.nodeValue, node);
24002 8: function(node) {
24003 writer.comment(node.nodeValue);
24005 // Processing instruction
24006 7: function(node) {
24007 writer.pi(node.name, node.nodeValue);
24010 10: function(node) {
24011 writer.doctype(node.nodeValue);
24014 4: function(node) {
24015 writer.cdata(node.nodeValue);
24017 // Document fragment
24018 11: function(node) {
24019 node = node.firstChild;
24025 node = node.nextSibling
24030 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
24031 return writer.getContent();
24034 walk: function(node)
24036 var attrName, attrValue, sortedAttrs, i, l, elementRule,
24037 handler = this.handlers[node.nodeType];
24044 var name = node.nodeName;
24045 var isEmpty = node.childNodes.length < 1;
24047 var writer = this.writer;
24048 var attrs = node.attributes;
24051 writer.start(node.nodeName, attrs, isEmpty, node);
24055 node = node.firstChild;
24062 node = node.nextSibling;
24068 // Serialize element and treat all non elements as fragments
24073 * This is based loosely on tinymce
24074 * @class Roo.htmleditor.TidyWriter
24075 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
24078 * - not tested much with 'PRE' formated elements.
24084 Roo.htmleditor.TidyWriter = function(settings)
24087 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
24088 Roo.apply(this, settings);
24092 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
24095 Roo.htmleditor.TidyWriter.prototype = {
24102 // part of state...
24106 last_inline : false,
24111 * Writes the a start element such as <p id="a">.
24114 * @param {String} name Name of the element.
24115 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
24116 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
24118 start: function(name, attrs, empty, node)
24120 var i, l, attr, value;
24122 // there are some situations where adding line break && indentation will not work. will not work.
24123 // <span / b / i ... formating?
24125 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
24126 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
24128 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
24130 var add_lb = name == 'BR' ? false : in_inline;
24132 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
24136 var indentstr = this.indentstr;
24138 // e_inline = elements that can be inline, but still allow \n before and after?
24139 // only 'BR' ??? any others?
24141 // ADD LINE BEFORE tage
24142 if (!this.in_pre) {
24145 if (name == 'BR') {
24147 } else if (this.lastElementEndsWS()) {
24150 // otherwise - no new line. (and dont indent.)
24161 this.html.push(indentstr + '<', name.toLowerCase());
24164 for (i = 0, l = attrs.length; i < l; i++) {
24166 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
24172 this.html[this.html.length] = '/>';
24174 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
24176 var e_inline = name == 'BR' ? false : this.in_inline;
24178 if (!e_inline && !this.in_pre) {
24185 this.html[this.html.length] = '>';
24187 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
24189 if (!in_inline && !in_pre) {
24190 var cn = node.firstChild;
24192 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
24196 cn = cn.nextSibling;
24204 indentstr : in_pre ? '' : (this.indentstr + this.indent),
24206 in_inline : in_inline
24208 // add a line after if we are not in a
24210 if (!in_inline && !in_pre) {
24219 lastElementEndsWS : function()
24221 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
24222 if (value === false) {
24225 return value.match(/\s+$/);
24230 * Writes the a end element such as </p>.
24233 * @param {String} name Name of the element.
24235 end: function(name) {
24238 var indentstr = '';
24239 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
24241 if (!this.in_pre && !in_inline) {
24243 indentstr = this.indentstr;
24245 this.html.push(indentstr + '</', name.toLowerCase(), '>');
24246 this.last_inline = in_inline;
24248 // pop the indent state..
24251 * Writes a text node.
24253 * In pre - we should not mess with the contents.
24257 * @param {String} text String to write out.
24258 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
24260 text: function(in_text, node)
24262 // if not in whitespace critical
24263 if (in_text.length < 1) {
24266 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
24269 this.html[this.html.length] = text;
24273 if (this.in_inline) {
24274 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
24276 text = text.replace(/\s+/,' '); // all white space to single white space
24279 // if next tag is '<BR>', then we can trim right..
24280 if (node.nextSibling &&
24281 node.nextSibling.nodeType == 1 &&
24282 node.nextSibling.nodeName == 'BR' )
24284 text = text.replace(/\s+$/g,'');
24286 // if previous tag was a BR, we can also trim..
24287 if (node.previousSibling &&
24288 node.previousSibling.nodeType == 1 &&
24289 node.previousSibling.nodeName == 'BR' )
24291 text = this.indentstr + text.replace(/^\s+/g,'');
24293 if (text.match(/\n/)) {
24294 text = text.replace(
24295 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
24297 // remoeve the last whitespace / line break.
24298 text = text.replace(/\n\s+$/,'');
24300 // repace long lines
24304 this.html[this.html.length] = text;
24307 // see if previous element was a inline element.
24308 var indentstr = this.indentstr;
24310 text = text.replace(/\s+/g," "); // all whitespace into single white space.
24312 // should trim left?
24313 if (node.previousSibling &&
24314 node.previousSibling.nodeType == 1 &&
24315 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
24321 text = text.replace(/^\s+/,''); // trim left
24324 // should trim right?
24325 if (node.nextSibling &&
24326 node.nextSibling.nodeType == 1 &&
24327 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
24332 text = text.replace(/\s+$/,''); // trim right
24339 if (text.length < 1) {
24342 if (!text.match(/\n/)) {
24343 this.html.push(indentstr + text);
24347 text = this.indentstr + text.replace(
24348 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
24350 // remoeve the last whitespace / line break.
24351 text = text.replace(/\s+$/,'');
24353 this.html.push(text);
24355 // split and indent..
24360 * Writes a cdata node such as <![CDATA[data]]>.
24363 * @param {String} text String to write out inside the cdata.
24365 cdata: function(text) {
24366 this.html.push('<![CDATA[', text, ']]>');
24369 * Writes a comment node such as <!-- Comment -->.
24372 * @param {String} text String to write out inside the comment.
24374 comment: function(text) {
24375 this.html.push('<!--', text, '-->');
24378 * Writes a PI node such as <?xml attr="value" ?>.
24381 * @param {String} name Name of the pi.
24382 * @param {String} text String to write out inside the pi.
24384 pi: function(name, text) {
24385 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
24386 this.indent != '' && this.html.push('\n');
24389 * Writes a doctype node such as <!DOCTYPE data>.
24392 * @param {String} text String to write out inside the doctype.
24394 doctype: function(text) {
24395 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
24398 * Resets the internal buffer if one wants to reuse the writer.
24402 reset: function() {
24403 this.html.length = 0;
24412 * Returns the contents that got serialized.
24414 * @method getContent
24415 * @return {String} HTML contents that got written down.
24417 getContent: function() {
24418 return this.html.join('').replace(/\n$/, '');
24421 pushState : function(cfg)
24423 this.state.push(cfg);
24424 Roo.apply(this, cfg);
24427 popState : function()
24429 if (this.state.length < 1) {
24430 return; // nothing to push
24437 if (this.state.length > 0) {
24438 cfg = this.state[this.state.length-1];
24440 Roo.apply(this, cfg);
24443 addLine: function()
24445 if (this.html.length < 1) {
24450 var value = this.html[this.html.length - 1];
24451 if (value.length > 0 && '\n' !== value) {
24452 this.html.push('\n');
24457 //'pre script noscript style textarea video audio iframe object code'
24458 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
24462 Roo.htmleditor.TidyWriter.inline_elements = [
24463 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
24464 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
24466 Roo.htmleditor.TidyWriter.shortend_elements = [
24467 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
24468 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
24471 Roo.htmleditor.TidyWriter.whitespace_elements = [
24472 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
24474 * This is based loosely on tinymce
24475 * @class Roo.htmleditor.TidyEntities
24477 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
24479 * Not 100% sure this is actually used or needed.
24482 Roo.htmleditor.TidyEntities = {
24485 * initialize data..
24487 init : function (){
24489 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
24494 buildEntitiesLookup: function(items, radix) {
24495 var i, chr, entity, lookup = {};
24499 items = typeof(items) == 'string' ? items.split(',') : items;
24500 radix = radix || 10;
24501 // Build entities lookup table
24502 for (i = 0; i < items.length; i += 2) {
24503 chr = String.fromCharCode(parseInt(items[i], radix));
24504 // Only add non base entities
24505 if (!this.baseEntities[chr]) {
24506 entity = '&' + items[i + 1] + ';';
24507 lookup[chr] = entity;
24508 lookup[entity] = chr;
24547 // Needs to be escaped since the YUI compressor would otherwise break the code
24554 // Reverse lookup table for raw entities
24555 reverseEntities : {
24563 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
24564 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
24565 rawCharsRegExp : /[<>&\"\']/g,
24566 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
24567 namedEntities : false,
24568 namedEntitiesData : [
25069 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
25071 * @method encodeRaw
25072 * @param {String} text Text to encode.
25073 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
25074 * @return {String} Entity encoded text.
25076 encodeRaw: function(text, attr)
25079 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
25080 return t.baseEntities[chr] || chr;
25084 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
25085 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
25086 * and is exposed as the DOMUtils.encode function.
25088 * @method encodeAllRaw
25089 * @param {String} text Text to encode.
25090 * @return {String} Entity encoded text.
25092 encodeAllRaw: function(text) {
25094 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
25095 return t.baseEntities[chr] || chr;
25099 * Encodes the specified string using numeric entities. The core entities will be
25100 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
25102 * @method encodeNumeric
25103 * @param {String} text Text to encode.
25104 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
25105 * @return {String} Entity encoded text.
25107 encodeNumeric: function(text, attr) {
25109 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
25110 // Multi byte sequence convert it to a single entity
25111 if (chr.length > 1) {
25112 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
25114 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
25118 * Encodes the specified string using named entities. The core entities will be encoded
25119 * as named ones but all non lower ascii characters will be encoded into named entities.
25121 * @method encodeNamed
25122 * @param {String} text Text to encode.
25123 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
25124 * @param {Object} entities Optional parameter with entities to use.
25125 * @return {String} Entity encoded text.
25127 encodeNamed: function(text, attr, entities) {
25129 entities = entities || this.namedEntities;
25130 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
25131 return t.baseEntities[chr] || entities[chr] || chr;
25135 * Returns an encode function based on the name(s) and it's optional entities.
25137 * @method getEncodeFunc
25138 * @param {String} name Comma separated list of encoders for example named,numeric.
25139 * @param {String} entities Optional parameter with entities to use instead of the built in set.
25140 * @return {function} Encode function to be used.
25142 getEncodeFunc: function(name, entities) {
25143 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
25145 function encodeNamedAndNumeric(text, attr) {
25146 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
25147 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
25151 function encodeCustomNamed(text, attr) {
25152 return t.encodeNamed(text, attr, entities);
25154 // Replace + with , to be compatible with previous TinyMCE versions
25155 name = this.makeMap(name.replace(/\+/g, ','));
25156 // Named and numeric encoder
25157 if (name.named && name.numeric) {
25158 return this.encodeNamedAndNumeric;
25164 return encodeCustomNamed;
25166 return this.encodeNamed;
25169 if (name.numeric) {
25170 return this.encodeNumeric;
25173 return this.encodeRaw;
25176 * Decodes the specified string, this will replace entities with raw UTF characters.
25179 * @param {String} text Text to entity decode.
25180 * @return {String} Entity decoded string.
25182 decode: function(text)
25185 return text.replace(this.entityRegExp, function(all, numeric) {
25187 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
25188 // Support upper UTF
25189 if (numeric > 65535) {
25191 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
25193 return t.asciiMap[numeric] || String.fromCharCode(numeric);
25195 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
25198 nativeDecode : function (text) {
25201 makeMap : function (items, delim, map) {
25203 items = items || [];
25204 delim = delim || ',';
25205 if (typeof items == "string") {
25206 items = items.split(delim);
25211 map[items[i]] = {};
25219 Roo.htmleditor.TidyEntities.init();
25221 * @class Roo.htmleditor.KeyEnter
25222 * Handle Enter press..
25223 * @cfg {Roo.HtmlEditorCore} core the editor.
25225 * Create a new Filter.
25226 * @param {Object} config Configuration options
25233 Roo.htmleditor.KeyEnter = function(cfg) {
25234 Roo.apply(this, cfg);
25235 // this does not actually call walk as it's really just a abstract class
25237 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
25240 //Roo.htmleditor.KeyEnter.i = 0;
25243 Roo.htmleditor.KeyEnter.prototype = {
25247 keypress : function(e)
25249 if (e.charCode != 13 && e.charCode != 10) {
25250 Roo.log([e.charCode,e]);
25253 e.preventDefault();
25254 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
25255 var doc = this.core.doc;
25259 var sel = this.core.getSelection();
25260 var range = sel.getRangeAt(0);
25261 var n = range.commonAncestorContainer;
25262 var pc = range.closest([ 'ol', 'ul']);
25263 var pli = range.closest('li');
25264 if (!pc || e.ctrlKey) {
25265 // on it list, or ctrl pressed.
25267 sel.insertNode('br', 'after');
25269 // only do this if we have ctrl key..
25270 var br = doc.createElement('br');
25271 br.className = 'clear';
25272 br.setAttribute('style', 'clear: both');
25273 sel.insertNode(br, 'after');
25277 this.core.undoManager.addEvent();
25278 this.core.fireEditorEvent(e);
25282 // deal with <li> insetion
25283 if (pli.innerText.trim() == '' &&
25284 pli.previousSibling &&
25285 pli.previousSibling.nodeName == 'LI' &&
25286 pli.previousSibling.innerText.trim() == '') {
25287 pli.parentNode.removeChild(pli.previousSibling);
25288 sel.cursorAfter(pc);
25289 this.core.undoManager.addEvent();
25290 this.core.fireEditorEvent(e);
25294 var li = doc.createElement('LI');
25295 li.innerHTML = ' ';
25296 if (!pli || !pli.firstSibling) {
25297 pc.appendChild(li);
25299 pli.parentNode.insertBefore(li, pli.firstSibling);
25301 sel.cursorText (li.firstChild);
25303 this.core.undoManager.addEvent();
25304 this.core.fireEditorEvent(e);
25316 * @class Roo.htmleditor.Block
25317 * Base class for html editor blocks - do not use it directly .. extend it..
25318 * @cfg {DomElement} node The node to apply stuff to.
25319 * @cfg {String} friendly_name the name that appears in the context bar about this block
25320 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
25323 * Create a new Filter.
25324 * @param {Object} config Configuration options
25327 Roo.htmleditor.Block = function(cfg)
25329 // do nothing .. should not be called really.
25332 * factory method to get the block from an element (using cache if necessary)
25334 * @param {HtmlElement} the dom element
25336 Roo.htmleditor.Block.factory = function(node)
25338 var cc = Roo.htmleditor.Block.cache;
25339 var id = Roo.get(node).id;
25340 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
25341 Roo.htmleditor.Block.cache[id].readElement(node);
25342 return Roo.htmleditor.Block.cache[id];
25344 var db = node.getAttribute('data-block');
25346 db = node.nodeName.toLowerCase().toUpperCaseFirst();
25348 var cls = Roo.htmleditor['Block' + db];
25349 if (typeof(cls) == 'undefined') {
25350 //Roo.log(node.getAttribute('data-block'));
25351 Roo.log("OOps missing block : " + 'Block' + db);
25354 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
25355 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
25359 * initalize all Elements from content that are 'blockable'
25361 * @param the body element
25363 Roo.htmleditor.Block.initAll = function(body, type)
25365 if (typeof(type) == 'undefined') {
25366 var ia = Roo.htmleditor.Block.initAll;
25372 Roo.each(Roo.get(body).query(type), function(e) {
25373 Roo.htmleditor.Block.factory(e);
25376 // question goes here... do we need to clear out this cache sometimes?
25377 // or show we make it relivant to the htmleditor.
25378 Roo.htmleditor.Block.cache = {};
25380 Roo.htmleditor.Block.prototype = {
25384 // used by context menu
25385 friendly_name : 'Based Block',
25387 // text for button to delete this element
25388 deleteTitle : false,
25392 * Update a node with values from this object
25393 * @param {DomElement} node
25395 updateElement : function(node)
25397 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
25400 * convert to plain HTML for calling insertAtCursor..
25402 toHTML : function()
25404 return Roo.DomHelper.markup(this.toObject());
25407 * used by readEleemnt to extract data from a node
25408 * may need improving as it's pretty basic
25410 * @param {DomElement} node
25411 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
25412 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
25413 * @param {String} style the style property - eg. text-align
25415 getVal : function(node, tag, attr, style)
25418 if (tag !== true && n.tagName != tag.toUpperCase()) {
25419 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
25420 // but kiss for now.
25421 n = node.getElementsByTagName(tag).item(0);
25426 if (attr === false) {
25429 if (attr == 'html') {
25430 return n.innerHTML;
25432 if (attr == 'style') {
25433 return n.style[style];
25436 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
25440 * create a DomHelper friendly object - for use with
25441 * Roo.DomHelper.markup / overwrite / etc..
25444 toObject : function()
25449 * Read a node that has a 'data-block' property - and extract the values from it.
25450 * @param {DomElement} node - the node
25452 readElement : function(node)
25463 * @class Roo.htmleditor.BlockFigure
25464 * Block that has an image and a figcaption
25465 * @cfg {String} image_src the url for the image
25466 * @cfg {String} align (left|right) alignment for the block default left
25467 * @cfg {String} caption the text to appear below (and in the alt tag)
25468 * @cfg {String} caption_display (block|none) display or not the caption
25469 * @cfg {String|number} image_width the width of the image number or %?
25470 * @cfg {String|number} image_height the height of the image number or %?
25473 * Create a new Filter.
25474 * @param {Object} config Configuration options
25477 Roo.htmleditor.BlockFigure = function(cfg)
25480 this.readElement(cfg.node);
25481 this.updateElement(cfg.node);
25483 Roo.apply(this, cfg);
25485 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
25492 caption_display : 'block',
25498 // margin: '2%', not used
25500 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
25503 // used by context menu
25504 friendly_name : 'Image with caption',
25505 deleteTitle : "Delete Image and Caption",
25507 contextMenu : function(toolbar)
25510 var block = function() {
25511 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
25515 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
25517 var syncValue = toolbar.editorcore.syncValue;
25523 xtype : 'TextItem',
25525 xns : rooui.Toolbar //Boostrap?
25529 text: 'Change Image URL',
25532 click: function (btn, state)
25536 Roo.MessageBox.show({
25537 title : "Image Source URL",
25538 msg : "Enter the url for the image",
25539 buttons: Roo.MessageBox.OKCANCEL,
25540 fn: function(btn, val){
25547 toolbar.editorcore.onEditorEvent();
25551 //multiline: multiline,
25553 value : b.image_src
25557 xns : rooui.Toolbar
25562 text: 'Change Link URL',
25565 click: function (btn, state)
25569 Roo.MessageBox.show({
25570 title : "Link URL",
25571 msg : "Enter the url for the link - leave blank to have no link",
25572 buttons: Roo.MessageBox.OKCANCEL,
25573 fn: function(btn, val){
25580 toolbar.editorcore.onEditorEvent();
25584 //multiline: multiline,
25590 xns : rooui.Toolbar
25594 text: 'Show Video URL',
25597 click: function (btn, state)
25599 Roo.MessageBox.alert("Video URL",
25600 block().video_url == '' ? 'This image is not linked ot a video' :
25601 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
25604 xns : rooui.Toolbar
25609 xtype : 'TextItem',
25611 xns : rooui.Toolbar //Boostrap?
25614 xtype : 'ComboBox',
25615 allowBlank : false,
25616 displayField : 'val',
25619 triggerAction : 'all',
25621 valueField : 'val',
25625 select : function (combo, r, index)
25627 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
25629 b.width = r.get('val');
25632 toolbar.editorcore.onEditorEvent();
25637 xtype : 'SimpleStore',
25650 xtype : 'TextItem',
25652 xns : rooui.Toolbar //Boostrap?
25655 xtype : 'ComboBox',
25656 allowBlank : false,
25657 displayField : 'val',
25660 triggerAction : 'all',
25662 valueField : 'val',
25666 select : function (combo, r, index)
25668 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
25670 b.align = r.get('val');
25673 toolbar.editorcore.onEditorEvent();
25678 xtype : 'SimpleStore',
25692 text: 'Hide Caption',
25693 name : 'caption_display',
25695 enableToggle : true,
25696 setValue : function(v) {
25697 // this trigger toggle.
25699 this.setText(v ? "Hide Caption" : "Show Caption");
25700 this.setPressed(v != 'block');
25703 toggle: function (btn, state)
25706 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
25707 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
25710 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
25711 toolbar.editorcore.onEditorEvent();
25714 xns : rooui.Toolbar
25720 * create a DomHelper friendly object - for use with
25721 * Roo.DomHelper.markup / overwrite / etc..
25723 toObject : function()
25725 var d = document.createElement('div');
25726 d.innerHTML = this.caption;
25728 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
25730 var iw = this.align == 'center' ? this.width : '100%';
25733 contenteditable : 'false',
25734 src : this.image_src,
25735 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
25738 maxWidth : iw + ' !important', // this is not getting rendered?
25744 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
25746 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
25751 if (this.href.length > 0) {
25755 contenteditable : 'true',
25763 if (this.video_url.length > 0) {
25768 allowfullscreen : true,
25769 width : 420, // these are for video tricks - that we replace the outer
25771 src : this.video_url,
25777 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
25778 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
25783 'data-block' : 'Figure',
25784 'data-width' : this.width,
25785 contenteditable : 'false',
25789 float : this.align ,
25790 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
25791 width : this.align == 'center' ? '100%' : this.width,
25793 padding: this.align == 'center' ? '0' : '0 10px' ,
25794 textAlign : this.align // seems to work for email..
25799 align : this.align,
25805 'data-display' : this.caption_display,
25807 textAlign : 'left',
25809 lineHeight : '24px',
25810 display : this.caption_display,
25811 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
25813 width: this.align == 'center' ? this.width : '100%'
25817 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
25822 marginTop : '16px',
25828 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
25830 contenteditable : true,
25846 readElement : function(node)
25848 // this should not really come from the link...
25849 this.video_url = this.getVal(node, 'div', 'src');
25850 this.cls = this.getVal(node, 'div', 'class');
25851 this.href = this.getVal(node, 'a', 'href');
25854 this.image_src = this.getVal(node, 'img', 'src');
25856 this.align = this.getVal(node, 'figure', 'align');
25857 var figcaption = this.getVal(node, 'figcaption', false);
25858 if (figcaption !== '') {
25859 this.caption = this.getVal(figcaption, 'i', 'html');
25863 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
25864 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
25865 this.width = this.getVal(node, true, 'data-width');
25866 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
25869 removeNode : function()
25886 * @class Roo.htmleditor.BlockTable
25887 * Block that manages a table
25890 * Create a new Filter.
25891 * @param {Object} config Configuration options
25894 Roo.htmleditor.BlockTable = function(cfg)
25897 this.readElement(cfg.node);
25898 this.updateElement(cfg.node);
25900 Roo.apply(this, cfg);
25903 for(var r = 0; r < this.no_row; r++) {
25905 for(var c = 0; c < this.no_col; c++) {
25906 this.rows[r][c] = this.emptyCell();
25913 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
25922 // used by context menu
25923 friendly_name : 'Table',
25924 deleteTitle : 'Delete Table',
25925 // context menu is drawn once..
25927 contextMenu : function(toolbar)
25930 var block = function() {
25931 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
25935 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
25937 var syncValue = toolbar.editorcore.syncValue;
25943 xtype : 'TextItem',
25945 xns : rooui.Toolbar //Boostrap?
25948 xtype : 'ComboBox',
25949 allowBlank : false,
25950 displayField : 'val',
25953 triggerAction : 'all',
25955 valueField : 'val',
25959 select : function (combo, r, index)
25961 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
25963 b.width = r.get('val');
25966 toolbar.editorcore.onEditorEvent();
25971 xtype : 'SimpleStore',
25983 xtype : 'TextItem',
25984 text : "Columns: ",
25985 xns : rooui.Toolbar //Boostrap?
25992 click : function (_self, e)
25994 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
25995 block().removeColumn();
25997 toolbar.editorcore.onEditorEvent();
26000 xns : rooui.Toolbar
26006 click : function (_self, e)
26008 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26009 block().addColumn();
26011 toolbar.editorcore.onEditorEvent();
26014 xns : rooui.Toolbar
26018 xtype : 'TextItem',
26020 xns : rooui.Toolbar //Boostrap?
26027 click : function (_self, e)
26029 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26030 block().removeRow();
26032 toolbar.editorcore.onEditorEvent();
26035 xns : rooui.Toolbar
26041 click : function (_self, e)
26045 toolbar.editorcore.onEditorEvent();
26048 xns : rooui.Toolbar
26053 text: 'Reset Column Widths',
26056 click : function (_self, e)
26058 block().resetWidths();
26060 toolbar.editorcore.onEditorEvent();
26063 xns : rooui.Toolbar
26074 * create a DomHelper friendly object - for use with
26075 * Roo.DomHelper.markup / overwrite / etc..
26076 * ?? should it be called with option to hide all editing features?
26078 toObject : function()
26083 contenteditable : 'false', // this stops cell selection from picking the table.
26084 'data-block' : 'Table',
26087 border : 'solid 1px #000', // ??? hard coded?
26088 'border-collapse' : 'collapse'
26091 { tag : 'tbody' , cn : [] }
26095 // do we have a head = not really
26097 Roo.each(this.rows, function( row ) {
26102 border : 'solid 1px #000',
26108 ret.cn[0].cn.push(tr);
26109 // does the row have any properties? ?? height?
26111 Roo.each(row, function( cell ) {
26115 contenteditable : 'true',
26116 'data-block' : 'Td',
26120 if (cell.colspan > 1) {
26121 td.colspan = cell.colspan ;
26122 nc += cell.colspan;
26126 if (cell.rowspan > 1) {
26127 td.rowspan = cell.rowspan ;
26136 ncols = Math.max(nc, ncols);
26140 // add the header row..
26149 readElement : function(node)
26151 node = node ? node : this.node ;
26152 this.width = this.getVal(node, true, 'style', 'width') || '100%';
26156 var trs = Array.from(node.rows);
26157 trs.forEach(function(tr) {
26159 this.rows.push(row);
26163 Array.from(tr.cells).forEach(function(td) {
26166 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
26167 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
26168 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
26169 html : td.innerHTML
26171 no_column += add.colspan;
26178 this.no_col = Math.max(this.no_col, no_column);
26185 normalizeRows: function()
26189 this.rows.forEach(function(row) {
26192 row = this.normalizeRow(row);
26194 row.forEach(function(c) {
26195 while (typeof(ret[rid][cid]) != 'undefined') {
26198 if (typeof(ret[rid]) == 'undefined') {
26204 if (c.rowspan < 2) {
26208 for(var i = 1 ;i < c.rowspan; i++) {
26209 if (typeof(ret[rid+i]) == 'undefined') {
26212 ret[rid+i][cid] = c;
26220 normalizeRow: function(row)
26223 row.forEach(function(c) {
26224 if (c.colspan < 2) {
26228 for(var i =0 ;i < c.colspan; i++) {
26236 deleteColumn : function(sel)
26238 if (!sel || sel.type != 'col') {
26241 if (this.no_col < 2) {
26245 this.rows.forEach(function(row) {
26246 var cols = this.normalizeRow(row);
26247 var col = cols[sel.col];
26248 if (col.colspan > 1) {
26258 removeColumn : function()
26260 this.deleteColumn({
26262 col : this.no_col-1
26264 this.updateElement();
26268 addColumn : function()
26271 this.rows.forEach(function(row) {
26272 row.push(this.emptyCell());
26275 this.updateElement();
26278 deleteRow : function(sel)
26280 if (!sel || sel.type != 'row') {
26284 if (this.no_row < 2) {
26288 var rows = this.normalizeRows();
26291 rows[sel.row].forEach(function(col) {
26292 if (col.rowspan > 1) {
26295 col.remove = 1; // flage it as removed.
26300 this.rows.forEach(function(row) {
26302 row.forEach(function(c) {
26303 if (typeof(c.remove) == 'undefined') {
26308 if (newrow.length > 0) {
26312 this.rows = newrows;
26317 this.updateElement();
26320 removeRow : function()
26324 row : this.no_row-1
26330 addRow : function()
26334 for (var i = 0; i < this.no_col; i++ ) {
26336 row.push(this.emptyCell());
26339 this.rows.push(row);
26340 this.updateElement();
26344 // the default cell object... at present...
26345 emptyCell : function() {
26346 return (new Roo.htmleditor.BlockTd({})).toObject();
26351 removeNode : function()
26358 resetWidths : function()
26360 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
26361 var nn = Roo.htmleditor.Block.factory(n);
26363 nn.updateElement(n);
26376 * since selections really work on the table cell, then editing really should work from there
26378 * The original plan was to support merging etc... - but that may not be needed yet..
26380 * So this simple version will support:
26382 * adjust the width +/-
26383 * reset the width...
26392 * @class Roo.htmleditor.BlockTable
26393 * Block that manages a table
26396 * Create a new Filter.
26397 * @param {Object} config Configuration options
26400 Roo.htmleditor.BlockTd = function(cfg)
26403 this.readElement(cfg.node);
26404 this.updateElement(cfg.node);
26406 Roo.apply(this, cfg);
26411 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
26416 textAlign : 'left',
26423 // used by context menu
26424 friendly_name : 'Table Cell',
26425 deleteTitle : false, // use our customer delete
26427 // context menu is drawn once..
26429 contextMenu : function(toolbar)
26432 var cell = function() {
26433 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
26436 var table = function() {
26437 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
26441 var saveSel = function()
26443 lr = toolbar.editorcore.getSelection().getRangeAt(0);
26445 var restoreSel = function()
26449 toolbar.editorcore.focus();
26450 var cr = toolbar.editorcore.getSelection();
26451 cr.removeAllRanges();
26453 toolbar.editorcore.onEditorEvent();
26454 }).defer(10, this);
26460 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
26462 var syncValue = toolbar.editorcore.syncValue;
26469 text : 'Edit Table',
26471 click : function() {
26472 var t = toolbar.tb.selectedNode.closest('table');
26473 toolbar.editorcore.selectNode(t);
26474 toolbar.editorcore.onEditorEvent();
26483 xtype : 'TextItem',
26484 text : "Column Width: ",
26485 xns : rooui.Toolbar
26492 click : function (_self, e)
26494 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26495 cell().shrinkColumn();
26497 toolbar.editorcore.onEditorEvent();
26500 xns : rooui.Toolbar
26506 click : function (_self, e)
26508 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26509 cell().growColumn();
26511 toolbar.editorcore.onEditorEvent();
26514 xns : rooui.Toolbar
26518 xtype : 'TextItem',
26519 text : "Vertical Align: ",
26520 xns : rooui.Toolbar //Boostrap?
26523 xtype : 'ComboBox',
26524 allowBlank : false,
26525 displayField : 'val',
26528 triggerAction : 'all',
26530 valueField : 'val',
26534 select : function (combo, r, index)
26536 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26538 b.valign = r.get('val');
26541 toolbar.editorcore.onEditorEvent();
26546 xtype : 'SimpleStore',
26550 ['bottom'] // there are afew more...
26558 xtype : 'TextItem',
26559 text : "Merge Cells: ",
26560 xns : rooui.Toolbar
26569 click : function (_self, e)
26571 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26572 cell().mergeRight();
26573 //block().growColumn();
26575 toolbar.editorcore.onEditorEvent();
26578 xns : rooui.Toolbar
26585 click : function (_self, e)
26587 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26588 cell().mergeBelow();
26589 //block().growColumn();
26591 toolbar.editorcore.onEditorEvent();
26594 xns : rooui.Toolbar
26597 xtype : 'TextItem',
26599 xns : rooui.Toolbar
26607 click : function (_self, e)
26609 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26612 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
26613 toolbar.editorcore.onEditorEvent();
26617 xns : rooui.Toolbar
26621 xns : rooui.Toolbar
26630 xns : rooui.Toolbar,
26639 click : function (_self, e)
26643 cell().deleteColumn();
26645 toolbar.editorcore.selectNode(t.node);
26646 toolbar.editorcore.onEditorEvent();
26655 click : function (_self, e)
26658 cell().deleteRow();
26661 toolbar.editorcore.selectNode(t.node);
26662 toolbar.editorcore.onEditorEvent();
26669 xtype : 'Separator',
26676 click : function (_self, e)
26679 var nn = t.node.nextSibling || t.node.previousSibling;
26680 t.node.parentNode.removeChild(t.node);
26682 toolbar.editorcore.selectNode(nn, true);
26684 toolbar.editorcore.onEditorEvent();
26694 // align... << fixme
26702 * create a DomHelper friendly object - for use with
26703 * Roo.DomHelper.markup / overwrite / etc..
26704 * ?? should it be called with option to hide all editing features?
26707 * create a DomHelper friendly object - for use with
26708 * Roo.DomHelper.markup / overwrite / etc..
26709 * ?? should it be called with option to hide all editing features?
26711 toObject : function()
26715 contenteditable : 'true', // this stops cell selection from picking the table.
26716 'data-block' : 'Td',
26717 valign : this.valign,
26719 'text-align' : this.textAlign,
26720 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
26721 'border-collapse' : 'collapse',
26722 padding : '6px', // 8 for desktop / 4 for mobile
26723 'vertical-align': this.valign
26727 if (this.width != '') {
26728 ret.width = this.width;
26729 ret.style.width = this.width;
26733 if (this.colspan > 1) {
26734 ret.colspan = this.colspan ;
26736 if (this.rowspan > 1) {
26737 ret.rowspan = this.rowspan ;
26746 readElement : function(node)
26748 node = node ? node : this.node ;
26749 this.width = node.style.width;
26750 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
26751 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
26752 this.html = node.innerHTML;
26753 if (node.style.textAlign != '') {
26754 this.textAlign = node.style.textAlign;
26760 // the default cell object... at present...
26761 emptyCell : function() {
26765 textAlign : 'left',
26766 html : " " // is this going to be editable now?
26771 removeNode : function()
26773 return this.node.closest('table');
26781 toTableArray : function()
26784 var tab = this.node.closest('tr').closest('table');
26785 Array.from(tab.rows).forEach(function(r, ri){
26789 this.colWidths = [];
26790 var all_auto = true;
26791 Array.from(tab.rows).forEach(function(r, ri){
26794 Array.from(r.cells).forEach(function(ce, ci){
26799 colspan : ce.colSpan,
26800 rowspan : ce.rowSpan
26802 if (ce.isEqualNode(this.node)) {
26805 // if we have been filled up by a row?
26806 if (typeof(ret[rn][cn]) != 'undefined') {
26807 while(typeof(ret[rn][cn]) != 'undefined') {
26813 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
26814 this.colWidths[cn] = ce.style.width;
26815 if (this.colWidths[cn] != '') {
26821 if (c.colspan < 2 && c.rowspan < 2 ) {
26826 for(var j = 0; j < c.rowspan; j++) {
26827 if (typeof(ret[rn+j]) == 'undefined') {
26828 continue; // we have a problem..
26831 for(var i = 0; i < c.colspan; i++) {
26832 ret[rn+j][cn+i] = c;
26841 // initalize widths.?
26842 // either all widths or no widths..
26844 this.colWidths[0] = false; // no widths flag.
26855 mergeRight: function()
26858 // get the contents of the next cell along..
26859 var tr = this.node.closest('tr');
26860 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
26861 if (i >= tr.childNodes.length - 1) {
26862 return; // no cells on right to merge with.
26864 var table = this.toTableArray();
26866 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
26867 return; // nothing right?
26869 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
26870 // right cell - must be same rowspan and on the same row.
26871 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
26872 return; // right hand side is not same rowspan.
26877 this.node.innerHTML += ' ' + rc.cell.innerHTML;
26878 tr.removeChild(rc.cell);
26879 this.colspan += rc.colspan;
26880 this.node.setAttribute('colspan', this.colspan);
26882 var table = this.toTableArray();
26883 this.normalizeWidths(table);
26884 this.updateWidths(table);
26888 mergeBelow : function()
26890 var table = this.toTableArray();
26891 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
26892 return; // no row below
26894 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
26895 return; // nothing right?
26897 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
26899 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
26900 return; // right hand side is not same rowspan.
26902 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
26903 rc.cell.parentNode.removeChild(rc.cell);
26904 this.rowspan += rc.rowspan;
26905 this.node.setAttribute('rowspan', this.rowspan);
26910 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
26913 var table = this.toTableArray();
26914 var cd = this.cellData;
26918 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
26921 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
26922 if (r == cd.row && c == cd.col) {
26923 this.node.removeAttribute('rowspan');
26924 this.node.removeAttribute('colspan');
26927 var ntd = this.node.cloneNode(); // which col/row should be 0..
26928 ntd.removeAttribute('id');
26929 ntd.style.width = this.colWidths[c];
26930 ntd.innerHTML = '';
26931 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
26935 this.redrawAllCells(table);
26941 redrawAllCells: function(table)
26945 var tab = this.node.closest('tr').closest('table');
26946 var ctr = tab.rows[0].parentNode;
26947 Array.from(tab.rows).forEach(function(r, ri){
26949 Array.from(r.cells).forEach(function(ce, ci){
26950 ce.parentNode.removeChild(ce);
26952 r.parentNode.removeChild(r);
26954 for(var r = 0 ; r < table.length; r++) {
26955 var re = tab.rows[r];
26957 var re = tab.ownerDocument.createElement('tr');
26958 ctr.appendChild(re);
26959 for(var c = 0 ; c < table[r].length; c++) {
26960 if (table[r][c].cell === false) {
26964 re.appendChild(table[r][c].cell);
26966 table[r][c].cell = false;
26971 updateWidths : function(table)
26973 for(var r = 0 ; r < table.length; r++) {
26975 for(var c = 0 ; c < table[r].length; c++) {
26976 if (table[r][c].cell === false) {
26980 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
26981 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
26982 el.width = Math.floor(this.colWidths[c]) +'%';
26983 el.updateElement(el.node);
26985 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
26986 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
26988 for(var i = 0; i < table[r][c].colspan; i ++) {
26989 width += Math.floor(this.colWidths[c + i]);
26991 el.width = width +'%';
26992 el.updateElement(el.node);
26994 table[r][c].cell = false; // done
26998 normalizeWidths : function(table)
27000 if (this.colWidths[0] === false) {
27001 var nw = 100.0 / this.colWidths.length;
27002 this.colWidths.forEach(function(w,i) {
27003 this.colWidths[i] = nw;
27008 var t = 0, missing = [];
27010 this.colWidths.forEach(function(w,i) {
27012 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
27013 var add = this.colWidths[i];
27022 var nc = this.colWidths.length;
27023 if (missing.length) {
27024 var mult = (nc - missing.length) / (1.0 * nc);
27026 var ew = (100 -t) / (1.0 * missing.length);
27027 this.colWidths.forEach(function(w,i) {
27029 this.colWidths[i] = w * mult;
27033 this.colWidths[i] = ew;
27035 // have to make up numbers..
27038 // now we should have all the widths..
27043 shrinkColumn : function()
27045 var table = this.toTableArray();
27046 this.normalizeWidths(table);
27047 var col = this.cellData.col;
27048 var nw = this.colWidths[col] * 0.8;
27052 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
27053 this.colWidths.forEach(function(w,i) {
27055 this.colWidths[i] = nw;
27058 this.colWidths[i] += otherAdd
27060 this.updateWidths(table);
27063 growColumn : function()
27065 var table = this.toTableArray();
27066 this.normalizeWidths(table);
27067 var col = this.cellData.col;
27068 var nw = this.colWidths[col] * 1.2;
27072 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
27073 this.colWidths.forEach(function(w,i) {
27075 this.colWidths[i] = nw;
27078 this.colWidths[i] -= otherSub
27080 this.updateWidths(table);
27083 deleteRow : function()
27085 // delete this rows 'tr'
27086 // if any of the cells in this row have a rowspan > 1 && row!= this row..
27087 // then reduce the rowspan.
27088 var table = this.toTableArray();
27089 // this.cellData.row;
27090 for (var i =0;i< table[this.cellData.row].length ; i++) {
27091 var c = table[this.cellData.row][i];
27092 if (c.row != this.cellData.row) {
27095 c.cell.setAttribute('rowspan', c.rowspan);
27098 if (c.rowspan > 1) {
27100 c.cell.setAttribute('rowspan', c.rowspan);
27103 table.splice(this.cellData.row,1);
27104 this.redrawAllCells(table);
27107 deleteColumn : function()
27109 var table = this.toTableArray();
27111 for (var i =0;i< table.length ; i++) {
27112 var c = table[i][this.cellData.col];
27113 if (c.col != this.cellData.col) {
27114 table[i][this.cellData.col].colspan--;
27115 } else if (c.colspan > 1) {
27117 c.cell.setAttribute('colspan', c.colspan);
27119 table[i].splice(this.cellData.col,1);
27122 this.redrawAllCells(table);
27130 //<script type="text/javascript">
27133 * Based Ext JS Library 1.1.1
27134 * Copyright(c) 2006-2007, Ext JS, LLC.
27140 * @class Roo.HtmlEditorCore
27141 * @extends Roo.Component
27142 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
27144 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
27147 Roo.HtmlEditorCore = function(config){
27150 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
27155 * @event initialize
27156 * Fires when the editor is fully initialized (including the iframe)
27157 * @param {Roo.HtmlEditorCore} this
27162 * Fires when the editor is first receives the focus. Any insertion must wait
27163 * until after this event.
27164 * @param {Roo.HtmlEditorCore} this
27168 * @event beforesync
27169 * Fires before the textarea is updated with content from the editor iframe. Return false
27170 * to cancel the sync.
27171 * @param {Roo.HtmlEditorCore} this
27172 * @param {String} html
27176 * @event beforepush
27177 * Fires before the iframe editor is updated with content from the textarea. Return false
27178 * to cancel the push.
27179 * @param {Roo.HtmlEditorCore} this
27180 * @param {String} html
27185 * Fires when the textarea is updated with content from the editor iframe.
27186 * @param {Roo.HtmlEditorCore} this
27187 * @param {String} html
27192 * Fires when the iframe editor is updated with content from the textarea.
27193 * @param {Roo.HtmlEditorCore} this
27194 * @param {String} html
27199 * @event editorevent
27200 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27201 * @param {Roo.HtmlEditorCore} this
27208 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
27210 // defaults : white / black...
27211 this.applyBlacklists();
27218 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
27222 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
27228 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27233 * @cfg {Number} height (in pixels)
27237 * @cfg {Number} width (in pixels)
27241 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
27242 * if you are doing an email editor, this probably needs disabling, it's designed
27247 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
27249 enableBlocks : true,
27251 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27254 stylesheets: false,
27256 * @cfg {String} language default en - language of text (usefull for rtl languages)
27262 * @cfg {boolean} allowComments - default false - allow comments in HTML source
27263 * - by default they are stripped - if you are editing email you may need this.
27265 allowComments: false,
27269 // private properties
27270 validationEvent : false,
27272 initialized : false,
27274 sourceEditMode : false,
27275 onFocus : Roo.emptyFn,
27277 hideMode:'offsets',
27281 // blacklist + whitelisted elements..
27288 undoManager : false,
27290 * Protected method that will not generally be called directly. It
27291 * is called when the editor initializes the iframe with HTML contents. Override this method if you
27292 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
27294 getDocMarkup : function(){
27298 // inherit styels from page...??
27299 if (this.stylesheets === false) {
27301 Roo.get(document.head).select('style').each(function(node) {
27302 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
27305 Roo.get(document.head).select('link').each(function(node) {
27306 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
27309 } else if (!this.stylesheets.length) {
27311 st = '<style type="text/css">' +
27312 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
27315 for (var i in this.stylesheets) {
27316 if (typeof(this.stylesheets[i]) != 'string') {
27319 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
27324 st += '<style type="text/css">' +
27325 'IMG { cursor: pointer } ' +
27328 st += '<meta name="google" content="notranslate">';
27330 var cls = 'notranslate roo-htmleditor-body';
27332 if(this.bodyCls.length){
27333 cls += ' ' + this.bodyCls;
27336 return '<html class="notranslate" translate="no"><head>' + st +
27337 //<style type="text/css">' +
27338 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
27340 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
27344 onRender : function(ct, position)
27347 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
27348 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
27351 this.el.dom.style.border = '0 none';
27352 this.el.dom.setAttribute('tabIndex', -1);
27353 this.el.addClass('x-hidden hide');
27357 if(Roo.isIE){ // fix IE 1px bogus margin
27358 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
27362 this.frameId = Roo.id();
27366 var iframe = this.owner.wrap.createChild({
27368 cls: 'form-control', // bootstrap..
27370 name: this.frameId,
27371 frameBorder : 'no',
27372 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
27377 this.iframe = iframe.dom;
27379 this.assignDocWin();
27381 this.doc.designMode = 'on';
27384 this.doc.write(this.getDocMarkup());
27388 var task = { // must defer to wait for browser to be ready
27390 //console.log("run task?" + this.doc.readyState);
27391 this.assignDocWin();
27392 if(this.doc.body || this.doc.readyState == 'complete'){
27394 this.doc.designMode="on";
27399 Roo.TaskMgr.stop(task);
27400 this.initEditor.defer(10, this);
27407 Roo.TaskMgr.start(task);
27412 onResize : function(w, h)
27414 Roo.log('resize: ' +w + ',' + h );
27415 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
27419 if(typeof w == 'number'){
27421 this.iframe.style.width = w + 'px';
27423 if(typeof h == 'number'){
27425 this.iframe.style.height = h + 'px';
27427 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
27434 * Toggles the editor between standard and source edit mode.
27435 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27437 toggleSourceEdit : function(sourceEditMode){
27439 this.sourceEditMode = sourceEditMode === true;
27441 if(this.sourceEditMode){
27443 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
27446 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
27447 //this.iframe.className = '';
27450 //this.setSize(this.owner.wrap.getSize());
27451 //this.fireEvent('editmodechange', this, this.sourceEditMode);
27458 * Protected method that will not generally be called directly. If you need/want
27459 * custom HTML cleanup, this is the method you should override.
27460 * @param {String} html The HTML to be cleaned
27461 * return {String} The cleaned HTML
27463 cleanHtml : function(html)
27465 html = String(html);
27466 if(html.length > 5){
27467 if(Roo.isSafari){ // strip safari nonsense
27468 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
27471 if(html == ' '){
27478 * HTML Editor -> Textarea
27479 * Protected method that will not generally be called directly. Syncs the contents
27480 * of the editor iframe with the textarea.
27482 syncValue : function()
27484 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
27485 if(this.initialized){
27487 if (this.undoManager) {
27488 this.undoManager.addEvent();
27492 var bd = (this.doc.body || this.doc.documentElement);
27495 var sel = this.win.getSelection();
27497 var div = document.createElement('div');
27498 div.innerHTML = bd.innerHTML;
27499 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
27500 if (gtx.length > 0) {
27501 var rm = gtx.item(0).parentNode;
27502 rm.parentNode.removeChild(rm);
27506 if (this.enableBlocks) {
27507 new Roo.htmleditor.FilterBlock({ node : div });
27510 var tidy = new Roo.htmleditor.TidySerializer({
27513 var html = tidy.serialize(div);
27517 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
27518 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
27520 html = '<div style="'+m[0]+'">' + html + '</div>';
27523 html = this.cleanHtml(html);
27524 // fix up the special chars.. normaly like back quotes in word...
27525 // however we do not want to do this with chinese..
27526 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
27528 var cc = match.charCodeAt();
27530 // Get the character value, handling surrogate pairs
27531 if (match.length == 2) {
27532 // It's a surrogate pair, calculate the Unicode code point
27533 var high = match.charCodeAt(0) - 0xD800;
27534 var low = match.charCodeAt(1) - 0xDC00;
27535 cc = (high * 0x400) + low + 0x10000;
27537 (cc >= 0x4E00 && cc < 0xA000 ) ||
27538 (cc >= 0x3400 && cc < 0x4E00 ) ||
27539 (cc >= 0xf900 && cc < 0xfb00 )
27544 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
27545 return "&#" + cc + ";";
27552 if(this.owner.fireEvent('beforesync', this, html) !== false){
27553 this.el.dom.value = html;
27554 this.owner.fireEvent('sync', this, html);
27560 * TEXTAREA -> EDITABLE
27561 * Protected method that will not generally be called directly. Pushes the value of the textarea
27562 * into the iframe editor.
27564 pushValue : function()
27566 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
27567 if(this.initialized){
27568 var v = this.el.dom.value.trim();
27571 if(this.owner.fireEvent('beforepush', this, v) !== false){
27572 var d = (this.doc.body || this.doc.documentElement);
27575 this.el.dom.value = d.innerHTML;
27576 this.owner.fireEvent('push', this, v);
27578 if (this.autoClean) {
27579 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
27580 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
27582 if (this.enableBlocks) {
27583 Roo.htmleditor.Block.initAll(this.doc.body);
27586 this.updateLanguage();
27588 var lc = this.doc.body.lastChild;
27589 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
27590 // add an extra line at the end.
27591 this.doc.body.appendChild(this.doc.createElement('br'));
27599 deferFocus : function(){
27600 this.focus.defer(10, this);
27604 focus : function(){
27605 if(this.win && !this.sourceEditMode){
27612 assignDocWin: function()
27614 var iframe = this.iframe;
27617 this.doc = iframe.contentWindow.document;
27618 this.win = iframe.contentWindow;
27620 // if (!Roo.get(this.frameId)) {
27623 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
27624 // this.win = Roo.get(this.frameId).dom.contentWindow;
27626 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
27630 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
27631 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
27636 initEditor : function(){
27637 //console.log("INIT EDITOR");
27638 this.assignDocWin();
27642 this.doc.designMode="on";
27644 this.doc.write(this.getDocMarkup());
27647 var dbody = (this.doc.body || this.doc.documentElement);
27648 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
27649 // this copies styles from the containing element into thsi one..
27650 // not sure why we need all of this..
27651 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
27653 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
27654 //ss['background-attachment'] = 'fixed'; // w3c
27655 dbody.bgProperties = 'fixed'; // ie
27656 dbody.setAttribute("translate", "no");
27658 //Roo.DomHelper.applyStyles(dbody, ss);
27659 Roo.EventManager.on(this.doc, {
27661 'mouseup': this.onEditorEvent,
27662 'dblclick': this.onEditorEvent,
27663 'click': this.onEditorEvent,
27664 'keyup': this.onEditorEvent,
27669 Roo.EventManager.on(this.doc, {
27670 'paste': this.onPasteEvent,
27674 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
27677 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
27678 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
27680 this.initialized = true;
27683 // initialize special key events - enter
27684 new Roo.htmleditor.KeyEnter({core : this});
27688 this.owner.fireEvent('initialize', this);
27691 // this is to prevent a href clicks resulting in a redirect?
27693 onPasteEvent : function(e,v)
27695 // I think we better assume paste is going to be a dirty load of rubish from word..
27697 // even pasting into a 'email version' of this widget will have to clean up that mess.
27698 var cd = (e.browserEvent.clipboardData || window.clipboardData);
27700 // check what type of paste - if it's an image, then handle it differently.
27701 if (cd.files && cd.files.length > 0) {
27703 var urlAPI = (window.createObjectURL && window) ||
27704 (window.URL && URL.revokeObjectURL && URL) ||
27705 (window.webkitURL && webkitURL);
27707 var url = urlAPI.createObjectURL( cd.files[0]);
27708 this.insertAtCursor('<img src=" + url + ">');
27711 if (cd.types.indexOf('text/html') < 0 ) {
27715 var html = cd.getData('text/html'); // clipboard event
27716 if (cd.types.indexOf('text/rtf') > -1) {
27717 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
27718 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
27723 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
27724 .map(function(g) { return g.toDataURL(); })
27725 .filter(function(g) { return g != 'about:blank'; });
27728 html = this.cleanWordChars(html);
27730 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
27733 var sn = this.getParentElement();
27734 // check if d contains a table, and prevent nesting??
27735 //Roo.log(d.getElementsByTagName('table'));
27737 //Roo.log(sn.closest('table'));
27738 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
27739 e.preventDefault();
27740 this.insertAtCursor("You can not nest tables");
27741 //Roo.log("prevent?"); // fixme -
27747 if (images.length > 0) {
27748 // replace all v:imagedata - with img.
27749 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
27750 Roo.each(ar, function(node) {
27751 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
27752 node.parentNode.removeChild(node);
27756 Roo.each(d.getElementsByTagName('img'), function(img, i) {
27757 img.setAttribute('src', images[i]);
27760 if (this.autoClean) {
27761 new Roo.htmleditor.FilterWord({ node : d });
27763 new Roo.htmleditor.FilterStyleToTag({ node : d });
27764 new Roo.htmleditor.FilterAttributes({
27766 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
27767 attrib_clean : ['href', 'src' ]
27769 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
27770 // should be fonts..
27771 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
27772 new Roo.htmleditor.FilterParagraph({ node : d });
27773 new Roo.htmleditor.FilterSpan({ node : d });
27774 new Roo.htmleditor.FilterLongBr({ node : d });
27775 new Roo.htmleditor.FilterComment({ node : d });
27779 if (this.enableBlocks) {
27781 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
27782 if (img.closest('figure')) { // assume!! that it's aready
27785 var fig = new Roo.htmleditor.BlockFigure({
27786 image_src : img.src
27788 fig.updateElement(img); // replace it..
27794 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
27795 if (this.enableBlocks) {
27796 Roo.htmleditor.Block.initAll(this.doc.body);
27800 e.preventDefault();
27802 // default behaveiour should be our local cleanup paste? (optional?)
27803 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
27804 //this.owner.fireEvent('paste', e, v);
27807 onDestroy : function(){
27813 //for (var i =0; i < this.toolbars.length;i++) {
27814 // // fixme - ask toolbars for heights?
27815 // this.toolbars[i].onDestroy();
27818 //this.wrap.dom.innerHTML = '';
27819 //this.wrap.remove();
27824 onFirstFocus : function(){
27826 this.assignDocWin();
27827 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
27829 this.activated = true;
27832 if(Roo.isGecko){ // prevent silly gecko errors
27834 var s = this.win.getSelection();
27835 if(!s.focusNode || s.focusNode.nodeType != 3){
27836 var r = s.getRangeAt(0);
27837 r.selectNodeContents((this.doc.body || this.doc.documentElement));
27842 this.execCmd('useCSS', true);
27843 this.execCmd('styleWithCSS', false);
27846 this.owner.fireEvent('activate', this);
27850 adjustFont: function(btn){
27851 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
27852 //if(Roo.isSafari){ // safari
27855 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
27856 if(Roo.isSafari){ // safari
27857 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
27858 v = (v < 10) ? 10 : v;
27859 v = (v > 48) ? 48 : v;
27860 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
27865 v = Math.max(1, v+adjust);
27867 this.execCmd('FontSize', v );
27870 onEditorEvent : function(e)
27874 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
27875 return; // we do not handle this.. (undo manager does..)
27877 // in theory this detects if the last element is not a br, then we try and do that.
27878 // its so clicking in space at bottom triggers adding a br and moving the cursor.
27880 e.target.nodeName == 'BODY' &&
27881 e.type == "mouseup" &&
27882 this.doc.body.lastChild
27884 var lc = this.doc.body.lastChild;
27885 // gtx-trans is google translate plugin adding crap.
27886 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
27887 lc = lc.previousSibling;
27889 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
27890 // if last element is <BR> - then dont do anything.
27892 var ns = this.doc.createElement('br');
27893 this.doc.body.appendChild(ns);
27894 range = this.doc.createRange();
27895 range.setStartAfter(ns);
27896 range.collapse(true);
27897 var sel = this.win.getSelection();
27898 sel.removeAllRanges();
27899 sel.addRange(range);
27905 this.fireEditorEvent(e);
27906 // this.updateToolbar();
27907 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
27910 fireEditorEvent: function(e)
27912 this.owner.fireEvent('editorevent', this, e);
27915 insertTag : function(tg)
27917 // could be a bit smarter... -> wrap the current selected tRoo..
27918 if (tg.toLowerCase() == 'span' ||
27919 tg.toLowerCase() == 'code' ||
27920 tg.toLowerCase() == 'sup' ||
27921 tg.toLowerCase() == 'sub'
27924 range = this.createRange(this.getSelection());
27925 var wrappingNode = this.doc.createElement(tg.toLowerCase());
27926 wrappingNode.appendChild(range.extractContents());
27927 range.insertNode(wrappingNode);
27934 this.execCmd("formatblock", tg);
27935 this.undoManager.addEvent();
27938 insertText : function(txt)
27942 var range = this.createRange();
27943 range.deleteContents();
27944 //alert(Sender.getAttribute('label'));
27946 range.insertNode(this.doc.createTextNode(txt));
27947 this.undoManager.addEvent();
27953 * Executes a Midas editor command on the editor document and performs necessary focus and
27954 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
27955 * @param {String} cmd The Midas command
27956 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
27958 relayCmd : function(cmd, value)
27962 case 'justifyleft':
27963 case 'justifyright':
27964 case 'justifycenter':
27965 // if we are in a cell, then we will adjust the
27966 var n = this.getParentElement();
27967 var td = n.closest('td');
27969 var bl = Roo.htmleditor.Block.factory(td);
27970 bl.textAlign = cmd.replace('justify','');
27971 bl.updateElement();
27972 this.owner.fireEvent('editorevent', this);
27975 this.execCmd('styleWithCSS', true); //
27979 // if there is no selection, then we insert, and set the curson inside it..
27980 this.execCmd('styleWithCSS', false);
27990 this.execCmd(cmd, value);
27991 this.owner.fireEvent('editorevent', this);
27992 //this.updateToolbar();
27993 this.owner.deferFocus();
27997 * Executes a Midas editor command directly on the editor document.
27998 * For visual commands, you should use {@link #relayCmd} instead.
27999 * <b>This should only be called after the editor is initialized.</b>
28000 * @param {String} cmd The Midas command
28001 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
28003 execCmd : function(cmd, value){
28004 this.doc.execCommand(cmd, false, value === undefined ? null : value);
28011 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
28013 * @param {String} text | dom node..
28015 insertAtCursor : function(text)
28018 if(!this.activated){
28022 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
28026 // from jquery ui (MIT licenced)
28028 var win = this.win;
28030 if (win.getSelection && win.getSelection().getRangeAt) {
28032 // delete the existing?
28034 this.createRange(this.getSelection()).deleteContents();
28035 range = win.getSelection().getRangeAt(0);
28036 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
28037 range.insertNode(node);
28038 range = range.cloneRange();
28039 range.collapse(false);
28041 win.getSelection().removeAllRanges();
28042 win.getSelection().addRange(range);
28046 } else if (win.document.selection && win.document.selection.createRange) {
28047 // no firefox support
28048 var txt = typeof(text) == 'string' ? text : text.outerHTML;
28049 win.document.selection.createRange().pasteHTML(txt);
28052 // no firefox support
28053 var txt = typeof(text) == 'string' ? text : text.outerHTML;
28054 this.execCmd('InsertHTML', txt);
28062 mozKeyPress : function(e){
28064 var c = e.getCharCode(), cmd;
28067 c = String.fromCharCode(c).toLowerCase();
28081 // this.cleanUpPaste.defer(100, this);
28087 this.relayCmd(cmd);
28088 //this.win.focus();
28089 //this.execCmd(cmd);
28090 //this.deferFocus();
28091 e.preventDefault();
28099 fixKeys : function(){ // load time branching for fastest keydown performance
28103 return function(e){
28104 var k = e.getKey(), r;
28107 r = this.doc.selection.createRange();
28110 r.pasteHTML('    ');
28115 /// this is handled by Roo.htmleditor.KeyEnter
28118 r = this.doc.selection.createRange();
28120 var target = r.parentElement();
28121 if(!target || target.tagName.toLowerCase() != 'li'){
28123 r.pasteHTML('<br/>');
28130 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
28131 // this.cleanUpPaste.defer(100, this);
28137 }else if(Roo.isOpera){
28138 return function(e){
28139 var k = e.getKey();
28143 this.execCmd('InsertHTML','    ');
28147 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
28148 // this.cleanUpPaste.defer(100, this);
28153 }else if(Roo.isSafari){
28154 return function(e){
28155 var k = e.getKey();
28159 this.execCmd('InsertText','\t');
28163 this.mozKeyPress(e);
28165 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
28166 // this.cleanUpPaste.defer(100, this);
28174 getAllAncestors: function()
28176 var p = this.getSelectedNode();
28179 a.push(p); // push blank onto stack..
28180 p = this.getParentElement();
28184 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
28188 a.push(this.doc.body);
28192 lastSelNode : false,
28195 getSelection : function()
28197 this.assignDocWin();
28198 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
28201 * Select a dom node
28202 * @param {DomElement} node the node to select
28204 selectNode : function(node, collapse)
28206 var nodeRange = node.ownerDocument.createRange();
28208 nodeRange.selectNode(node);
28210 nodeRange.selectNodeContents(node);
28212 if (collapse === true) {
28213 nodeRange.collapse(true);
28216 var s = this.win.getSelection();
28217 s.removeAllRanges();
28218 s.addRange(nodeRange);
28221 getSelectedNode: function()
28223 // this may only work on Gecko!!!
28225 // should we cache this!!!!
28229 var range = this.createRange(this.getSelection()).cloneRange();
28232 var parent = range.parentElement();
28234 var testRange = range.duplicate();
28235 testRange.moveToElementText(parent);
28236 if (testRange.inRange(range)) {
28239 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
28242 parent = parent.parentElement;
28247 // is ancestor a text element.
28248 var ac = range.commonAncestorContainer;
28249 if (ac.nodeType == 3) {
28250 ac = ac.parentNode;
28253 var ar = ac.childNodes;
28256 var other_nodes = [];
28257 var has_other_nodes = false;
28258 for (var i=0;i<ar.length;i++) {
28259 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
28262 // fullly contained node.
28264 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
28269 // probably selected..
28270 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
28271 other_nodes.push(ar[i]);
28275 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
28280 has_other_nodes = true;
28282 if (!nodes.length && other_nodes.length) {
28283 nodes= other_nodes;
28285 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
28293 createRange: function(sel)
28295 // this has strange effects when using with
28296 // top toolbar - not sure if it's a great idea.
28297 //this.editor.contentWindow.focus();
28298 if (typeof sel != "undefined") {
28300 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
28302 return this.doc.createRange();
28305 return this.doc.createRange();
28308 getParentElement: function()
28311 this.assignDocWin();
28312 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
28314 var range = this.createRange(sel);
28317 var p = range.commonAncestorContainer;
28318 while (p.nodeType == 3) { // text node
28329 * Range intersection.. the hard stuff...
28333 * [ -- selected range --- ]
28337 * if end is before start or hits it. fail.
28338 * if start is after end or hits it fail.
28340 * if either hits (but other is outside. - then it's not
28346 // @see http://www.thismuchiknow.co.uk/?p=64.
28347 rangeIntersectsNode : function(range, node)
28349 var nodeRange = node.ownerDocument.createRange();
28351 nodeRange.selectNode(node);
28353 nodeRange.selectNodeContents(node);
28356 var rangeStartRange = range.cloneRange();
28357 rangeStartRange.collapse(true);
28359 var rangeEndRange = range.cloneRange();
28360 rangeEndRange.collapse(false);
28362 var nodeStartRange = nodeRange.cloneRange();
28363 nodeStartRange.collapse(true);
28365 var nodeEndRange = nodeRange.cloneRange();
28366 nodeEndRange.collapse(false);
28368 return rangeStartRange.compareBoundaryPoints(
28369 Range.START_TO_START, nodeEndRange) == -1 &&
28370 rangeEndRange.compareBoundaryPoints(
28371 Range.START_TO_START, nodeStartRange) == 1;
28375 rangeCompareNode : function(range, node)
28377 var nodeRange = node.ownerDocument.createRange();
28379 nodeRange.selectNode(node);
28381 nodeRange.selectNodeContents(node);
28385 range.collapse(true);
28387 nodeRange.collapse(true);
28389 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
28390 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
28392 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
28394 var nodeIsBefore = ss == 1;
28395 var nodeIsAfter = ee == -1;
28397 if (nodeIsBefore && nodeIsAfter) {
28400 if (!nodeIsBefore && nodeIsAfter) {
28401 return 1; //right trailed.
28404 if (nodeIsBefore && !nodeIsAfter) {
28405 return 2; // left trailed.
28411 cleanWordChars : function(input) {// change the chars to hex code
28414 [ 8211, "–" ],
28415 [ 8212, "—" ],
28423 var output = input;
28424 Roo.each(swapCodes, function(sw) {
28425 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
28427 output = output.replace(swapper, sw[1]);
28437 cleanUpChild : function (node)
28440 new Roo.htmleditor.FilterComment({node : node});
28441 new Roo.htmleditor.FilterAttributes({
28443 attrib_black : this.ablack,
28444 attrib_clean : this.aclean,
28445 style_white : this.cwhite,
28446 style_black : this.cblack
28448 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
28449 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
28455 * Clean up MS wordisms...
28456 * @deprecated - use filter directly
28458 cleanWord : function(node)
28460 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
28461 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
28468 * @deprecated - use filters
28470 cleanTableWidths : function(node)
28472 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
28479 applyBlacklists : function()
28481 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
28482 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
28484 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
28485 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
28486 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
28490 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
28491 if (b.indexOf(tag) > -1) {
28494 this.white.push(tag);
28498 Roo.each(w, function(tag) {
28499 if (b.indexOf(tag) > -1) {
28502 if (this.white.indexOf(tag) > -1) {
28505 this.white.push(tag);
28510 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
28511 if (w.indexOf(tag) > -1) {
28514 this.black.push(tag);
28518 Roo.each(b, function(tag) {
28519 if (w.indexOf(tag) > -1) {
28522 if (this.black.indexOf(tag) > -1) {
28525 this.black.push(tag);
28530 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
28531 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
28535 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
28536 if (b.indexOf(tag) > -1) {
28539 this.cwhite.push(tag);
28543 Roo.each(w, function(tag) {
28544 if (b.indexOf(tag) > -1) {
28547 if (this.cwhite.indexOf(tag) > -1) {
28550 this.cwhite.push(tag);
28555 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
28556 if (w.indexOf(tag) > -1) {
28559 this.cblack.push(tag);
28563 Roo.each(b, function(tag) {
28564 if (w.indexOf(tag) > -1) {
28567 if (this.cblack.indexOf(tag) > -1) {
28570 this.cblack.push(tag);
28575 setStylesheets : function(stylesheets)
28577 if(typeof(stylesheets) == 'string'){
28578 Roo.get(this.iframe.contentDocument.head).createChild({
28580 rel : 'stylesheet',
28589 Roo.each(stylesheets, function(s) {
28594 Roo.get(_this.iframe.contentDocument.head).createChild({
28596 rel : 'stylesheet',
28606 updateLanguage : function()
28608 if (!this.iframe || !this.iframe.contentDocument) {
28611 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
28615 removeStylesheets : function()
28619 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
28624 setStyle : function(style)
28626 Roo.get(this.iframe.contentDocument.head).createChild({
28635 // hide stuff that is not compatible
28649 * @event specialkey
28653 * @cfg {String} fieldClass @hide
28656 * @cfg {String} focusClass @hide
28659 * @cfg {String} autoCreate @hide
28662 * @cfg {String} inputType @hide
28665 * @cfg {String} invalidClass @hide
28668 * @cfg {String} invalidText @hide
28671 * @cfg {String} msgFx @hide
28674 * @cfg {String} validateOnBlur @hide
28678 Roo.HtmlEditorCore.white = [
28679 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
28681 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
28682 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
28683 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
28684 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
28685 'TABLE', 'UL', 'XMP',
28687 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
28690 'DIR', 'MENU', 'OL', 'UL', 'DL',
28696 Roo.HtmlEditorCore.black = [
28697 // 'embed', 'object', // enable - backend responsiblity to clean thiese
28699 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
28700 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
28701 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
28702 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
28703 //'FONT' // CLEAN LATER..
28704 'COLGROUP', 'COL' // messy tables.
28708 Roo.HtmlEditorCore.clean = [ // ?? needed???
28709 'SCRIPT', 'STYLE', 'TITLE', 'XML'
28711 Roo.HtmlEditorCore.tag_remove = [
28716 Roo.HtmlEditorCore.ablack = [
28720 Roo.HtmlEditorCore.aclean = [
28721 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
28725 Roo.HtmlEditorCore.pwhite= [
28726 'http', 'https', 'mailto'
28729 // white listed style attributes.
28730 Roo.HtmlEditorCore.cwhite= [
28731 // 'text-align', /// default is to allow most things..
28737 // black listed style attributes.
28738 Roo.HtmlEditorCore.cblack= [
28739 // 'font-size' -- this can be set by the project
28745 //<script type="text/javascript">
28748 * Ext JS Library 1.1.1
28749 * Copyright(c) 2006-2007, Ext JS, LLC.
28755 Roo.form.HtmlEditor = function(config){
28759 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
28761 if (!this.toolbars) {
28762 this.toolbars = [];
28764 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
28770 * @class Roo.form.HtmlEditor
28771 * @extends Roo.form.Field
28772 * Provides a lightweight HTML Editor component.
28774 * This has been tested on Fireforx / Chrome.. IE may not be so great..
28776 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
28777 * supported by this editor.</b><br/><br/>
28778 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
28779 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28781 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
28783 * @cfg {Boolean} clearUp
28787 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
28792 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
28797 * @cfg {Number} height (in pixels)
28801 * @cfg {Number} width (in pixels)
28806 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
28809 stylesheets: false,
28813 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
28818 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
28824 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
28829 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
28834 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
28836 allowComments: false,
28838 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
28840 enableBlocks : true,
28843 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
28844 * if you are doing an email editor, this probably needs disabling, it's designed
28848 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
28852 * @cfg {String} language default en - language of text (usefull for rtl languages)
28861 // private properties
28862 validationEvent : false,
28864 initialized : false,
28867 onFocus : Roo.emptyFn,
28869 hideMode:'offsets',
28871 actionMode : 'container', // defaults to hiding it...
28873 defaultAutoCreate : { // modified by initCompnoent..
28875 style:"width:500px;height:300px;",
28876 autocomplete: "new-password"
28880 initComponent : function(){
28883 * @event initialize
28884 * Fires when the editor is fully initialized (including the iframe)
28885 * @param {HtmlEditor} this
28890 * Fires when the editor is first receives the focus. Any insertion must wait
28891 * until after this event.
28892 * @param {HtmlEditor} this
28896 * @event beforesync
28897 * Fires before the textarea is updated with content from the editor iframe. Return false
28898 * to cancel the sync.
28899 * @param {HtmlEditor} this
28900 * @param {String} html
28904 * @event beforepush
28905 * Fires before the iframe editor is updated with content from the textarea. Return false
28906 * to cancel the push.
28907 * @param {HtmlEditor} this
28908 * @param {String} html
28913 * Fires when the textarea is updated with content from the editor iframe.
28914 * @param {HtmlEditor} this
28915 * @param {String} html
28920 * Fires when the iframe editor is updated with content from the textarea.
28921 * @param {HtmlEditor} this
28922 * @param {String} html
28926 * @event editmodechange
28927 * Fires when the editor switches edit modes
28928 * @param {HtmlEditor} this
28929 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
28931 editmodechange: true,
28933 * @event editorevent
28934 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
28935 * @param {HtmlEditor} this
28939 * @event firstfocus
28940 * Fires when on first focus - needed by toolbars..
28941 * @param {HtmlEditor} this
28946 * Auto save the htmlEditor value as a file into Events
28947 * @param {HtmlEditor} this
28951 * @event savedpreview
28952 * preview the saved version of htmlEditor
28953 * @param {HtmlEditor} this
28955 savedpreview: true,
28958 * @event stylesheetsclick
28959 * Fires when press the Sytlesheets button
28960 * @param {Roo.HtmlEditorCore} this
28962 stylesheetsclick: true,
28965 * Fires when press user pastes into the editor
28966 * @param {Roo.HtmlEditorCore} this
28970 this.defaultAutoCreate = {
28972 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
28973 autocomplete: "new-password"
28978 * Protected method that will not generally be called directly. It
28979 * is called when the editor creates its toolbar. Override this method if you need to
28980 * add custom toolbar buttons.
28981 * @param {HtmlEditor} editor
28983 createToolbar : function(editor){
28984 Roo.log("create toolbars");
28985 if (!editor.toolbars || !editor.toolbars.length) {
28986 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
28989 for (var i =0 ; i < editor.toolbars.length;i++) {
28990 editor.toolbars[i] = Roo.factory(
28991 typeof(editor.toolbars[i]) == 'string' ?
28992 { xtype: editor.toolbars[i]} : editor.toolbars[i],
28993 Roo.form.HtmlEditor);
28994 editor.toolbars[i].init(editor);
29000 * get the Context selected node
29001 * @returns {DomElement|boolean} selected node if active or false if none
29004 getSelectedNode : function()
29006 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
29009 return this.toolbars[1].tb.selectedNode;
29013 onRender : function(ct, position)
29016 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
29018 this.wrap = this.el.wrap({
29019 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
29022 this.editorcore.onRender(ct, position);
29024 if (this.resizable) {
29025 this.resizeEl = new Roo.Resizable(this.wrap, {
29029 minHeight : this.height,
29030 height: this.height,
29031 handles : this.resizable,
29034 resize : function(r, w, h) {
29035 _t.onResize(w,h); // -something
29041 this.createToolbar(this);
29045 this.setSize(this.wrap.getSize());
29047 if (this.resizeEl) {
29048 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
29049 // should trigger onReize..
29052 this.keyNav = new Roo.KeyNav(this.el, {
29054 "tab" : function(e){
29055 e.preventDefault();
29057 var value = this.getValue();
29059 var start = this.el.dom.selectionStart;
29060 var end = this.el.dom.selectionEnd;
29064 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
29065 this.el.dom.setSelectionRange(end + 1, end + 1);
29069 var f = value.substring(0, start).split("\t");
29071 if(f.pop().length != 0){
29075 this.setValue(f.join("\t") + value.substring(end));
29076 this.el.dom.setSelectionRange(start - 1, start - 1);
29080 "home" : function(e){
29081 e.preventDefault();
29083 var curr = this.el.dom.selectionStart;
29084 var lines = this.getValue().split("\n");
29091 this.el.dom.setSelectionRange(0, 0);
29097 for (var i = 0; i < lines.length;i++) {
29098 pos += lines[i].length;
29108 pos -= lines[i].length;
29114 this.el.dom.setSelectionRange(pos, pos);
29118 this.el.dom.selectionStart = pos;
29119 this.el.dom.selectionEnd = curr;
29122 "end" : function(e){
29123 e.preventDefault();
29125 var curr = this.el.dom.selectionStart;
29126 var lines = this.getValue().split("\n");
29133 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
29139 for (var i = 0; i < lines.length;i++) {
29141 pos += lines[i].length;
29155 this.el.dom.setSelectionRange(pos, pos);
29159 this.el.dom.selectionStart = curr;
29160 this.el.dom.selectionEnd = pos;
29165 doRelay : function(foo, bar, hname){
29166 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
29172 // if(this.autosave && this.w){
29173 // this.autoSaveFn = setInterval(this.autosave, 1000);
29178 onResize : function(w, h)
29180 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
29185 if(typeof w == 'number'){
29186 var aw = w - this.wrap.getFrameWidth('lr');
29187 this.el.setWidth(this.adjustWidth('textarea', aw));
29190 if(typeof h == 'number'){
29192 for (var i =0; i < this.toolbars.length;i++) {
29193 // fixme - ask toolbars for heights?
29194 tbh += this.toolbars[i].tb.el.getHeight();
29195 if (this.toolbars[i].footer) {
29196 tbh += this.toolbars[i].footer.el.getHeight();
29203 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
29204 ah -= 5; // knock a few pixes off for look..
29206 this.el.setHeight(this.adjustWidth('textarea', ah));
29210 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
29211 this.editorcore.onResize(ew,eh);
29216 * Toggles the editor between standard and source edit mode.
29217 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29219 toggleSourceEdit : function(sourceEditMode)
29221 this.editorcore.toggleSourceEdit(sourceEditMode);
29223 if(this.editorcore.sourceEditMode){
29224 Roo.log('editor - showing textarea');
29227 // Roo.log(this.syncValue());
29228 this.editorcore.syncValue();
29229 this.el.removeClass('x-hidden');
29230 this.el.dom.removeAttribute('tabIndex');
29232 this.el.dom.scrollTop = 0;
29235 for (var i = 0; i < this.toolbars.length; i++) {
29236 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
29237 this.toolbars[i].tb.hide();
29238 this.toolbars[i].footer.hide();
29243 Roo.log('editor - hiding textarea');
29245 // Roo.log(this.pushValue());
29246 this.editorcore.pushValue();
29248 this.el.addClass('x-hidden');
29249 this.el.dom.setAttribute('tabIndex', -1);
29251 for (var i = 0; i < this.toolbars.length; i++) {
29252 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
29253 this.toolbars[i].tb.show();
29254 this.toolbars[i].footer.show();
29258 //this.deferFocus();
29261 this.setSize(this.wrap.getSize());
29262 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
29264 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
29267 // private (for BoxComponent)
29268 adjustSize : Roo.BoxComponent.prototype.adjustSize,
29270 // private (for BoxComponent)
29271 getResizeEl : function(){
29275 // private (for BoxComponent)
29276 getPositionEl : function(){
29281 initEvents : function(){
29282 this.originalValue = this.getValue();
29286 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
29289 markInvalid : Roo.emptyFn,
29291 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
29294 clearInvalid : Roo.emptyFn,
29296 setValue : function(v){
29297 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
29298 this.editorcore.pushValue();
29302 * update the language in the body - really done by core
29303 * @param {String} language - eg. en / ar / zh-CN etc..
29305 updateLanguage : function(lang)
29307 this.language = lang;
29308 this.editorcore.language = lang;
29309 this.editorcore.updateLanguage();
29313 deferFocus : function(){
29314 this.focus.defer(10, this);
29318 focus : function(){
29319 this.editorcore.focus();
29325 onDestroy : function(){
29331 for (var i =0; i < this.toolbars.length;i++) {
29332 // fixme - ask toolbars for heights?
29333 this.toolbars[i].onDestroy();
29336 this.wrap.dom.innerHTML = '';
29337 this.wrap.remove();
29342 onFirstFocus : function(){
29343 //Roo.log("onFirstFocus");
29344 this.editorcore.onFirstFocus();
29345 for (var i =0; i < this.toolbars.length;i++) {
29346 this.toolbars[i].onFirstFocus();
29352 syncValue : function()
29354 this.editorcore.syncValue();
29357 pushValue : function()
29359 this.editorcore.pushValue();
29362 setStylesheets : function(stylesheets)
29364 this.editorcore.setStylesheets(stylesheets);
29367 removeStylesheets : function()
29369 this.editorcore.removeStylesheets();
29373 // hide stuff that is not compatible
29387 * @event specialkey
29391 * @cfg {String} fieldClass @hide
29394 * @cfg {String} focusClass @hide
29397 * @cfg {String} autoCreate @hide
29400 * @cfg {String} inputType @hide
29403 * @cfg {String} invalidClass @hide
29406 * @cfg {String} invalidText @hide
29409 * @cfg {String} msgFx @hide
29412 * @cfg {String} validateOnBlur @hide
29418 * Ext JS Library 1.1.1
29419 * Copyright(c) 2006-2007, Ext JS, LLC.
29425 * @class Roo.form.HtmlEditor.ToolbarStandard
29430 new Roo.form.HtmlEditor({
29433 new Roo.form.HtmlEditorToolbar1({
29434 disable : { fonts: 1 , format: 1, ..., ... , ...],
29440 * @cfg {Object} disable List of elements to disable..
29441 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
29445 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
29448 Roo.form.HtmlEditor.ToolbarStandard = function(config)
29451 Roo.apply(this, config);
29453 // default disabled, based on 'good practice'..
29454 this.disable = this.disable || {};
29455 Roo.applyIf(this.disable, {
29458 specialElements : true
29462 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
29463 // dont call parent... till later.
29466 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
29473 editorcore : false,
29475 * @cfg {Object} disable List of toolbar elements to disable
29482 * @cfg {String} createLinkText The default text for the create link prompt
29484 createLinkText : 'Please enter the URL for the link:',
29486 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
29488 defaultLinkValue : 'http:/'+'/',
29492 * @cfg {Array} fontFamilies An array of available font families
29510 // "á" , ?? a acute?
29515 "°" // , // degrees
29517 // "é" , // e ecute
29518 // "ú" , // u ecute?
29521 specialElements : [
29523 text: "Insert Table",
29526 ihtml : '<table><tr><td>Cell</td></tr></table>'
29530 text: "Insert Image",
29533 ihtml : '<img src="about:blank"/>'
29542 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
29543 "input:submit", "input:button", "select", "textarea", "label" ],
29546 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
29548 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
29557 * @cfg {String} defaultFont default font to use.
29559 defaultFont: 'tahoma',
29561 fontSelect : false,
29564 formatCombo : false,
29566 init : function(editor)
29568 this.editor = editor;
29569 this.editorcore = editor.editorcore ? editor.editorcore : editor;
29570 var editorcore = this.editorcore;
29574 var fid = editorcore.frameId;
29576 function btn(id, toggle, handler){
29577 var xid = fid + '-'+ id ;
29581 cls : 'x-btn-icon x-edit-'+id,
29582 enableToggle:toggle !== false,
29583 scope: _t, // was editor...
29584 handler:handler||_t.relayBtnCmd,
29585 clickEvent:'mousedown',
29586 tooltip: etb.buttonTips[id] || undefined, ///tips ???
29593 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
29595 // stop form submits
29596 tb.el.on('click', function(e){
29597 e.preventDefault(); // what does this do?
29600 if(!this.disable.font) { // && !Roo.isSafari){
29601 /* why no safari for fonts
29602 editor.fontSelect = tb.el.createChild({
29605 cls:'x-font-select',
29606 html: this.createFontOptions()
29609 editor.fontSelect.on('change', function(){
29610 var font = editor.fontSelect.dom.value;
29611 editor.relayCmd('fontname', font);
29612 editor.deferFocus();
29616 editor.fontSelect.dom,
29622 if(!this.disable.formats){
29623 this.formatCombo = new Roo.form.ComboBox({
29624 store: new Roo.data.SimpleStore({
29627 data : this.formats // from states.js
29631 //autoCreate : {tag: "div", size: "20"},
29632 displayField:'tag',
29636 triggerAction: 'all',
29637 emptyText:'Add tag',
29638 selectOnFocus:true,
29641 'select': function(c, r, i) {
29642 editorcore.insertTag(r.get('tag'));
29648 tb.addField(this.formatCombo);
29652 if(!this.disable.format){
29657 btn('strikethrough')
29660 if(!this.disable.fontSize){
29665 btn('increasefontsize', false, editorcore.adjustFont),
29666 btn('decreasefontsize', false, editorcore.adjustFont)
29671 if(!this.disable.colors){
29674 id:editorcore.frameId +'-forecolor',
29675 cls:'x-btn-icon x-edit-forecolor',
29676 clickEvent:'mousedown',
29677 tooltip: this.buttonTips['forecolor'] || undefined,
29679 menu : new Roo.menu.ColorMenu({
29680 allowReselect: true,
29681 focus: Roo.emptyFn,
29684 selectHandler: function(cp, color){
29685 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
29686 editor.deferFocus();
29689 clickEvent:'mousedown'
29692 id:editorcore.frameId +'backcolor',
29693 cls:'x-btn-icon x-edit-backcolor',
29694 clickEvent:'mousedown',
29695 tooltip: this.buttonTips['backcolor'] || undefined,
29697 menu : new Roo.menu.ColorMenu({
29698 focus: Roo.emptyFn,
29701 allowReselect: true,
29702 selectHandler: function(cp, color){
29704 editorcore.execCmd('useCSS', false);
29705 editorcore.execCmd('hilitecolor', color);
29706 editorcore.execCmd('useCSS', true);
29707 editor.deferFocus();
29709 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
29710 Roo.isSafari || Roo.isIE ? '#'+color : color);
29711 editor.deferFocus();
29715 clickEvent:'mousedown'
29720 // now add all the items...
29723 if(!this.disable.alignments){
29726 btn('justifyleft'),
29727 btn('justifycenter'),
29728 btn('justifyright')
29732 //if(!Roo.isSafari){
29733 if(!this.disable.links){
29736 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
29740 if(!this.disable.lists){
29743 btn('insertorderedlist'),
29744 btn('insertunorderedlist')
29747 if(!this.disable.sourceEdit){
29750 btn('sourceedit', true, function(btn){
29751 this.toggleSourceEdit(btn.pressed);
29758 // special menu.. - needs to be tidied up..
29759 if (!this.disable.special) {
29762 cls: 'x-edit-none',
29768 for (var i =0; i < this.specialChars.length; i++) {
29769 smenu.menu.items.push({
29771 html: this.specialChars[i],
29772 handler: function(a,b) {
29773 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
29774 //editor.insertAtCursor(a.html);
29788 if (!this.disable.cleanStyles) {
29790 cls: 'x-btn-icon x-btn-clear',
29796 for (var i =0; i < this.cleanStyles.length; i++) {
29797 cmenu.menu.items.push({
29798 actiontype : this.cleanStyles[i],
29799 html: 'Remove ' + this.cleanStyles[i],
29800 handler: function(a,b) {
29803 var c = Roo.get(editorcore.doc.body);
29804 c.select('[style]').each(function(s) {
29805 s.dom.style.removeProperty(a.actiontype);
29807 editorcore.syncValue();
29812 cmenu.menu.items.push({
29813 actiontype : 'tablewidths',
29814 html: 'Remove Table Widths',
29815 handler: function(a,b) {
29816 editorcore.cleanTableWidths();
29817 editorcore.syncValue();
29821 cmenu.menu.items.push({
29822 actiontype : 'word',
29823 html: 'Remove MS Word Formating',
29824 handler: function(a,b) {
29825 editorcore.cleanWord();
29826 editorcore.syncValue();
29831 cmenu.menu.items.push({
29832 actiontype : 'all',
29833 html: 'Remove All Styles',
29834 handler: function(a,b) {
29836 var c = Roo.get(editorcore.doc.body);
29837 c.select('[style]').each(function(s) {
29838 s.dom.removeAttribute('style');
29840 editorcore.syncValue();
29845 cmenu.menu.items.push({
29846 actiontype : 'all',
29847 html: 'Remove All CSS Classes',
29848 handler: function(a,b) {
29850 var c = Roo.get(editorcore.doc.body);
29851 c.select('[class]').each(function(s) {
29852 s.dom.removeAttribute('class');
29854 editorcore.cleanWord();
29855 editorcore.syncValue();
29860 cmenu.menu.items.push({
29861 actiontype : 'tidy',
29862 html: 'Tidy HTML Source',
29863 handler: function(a,b) {
29864 new Roo.htmleditor.Tidy(editorcore.doc.body);
29865 editorcore.syncValue();
29874 if (!this.disable.specialElements) {
29877 cls: 'x-edit-none',
29882 for (var i =0; i < this.specialElements.length; i++) {
29883 semenu.menu.items.push(
29885 handler: function(a,b) {
29886 editor.insertAtCursor(this.ihtml);
29888 }, this.specialElements[i])
29900 for(var i =0; i< this.btns.length;i++) {
29901 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
29902 b.cls = 'x-edit-none';
29904 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
29905 b.cls += ' x-init-enable';
29908 b.scope = editorcore;
29916 // disable everything...
29918 this.tb.items.each(function(item){
29921 item.id != editorcore.frameId+ '-sourceedit' &&
29922 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
29928 this.rendered = true;
29930 // the all the btns;
29931 editor.on('editorevent', this.updateToolbar, this);
29932 // other toolbars need to implement this..
29933 //editor.on('editmodechange', this.updateToolbar, this);
29937 relayBtnCmd : function(btn) {
29938 this.editorcore.relayCmd(btn.cmd);
29940 // private used internally
29941 createLink : function(){
29942 //Roo.log("create link?");
29943 var ec = this.editorcore;
29944 var ar = ec.getAllAncestors();
29946 for(var i = 0;i< ar.length;i++) {
29947 if (ar[i] && ar[i].nodeName == 'A') {
29955 Roo.MessageBox.show({
29956 title : "Add / Edit Link URL",
29957 msg : "Enter the url for the link",
29958 buttons: Roo.MessageBox.OKCANCEL,
29959 fn: function(btn, url){
29963 if(url && url != 'http:/'+'/'){
29965 n.setAttribute('href', url);
29967 ec.relayCmd('createlink', url);
29973 //multiline: multiline,
29975 value : n ? n.getAttribute('href') : ''
29979 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
29985 * Protected method that will not generally be called directly. It triggers
29986 * a toolbar update by reading the markup state of the current selection in the editor.
29988 updateToolbar: function(){
29990 if(!this.editorcore.activated){
29991 this.editor.onFirstFocus();
29995 var btns = this.tb.items.map,
29996 doc = this.editorcore.doc,
29997 frameId = this.editorcore.frameId;
29999 if(!this.disable.font && !Roo.isSafari){
30001 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
30002 if(name != this.fontSelect.dom.value){
30003 this.fontSelect.dom.value = name;
30007 if(!this.disable.format){
30008 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
30009 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
30010 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
30011 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
30013 if(!this.disable.alignments){
30014 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
30015 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
30016 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
30018 if(!Roo.isSafari && !this.disable.lists){
30019 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
30020 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
30023 var ans = this.editorcore.getAllAncestors();
30024 if (this.formatCombo) {
30027 var store = this.formatCombo.store;
30028 this.formatCombo.setValue("");
30029 for (var i =0; i < ans.length;i++) {
30030 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
30032 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
30040 // hides menus... - so this cant be on a menu...
30041 Roo.menu.MenuMgr.hideAll();
30043 //this.editorsyncValue();
30047 createFontOptions : function(){
30048 var buf = [], fs = this.fontFamilies, ff, lc;
30052 for(var i = 0, len = fs.length; i< len; i++){
30054 lc = ff.toLowerCase();
30056 '<option value="',lc,'" style="font-family:',ff,';"',
30057 (this.defaultFont == lc ? ' selected="true">' : '>'),
30062 return buf.join('');
30065 toggleSourceEdit : function(sourceEditMode){
30067 Roo.log("toolbar toogle");
30068 if(sourceEditMode === undefined){
30069 sourceEditMode = !this.sourceEditMode;
30071 this.sourceEditMode = sourceEditMode === true;
30072 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
30073 // just toggle the button?
30074 if(btn.pressed !== this.sourceEditMode){
30075 btn.toggle(this.sourceEditMode);
30079 if(sourceEditMode){
30080 Roo.log("disabling buttons");
30081 this.tb.items.each(function(item){
30082 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
30088 Roo.log("enabling buttons");
30089 if(this.editorcore.initialized){
30090 this.tb.items.each(function(item){
30093 // initialize 'blocks'
30094 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
30095 Roo.htmleditor.Block.factory(e).updateElement(e);
30101 Roo.log("calling toggole on editor");
30102 // tell the editor that it's been pressed..
30103 this.editor.toggleSourceEdit(sourceEditMode);
30107 * Object collection of toolbar tooltips for the buttons in the editor. The key
30108 * is the command id associated with that button and the value is a valid QuickTips object.
30113 title: 'Bold (Ctrl+B)',
30114 text: 'Make the selected text bold.',
30115 cls: 'x-html-editor-tip'
30118 title: 'Italic (Ctrl+I)',
30119 text: 'Make the selected text italic.',
30120 cls: 'x-html-editor-tip'
30128 title: 'Bold (Ctrl+B)',
30129 text: 'Make the selected text bold.',
30130 cls: 'x-html-editor-tip'
30133 title: 'Italic (Ctrl+I)',
30134 text: 'Make the selected text italic.',
30135 cls: 'x-html-editor-tip'
30138 title: 'Underline (Ctrl+U)',
30139 text: 'Underline the selected text.',
30140 cls: 'x-html-editor-tip'
30143 title: 'Strikethrough',
30144 text: 'Strikethrough the selected text.',
30145 cls: 'x-html-editor-tip'
30147 increasefontsize : {
30148 title: 'Grow Text',
30149 text: 'Increase the font size.',
30150 cls: 'x-html-editor-tip'
30152 decreasefontsize : {
30153 title: 'Shrink Text',
30154 text: 'Decrease the font size.',
30155 cls: 'x-html-editor-tip'
30158 title: 'Text Highlight Color',
30159 text: 'Change the background color of the selected text.',
30160 cls: 'x-html-editor-tip'
30163 title: 'Font Color',
30164 text: 'Change the color of the selected text.',
30165 cls: 'x-html-editor-tip'
30168 title: 'Align Text Left',
30169 text: 'Align text to the left.',
30170 cls: 'x-html-editor-tip'
30173 title: 'Center Text',
30174 text: 'Center text in the editor.',
30175 cls: 'x-html-editor-tip'
30178 title: 'Align Text Right',
30179 text: 'Align text to the right.',
30180 cls: 'x-html-editor-tip'
30182 insertunorderedlist : {
30183 title: 'Bullet List',
30184 text: 'Start a bulleted list.',
30185 cls: 'x-html-editor-tip'
30187 insertorderedlist : {
30188 title: 'Numbered List',
30189 text: 'Start a numbered list.',
30190 cls: 'x-html-editor-tip'
30193 title: 'Hyperlink',
30194 text: 'Make the selected text a hyperlink.',
30195 cls: 'x-html-editor-tip'
30198 title: 'Source Edit',
30199 text: 'Switch to source editing mode.',
30200 cls: 'x-html-editor-tip'
30204 onDestroy : function(){
30207 this.tb.items.each(function(item){
30209 item.menu.removeAll();
30211 item.menu.el.destroy();
30219 onFirstFocus: function() {
30220 this.tb.items.each(function(item){
30229 // <script type="text/javascript">
30232 * Ext JS Library 1.1.1
30233 * Copyright(c) 2006-2007, Ext JS, LLC.
30240 * @class Roo.form.HtmlEditor.ToolbarContext
30245 new Roo.form.HtmlEditor({
30248 { xtype: 'ToolbarStandard', styles : {} }
30249 { xtype: 'ToolbarContext', disable : {} }
30255 * @config : {Object} disable List of elements to disable.. (not done yet.)
30256 * @config : {Object} styles Map of styles available.
30260 Roo.form.HtmlEditor.ToolbarContext = function(config)
30263 Roo.apply(this, config);
30264 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
30265 // dont call parent... till later.
30266 this.styles = this.styles || {};
30271 Roo.form.HtmlEditor.ToolbarContext.types = {
30286 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
30312 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
30383 name : 'selectoptions',
30389 // should we really allow this??
30390 // should this just be
30407 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
30408 Roo.form.HtmlEditor.ToolbarContext.stores = false;
30410 Roo.form.HtmlEditor.ToolbarContext.options = {
30412 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
30413 [ 'Courier New', 'Courier New'],
30414 [ 'Tahoma', 'Tahoma'],
30415 [ 'Times New Roman,serif', 'Times'],
30416 [ 'Verdana','Verdana' ]
30420 // fixme - these need to be configurable..
30423 //Roo.form.HtmlEditor.ToolbarContext.types
30426 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
30433 editorcore : false,
30435 * @cfg {Object} disable List of toolbar elements to disable
30440 * @cfg {Object} styles List of styles
30441 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
30443 * These must be defined in the page, so they get rendered correctly..
30454 init : function(editor)
30456 this.editor = editor;
30457 this.editorcore = editor.editorcore ? editor.editorcore : editor;
30458 var editorcore = this.editorcore;
30460 var fid = editorcore.frameId;
30462 function btn(id, toggle, handler){
30463 var xid = fid + '-'+ id ;
30467 cls : 'x-btn-icon x-edit-'+id,
30468 enableToggle:toggle !== false,
30469 scope: editorcore, // was editor...
30470 handler:handler||editorcore.relayBtnCmd,
30471 clickEvent:'mousedown',
30472 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30476 // create a new element.
30477 var wdiv = editor.wrap.createChild({
30479 }, editor.wrap.dom.firstChild.nextSibling, true);
30481 // can we do this more than once??
30483 // stop form submits
30486 // disable everything...
30487 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
30488 this.toolbars = {};
30489 // block toolbars are built in updateToolbar when needed.
30490 for (var i in ty) {
30492 this.toolbars[i] = this.buildToolbar(ty[i],i);
30494 this.tb = this.toolbars.BODY;
30496 this.buildFooter();
30497 this.footer.show();
30498 editor.on('hide', function( ) { this.footer.hide() }, this);
30499 editor.on('show', function( ) { this.footer.show() }, this);
30502 this.rendered = true;
30504 // the all the btns;
30505 editor.on('editorevent', this.updateToolbar, this);
30506 // other toolbars need to implement this..
30507 //editor.on('editmodechange', this.updateToolbar, this);
30513 * Protected method that will not generally be called directly. It triggers
30514 * a toolbar update by reading the markup state of the current selection in the editor.
30516 * Note you can force an update by calling on('editorevent', scope, false)
30518 updateToolbar: function(editor ,ev, sel)
30522 ev.stopEvent(); // se if we can stop this looping with mutiple events.
30526 // capture mouse up - this is handy for selecting images..
30527 // perhaps should go somewhere else...
30528 if(!this.editorcore.activated){
30529 this.editor.onFirstFocus();
30532 //Roo.log(ev ? ev.target : 'NOTARGET');
30535 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
30536 // selectNode - might want to handle IE?
30541 (ev.type == 'mouseup' || ev.type == 'click' ) &&
30542 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
30543 // they have click on an image...
30544 // let's see if we can change the selection...
30547 // this triggers looping?
30548 //this.editorcore.selectNode(sel);
30552 // this forces an id..
30553 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
30554 e.classList.remove('roo-ed-selection');
30556 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
30557 //Roo.get(node).addClass('roo-ed-selection');
30559 //var updateFooter = sel ? false : true;
30562 var ans = this.editorcore.getAllAncestors();
30565 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
30568 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
30569 sel = sel ? sel : this.editorcore.doc.body;
30570 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
30574 var tn = sel.tagName.toUpperCase();
30575 var lastSel = this.tb.selectedNode;
30576 this.tb.selectedNode = sel;
30577 var left_label = tn;
30579 // ok see if we are editing a block?
30582 // you are not actually selecting the block.
30583 if (sel && sel.hasAttribute('data-block')) {
30585 } else if (sel && sel.closest('[data-block]')) {
30587 db = sel.closest('[data-block]');
30588 //var cepar = sel.closest('[contenteditable=true]');
30589 //if (db && cepar && cepar.tagName != 'BODY') {
30590 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
30596 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
30597 if (db && this.editorcore.enableBlocks) {
30598 block = Roo.htmleditor.Block.factory(db);
30603 db.classList.length > 0 ? db.className + ' ' : ''
30604 ) + 'roo-ed-selection';
30606 // since we removed it earlier... its not there..
30607 tn = 'BLOCK.' + db.getAttribute('data-block');
30609 //this.editorcore.selectNode(db);
30610 if (typeof(this.toolbars[tn]) == 'undefined') {
30611 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
30613 this.toolbars[tn].selectedNode = db;
30614 left_label = block.friendly_name;
30615 ans = this.editorcore.getAllAncestors();
30623 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
30624 return; // no change?
30630 ///console.log("show: " + tn);
30631 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
30635 this.tb.items.first().el.innerHTML = left_label + ': ';
30638 // update attributes
30639 if (block && this.tb.fields) {
30641 this.tb.fields.each(function(e) {
30642 e.setValue(block[e.name]);
30646 } else if (this.tb.fields && this.tb.selectedNode) {
30647 this.tb.fields.each( function(e) {
30649 e.setValue(this.tb.selectedNode.style[e.stylename]);
30652 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
30654 this.updateToolbarStyles(this.tb.selectedNode);
30659 Roo.menu.MenuMgr.hideAll();
30664 // update the footer
30666 this.updateFooter(ans);
30670 updateToolbarStyles : function(sel)
30672 var hasStyles = false;
30673 for(var i in this.styles) {
30679 if (hasStyles && this.tb.hasStyles) {
30680 var st = this.tb.fields.item(0);
30682 st.store.removeAll();
30683 var cn = sel.className.split(/\s+/);
30686 if (this.styles['*']) {
30688 Roo.each(this.styles['*'], function(v) {
30689 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
30692 if (this.styles[tn]) {
30693 Roo.each(this.styles[tn], function(v) {
30694 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
30698 st.store.loadData(avs);
30705 updateFooter : function(ans)
30708 if (ans === false) {
30709 this.footDisp.dom.innerHTML = '';
30713 this.footerEls = ans.reverse();
30714 Roo.each(this.footerEls, function(a,i) {
30715 if (!a) { return; }
30716 html += html.length ? ' > ' : '';
30718 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
30723 var sz = this.footDisp.up('td').getSize();
30724 this.footDisp.dom.style.width = (sz.width -10) + 'px';
30725 this.footDisp.dom.style.marginLeft = '5px';
30727 this.footDisp.dom.style.overflow = 'hidden';
30729 this.footDisp.dom.innerHTML = html;
30736 onDestroy : function(){
30739 this.tb.items.each(function(item){
30741 item.menu.removeAll();
30743 item.menu.el.destroy();
30751 onFirstFocus: function() {
30752 // need to do this for all the toolbars..
30753 this.tb.items.each(function(item){
30757 buildToolbar: function(tlist, nm, friendly_name, block)
30759 var editor = this.editor;
30760 var editorcore = this.editorcore;
30761 // create a new element.
30762 var wdiv = editor.wrap.createChild({
30764 }, editor.wrap.dom.firstChild.nextSibling, true);
30767 var tb = new Roo.Toolbar(wdiv);
30768 ///this.tb = tb; // << this sets the active toolbar..
30769 if (tlist === false && block) {
30770 tlist = block.contextMenu(this);
30773 tb.hasStyles = false;
30776 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
30778 var styles = Array.from(this.styles);
30782 if (styles && styles.length) {
30783 tb.hasStyles = true;
30784 // this needs a multi-select checkbox...
30785 tb.addField( new Roo.form.ComboBox({
30786 store: new Roo.data.SimpleStore({
30788 fields: ['val', 'selected'],
30791 name : '-roo-edit-className',
30792 attrname : 'className',
30793 displayField: 'val',
30797 triggerAction: 'all',
30798 emptyText:'Select Style',
30799 selectOnFocus:true,
30802 'select': function(c, r, i) {
30803 // initial support only for on class per el..
30804 tb.selectedNode.className = r ? r.get('val') : '';
30805 editorcore.syncValue();
30812 var tbc = Roo.form.HtmlEditor.ToolbarContext;
30815 for (var i = 0; i < tlist.length; i++) {
30817 // newer versions will use xtype cfg to create menus.
30818 if (typeof(tlist[i].xtype) != 'undefined') {
30820 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
30826 var item = tlist[i];
30827 tb.add(item.title + ": ");
30830 //optname == used so you can configure the options available..
30831 var opts = item.opts ? item.opts : false;
30832 if (item.optname) { // use the b
30833 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
30838 // opts == pulldown..
30839 tb.addField( new Roo.form.ComboBox({
30840 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
30842 fields: ['val', 'display'],
30845 name : '-roo-edit-' + tlist[i].name,
30847 attrname : tlist[i].name,
30848 stylename : item.style ? item.style : false,
30850 displayField: item.displayField ? item.displayField : 'val',
30851 valueField : 'val',
30853 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
30855 triggerAction: 'all',
30856 emptyText:'Select',
30857 selectOnFocus:true,
30858 width: item.width ? item.width : 130,
30860 'select': function(c, r, i) {
30864 tb.selectedNode.style[c.stylename] = r.get('val');
30865 editorcore.syncValue();
30869 tb.selectedNode.removeAttribute(c.attrname);
30870 editorcore.syncValue();
30873 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
30874 editorcore.syncValue();
30883 tb.addField( new Roo.form.TextField({
30886 //allowBlank:false,
30892 tb.addField( new Roo.form.TextField({
30893 name: '-roo-edit-' + tlist[i].name,
30894 attrname : tlist[i].name,
30900 'change' : function(f, nv, ov) {
30903 tb.selectedNode.setAttribute(f.attrname, nv);
30904 editorcore.syncValue();
30912 var show_delete = !block || block.deleteTitle !== false;
30914 show_delete = false;
30918 text: 'Stylesheets',
30921 click : function ()
30923 _this.editor.fireEvent('stylesheetsclick', _this.editor);
30932 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
30935 click : function ()
30937 var sn = tb.selectedNode;
30939 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
30945 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
30946 if (sn.hasAttribute('data-block')) {
30947 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
30948 sn.parentNode.removeChild(sn);
30950 } else if (sn && sn.tagName != 'BODY') {
30951 // remove and keep parents.
30952 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
30957 var range = editorcore.createRange();
30959 range.setStart(stn,0);
30960 range.setEnd(stn,0);
30961 var selection = editorcore.getSelection();
30962 selection.removeAllRanges();
30963 selection.addRange(range);
30966 //_this.updateToolbar(null, null, pn);
30967 _this.updateToolbar(null, null, null);
30968 _this.updateFooter(false);
30979 tb.el.on('click', function(e){
30980 e.preventDefault(); // what does this do?
30982 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
30985 // dont need to disable them... as they will get hidden
30990 buildFooter : function()
30993 var fel = this.editor.wrap.createChild();
30994 this.footer = new Roo.Toolbar(fel);
30995 // toolbar has scrolly on left / right?
30996 var footDisp= new Roo.Toolbar.Fill();
31002 handler : function() {
31003 _t.footDisp.scrollTo('left',0,true)
31007 this.footer.add( footDisp );
31012 handler : function() {
31014 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
31018 var fel = Roo.get(footDisp.el);
31019 fel.addClass('x-editor-context');
31020 this.footDispWrap = fel;
31021 this.footDispWrap.overflow = 'hidden';
31023 this.footDisp = fel.createChild();
31024 this.footDispWrap.on('click', this.onContextClick, this)
31028 // when the footer contect changes
31029 onContextClick : function (ev,dom)
31031 ev.preventDefault();
31032 var cn = dom.className;
31034 if (!cn.match(/x-ed-loc-/)) {
31037 var n = cn.split('-').pop();
31038 var ans = this.footerEls;
31041 this.editorcore.selectNode(sel);
31044 this.updateToolbar(null, null, sel);
31061 * Ext JS Library 1.1.1
31062 * Copyright(c) 2006-2007, Ext JS, LLC.
31064 * Originally Released Under LGPL - original licence link has changed is not relivant.
31067 * <script type="text/javascript">
31071 * @class Roo.form.BasicForm
31072 * @extends Roo.util.Observable
31073 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
31075 * @param {String/HTMLElement/Roo.Element} el The form element or its id
31076 * @param {Object} config Configuration options
31078 Roo.form.BasicForm = function(el, config){
31079 this.allItems = [];
31080 this.childForms = [];
31081 Roo.apply(this, config);
31083 * The Roo.form.Field items in this form.
31084 * @type MixedCollection
31088 this.items = new Roo.util.MixedCollection(false, function(o){
31089 return o.id || (o.id = Roo.id());
31093 * @event beforeaction
31094 * Fires before any action is performed. Return false to cancel the action.
31095 * @param {Form} this
31096 * @param {Action} action The action to be performed
31098 beforeaction: true,
31100 * @event actionfailed
31101 * Fires when an action fails.
31102 * @param {Form} this
31103 * @param {Action} action The action that failed
31105 actionfailed : true,
31107 * @event actioncomplete
31108 * Fires when an action is completed.
31109 * @param {Form} this
31110 * @param {Action} action The action that completed
31112 actioncomplete : true
31117 Roo.form.BasicForm.superclass.constructor.call(this);
31119 Roo.form.BasicForm.popover.apply();
31122 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
31124 * @cfg {String} method
31125 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
31128 * @cfg {DataReader} reader
31129 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
31130 * This is optional as there is built-in support for processing JSON.
31133 * @cfg {DataReader} errorReader
31134 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
31135 * This is completely optional as there is built-in support for processing JSON.
31138 * @cfg {String} url
31139 * The URL to use for form actions if one isn't supplied in the action options.
31142 * @cfg {Boolean} fileUpload
31143 * Set to true if this form is a file upload.
31147 * @cfg {Object} baseParams
31148 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
31153 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
31158 activeAction : null,
31161 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
31162 * or setValues() data instead of when the form was first created.
31164 trackResetOnLoad : false,
31168 * childForms - used for multi-tab forms
31171 childForms : false,
31174 * allItems - full list of fields.
31180 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
31181 * element by passing it or its id or mask the form itself by passing in true.
31184 waitMsgTarget : false,
31189 disableMask : false,
31192 * @cfg {Boolean} errorMask (true|false) default false
31197 * @cfg {Number} maskOffset Default 100
31202 initEl : function(el){
31203 this.el = Roo.get(el);
31204 this.id = this.el.id || Roo.id();
31205 this.el.on('submit', this.onSubmit, this);
31206 this.el.addClass('x-form');
31210 onSubmit : function(e){
31215 * Returns true if client-side validation on the form is successful.
31218 isValid : function(){
31220 var target = false;
31221 this.items.each(function(f){
31228 if(!target && f.el.isVisible(true)){
31233 if(this.errorMask && !valid){
31234 Roo.form.BasicForm.popover.mask(this, target);
31240 * Returns array of invalid form fields.
31244 invalidFields : function()
31247 this.items.each(function(f){
31260 * DEPRICATED Returns true if any fields in this form have changed since their original load.
31263 isDirty : function(){
31265 this.items.each(function(f){
31275 * Returns true if any fields in this form have changed since their original load. (New version)
31279 hasChanged : function()
31282 this.items.each(function(f){
31283 if(f.hasChanged()){
31292 * Resets all hasChanged to 'false' -
31293 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
31294 * So hasChanged storage is only to be used for this purpose
31297 resetHasChanged : function()
31299 this.items.each(function(f){
31300 f.resetHasChanged();
31307 * Performs a predefined action (submit or load) or custom actions you define on this form.
31308 * @param {String} actionName The name of the action type
31309 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
31310 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
31311 * accept other config options):
31313 Property Type Description
31314 ---------------- --------------- ----------------------------------------------------------------------------------
31315 url String The url for the action (defaults to the form's url)
31316 method String The form method to use (defaults to the form's method, or POST if not defined)
31317 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
31318 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
31319 validate the form on the client (defaults to false)
31321 * @return {BasicForm} this
31323 doAction : function(action, options){
31324 if(typeof action == 'string'){
31325 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
31327 if(this.fireEvent('beforeaction', this, action) !== false){
31328 this.beforeAction(action);
31329 action.run.defer(100, action);
31335 * Shortcut to do a submit action.
31336 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
31337 * @return {BasicForm} this
31339 submit : function(options){
31340 this.doAction('submit', options);
31345 * Shortcut to do a load action.
31346 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
31347 * @return {BasicForm} this
31349 load : function(options){
31350 this.doAction('load', options);
31355 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
31356 * @param {Record} record The record to edit
31357 * @return {BasicForm} this
31359 updateRecord : function(record){
31360 record.beginEdit();
31361 var fs = record.fields;
31362 fs.each(function(f){
31363 var field = this.findField(f.name);
31365 record.set(f.name, field.getValue());
31373 * Loads an Roo.data.Record into this form.
31374 * @param {Record} record The record to load
31375 * @return {BasicForm} this
31377 loadRecord : function(record){
31378 this.setValues(record.data);
31383 beforeAction : function(action){
31384 var o = action.options;
31386 if(!this.disableMask) {
31387 if(this.waitMsgTarget === true){
31388 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
31389 }else if(this.waitMsgTarget){
31390 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
31391 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
31393 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
31401 afterAction : function(action, success){
31402 this.activeAction = null;
31403 var o = action.options;
31405 if(!this.disableMask) {
31406 if(this.waitMsgTarget === true){
31408 }else if(this.waitMsgTarget){
31409 this.waitMsgTarget.unmask();
31411 Roo.MessageBox.updateProgress(1);
31412 Roo.MessageBox.hide();
31420 Roo.callback(o.success, o.scope, [this, action]);
31421 this.fireEvent('actioncomplete', this, action);
31425 // failure condition..
31426 // we have a scenario where updates need confirming.
31427 // eg. if a locking scenario exists..
31428 // we look for { errors : { needs_confirm : true }} in the response.
31430 (typeof(action.result) != 'undefined') &&
31431 (typeof(action.result.errors) != 'undefined') &&
31432 (typeof(action.result.errors.needs_confirm) != 'undefined')
31435 Roo.MessageBox.confirm(
31436 "Change requires confirmation",
31437 action.result.errorMsg,
31442 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
31452 Roo.callback(o.failure, o.scope, [this, action]);
31453 // show an error message if no failed handler is set..
31454 if (!this.hasListener('actionfailed')) {
31455 Roo.MessageBox.alert("Error",
31456 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
31457 action.result.errorMsg :
31458 "Saving Failed, please check your entries or try again"
31462 this.fireEvent('actionfailed', this, action);
31468 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
31469 * @param {String} id The value to search for
31472 findField : function(id){
31473 var field = this.items.get(id);
31475 this.items.each(function(f){
31476 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
31482 return field || null;
31486 * Add a secondary form to this one,
31487 * Used to provide tabbed forms. One form is primary, with hidden values
31488 * which mirror the elements from the other forms.
31490 * @param {Roo.form.Form} form to add.
31493 addForm : function(form)
31496 if (this.childForms.indexOf(form) > -1) {
31500 this.childForms.push(form);
31502 Roo.each(form.allItems, function (fe) {
31504 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
31505 if (this.findField(n)) { // already added..
31508 var add = new Roo.form.Hidden({
31511 add.render(this.el);
31518 * Mark fields in this form invalid in bulk.
31519 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
31520 * @return {BasicForm} this
31522 markInvalid : function(errors){
31523 if(errors instanceof Array){
31524 for(var i = 0, len = errors.length; i < len; i++){
31525 var fieldError = errors[i];
31526 var f = this.findField(fieldError.id);
31528 f.markInvalid(fieldError.msg);
31534 if(typeof errors[id] != 'function' && (field = this.findField(id))){
31535 field.markInvalid(errors[id]);
31539 Roo.each(this.childForms || [], function (f) {
31540 f.markInvalid(errors);
31547 * Set values for fields in this form in bulk.
31548 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
31549 * @return {BasicForm} this
31551 setValues : function(values){
31552 if(values instanceof Array){ // array of objects
31553 for(var i = 0, len = values.length; i < len; i++){
31555 var f = this.findField(v.id);
31557 f.setValue(v.value);
31558 if(this.trackResetOnLoad){
31559 f.originalValue = f.getValue();
31563 }else{ // object hash
31566 if(typeof values[id] != 'function' && (field = this.findField(id))){
31568 if (field.setFromData &&
31569 field.valueField &&
31570 field.displayField &&
31571 // combos' with local stores can
31572 // be queried via setValue()
31573 // to set their value..
31574 (field.store && !field.store.isLocal)
31578 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
31579 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
31580 field.setFromData(sd);
31583 field.setValue(values[id]);
31587 if(this.trackResetOnLoad){
31588 field.originalValue = field.getValue();
31593 this.resetHasChanged();
31596 Roo.each(this.childForms || [], function (f) {
31597 f.setValues(values);
31598 f.resetHasChanged();
31605 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
31606 * they are returned as an array.
31607 * @param {Boolean} asString
31610 getValues : function(asString)
31612 if (this.childForms) {
31613 // copy values from the child forms
31614 Roo.each(this.childForms, function (f) {
31615 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
31620 if (typeof(FormData) != 'undefined' && asString !== true) {
31621 // this relies on a 'recent' version of chrome apparently...
31623 var fd = (new FormData(this.el.dom)).entries();
31625 var ent = fd.next();
31626 while (!ent.done) {
31627 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
31638 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
31639 if(asString === true){
31642 return Roo.urlDecode(fs);
31646 * Returns the fields in this form as an object with key/value pairs.
31647 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
31648 * Normally this will not return readOnly data
31649 * @param {Boolean} with_readonly return readonly field data.
31652 getFieldValues : function(with_readonly)
31654 if (this.childForms) {
31655 // copy values from the child forms
31656 // should this call getFieldValues - probably not as we do not currently copy
31657 // hidden fields when we generate..
31658 Roo.each(this.childForms, function (f) {
31659 this.setValues(f.getFieldValues());
31664 this.items.each(function(f){
31666 if (f.readOnly && with_readonly !== true) {
31667 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
31668 // if a subform contains a copy of them.
31669 // if you have subforms with the same editable data, you will need to copy the data back
31673 if (!f.getName()) {
31676 var v = f.getValue();
31677 if (f.inputType =='radio') {
31678 if (typeof(ret[f.getName()]) == 'undefined') {
31679 ret[f.getName()] = ''; // empty..
31682 if (!f.el.dom.checked) {
31686 v = f.el.dom.value;
31690 // not sure if this supported any more..
31691 if ((typeof(v) == 'object') && f.getRawValue) {
31692 v = f.getRawValue() ; // dates..
31694 // combo boxes where name != hiddenName...
31695 if (f.name != f.getName()) {
31696 ret[f.name] = f.getRawValue();
31698 ret[f.getName()] = v;
31705 * Clears all invalid messages in this form.
31706 * @return {BasicForm} this
31708 clearInvalid : function(){
31709 this.items.each(function(f){
31713 Roo.each(this.childForms || [], function (f) {
31722 * Resets this form.
31723 * @return {BasicForm} this
31725 reset : function(){
31726 this.items.each(function(f){
31730 Roo.each(this.childForms || [], function (f) {
31733 this.resetHasChanged();
31739 * Add Roo.form components to this form.
31740 * @param {Field} field1
31741 * @param {Field} field2 (optional)
31742 * @param {Field} etc (optional)
31743 * @return {BasicForm} this
31746 this.items.addAll(Array.prototype.slice.call(arguments, 0));
31752 * Removes a field from the items collection (does NOT remove its markup).
31753 * @param {Field} field
31754 * @return {BasicForm} this
31756 remove : function(field){
31757 this.items.remove(field);
31762 * Looks at the fields in this form, checks them for an id attribute,
31763 * and calls applyTo on the existing dom element with that id.
31764 * @return {BasicForm} this
31766 render : function(){
31767 this.items.each(function(f){
31768 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
31776 * Calls {@link Ext#apply} for all fields in this form with the passed object.
31777 * @param {Object} values
31778 * @return {BasicForm} this
31780 applyToFields : function(o){
31781 this.items.each(function(f){
31788 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
31789 * @param {Object} values
31790 * @return {BasicForm} this
31792 applyIfToFields : function(o){
31793 this.items.each(function(f){
31801 Roo.BasicForm = Roo.form.BasicForm;
31803 Roo.apply(Roo.form.BasicForm, {
31817 intervalID : false,
31823 if(this.isApplied){
31828 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
31829 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
31830 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
31831 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
31834 this.maskEl.top.enableDisplayMode("block");
31835 this.maskEl.left.enableDisplayMode("block");
31836 this.maskEl.bottom.enableDisplayMode("block");
31837 this.maskEl.right.enableDisplayMode("block");
31839 Roo.get(document.body).on('click', function(){
31843 Roo.get(document.body).on('touchstart', function(){
31847 this.isApplied = true
31850 mask : function(form, target)
31854 this.target = target;
31856 if(!this.form.errorMask || !target.el){
31860 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
31862 var ot = this.target.el.calcOffsetsTo(scrollable);
31864 var scrollTo = ot[1] - this.form.maskOffset;
31866 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
31868 scrollable.scrollTo('top', scrollTo);
31870 var el = this.target.wrap || this.target.el;
31872 var box = el.getBox();
31874 this.maskEl.top.setStyle('position', 'absolute');
31875 this.maskEl.top.setStyle('z-index', 10000);
31876 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
31877 this.maskEl.top.setLeft(0);
31878 this.maskEl.top.setTop(0);
31879 this.maskEl.top.show();
31881 this.maskEl.left.setStyle('position', 'absolute');
31882 this.maskEl.left.setStyle('z-index', 10000);
31883 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
31884 this.maskEl.left.setLeft(0);
31885 this.maskEl.left.setTop(box.y - this.padding);
31886 this.maskEl.left.show();
31888 this.maskEl.bottom.setStyle('position', 'absolute');
31889 this.maskEl.bottom.setStyle('z-index', 10000);
31890 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
31891 this.maskEl.bottom.setLeft(0);
31892 this.maskEl.bottom.setTop(box.bottom + this.padding);
31893 this.maskEl.bottom.show();
31895 this.maskEl.right.setStyle('position', 'absolute');
31896 this.maskEl.right.setStyle('z-index', 10000);
31897 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
31898 this.maskEl.right.setLeft(box.right + this.padding);
31899 this.maskEl.right.setTop(box.y - this.padding);
31900 this.maskEl.right.show();
31902 this.intervalID = window.setInterval(function() {
31903 Roo.form.BasicForm.popover.unmask();
31906 window.onwheel = function(){ return false;};
31908 (function(){ this.isMasked = true; }).defer(500, this);
31912 unmask : function()
31914 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
31918 this.maskEl.top.setStyle('position', 'absolute');
31919 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
31920 this.maskEl.top.hide();
31922 this.maskEl.left.setStyle('position', 'absolute');
31923 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
31924 this.maskEl.left.hide();
31926 this.maskEl.bottom.setStyle('position', 'absolute');
31927 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
31928 this.maskEl.bottom.hide();
31930 this.maskEl.right.setStyle('position', 'absolute');
31931 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
31932 this.maskEl.right.hide();
31934 window.onwheel = function(){ return true;};
31936 if(this.intervalID){
31937 window.clearInterval(this.intervalID);
31938 this.intervalID = false;
31941 this.isMasked = false;
31949 * Ext JS Library 1.1.1
31950 * Copyright(c) 2006-2007, Ext JS, LLC.
31952 * Originally Released Under LGPL - original licence link has changed is not relivant.
31955 * <script type="text/javascript">
31959 * @class Roo.form.Form
31960 * @extends Roo.form.BasicForm
31961 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
31962 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
31964 * @param {Object} config Configuration options
31966 Roo.form.Form = function(config){
31968 if (config.items) {
31969 xitems = config.items;
31970 delete config.items;
31974 Roo.form.Form.superclass.constructor.call(this, null, config);
31975 this.url = this.url || this.action;
31977 this.root = new Roo.form.Layout(Roo.applyIf({
31981 this.active = this.root;
31983 * Array of all the buttons that have been added to this form via {@link addButton}
31987 this.allItems = [];
31990 * @event clientvalidation
31991 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
31992 * @param {Form} this
31993 * @param {Boolean} valid true if the form has passed client-side validation
31995 clientvalidation: true,
31998 * Fires when the form is rendered
31999 * @param {Roo.form.Form} form
32004 if (this.progressUrl) {
32005 // push a hidden field onto the list of fields..
32009 name : 'UPLOAD_IDENTIFIER'
32014 Roo.each(xitems, this.addxtype, this);
32018 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
32020 * @cfg {Roo.Button} buttons[] buttons at bottom of form
32024 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
32027 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
32030 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
32032 buttonAlign:'center',
32035 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
32040 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
32041 * This property cascades to child containers if not set.
32046 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
32047 * fires a looping event with that state. This is required to bind buttons to the valid
32048 * state using the config value formBind:true on the button.
32050 monitorValid : false,
32053 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
32058 * @cfg {String} progressUrl - Url to return progress data
32061 progressUrl : false,
32063 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
32064 * sending a formdata with extra parameters - eg uploaded elements.
32070 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
32071 * fields are added and the column is closed. If no fields are passed the column remains open
32072 * until end() is called.
32073 * @param {Object} config The config to pass to the column
32074 * @param {Field} field1 (optional)
32075 * @param {Field} field2 (optional)
32076 * @param {Field} etc (optional)
32077 * @return Column The column container object
32079 column : function(c){
32080 var col = new Roo.form.Column(c);
32082 if(arguments.length > 1){ // duplicate code required because of Opera
32083 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
32090 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
32091 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
32092 * until end() is called.
32093 * @param {Object} config The config to pass to the fieldset
32094 * @param {Field} field1 (optional)
32095 * @param {Field} field2 (optional)
32096 * @param {Field} etc (optional)
32097 * @return FieldSet The fieldset container object
32099 fieldset : function(c){
32100 var fs = new Roo.form.FieldSet(c);
32102 if(arguments.length > 1){ // duplicate code required because of Opera
32103 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
32110 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
32111 * fields are added and the container is closed. If no fields are passed the container remains open
32112 * until end() is called.
32113 * @param {Object} config The config to pass to the Layout
32114 * @param {Field} field1 (optional)
32115 * @param {Field} field2 (optional)
32116 * @param {Field} etc (optional)
32117 * @return Layout The container object
32119 container : function(c){
32120 var l = new Roo.form.Layout(c);
32122 if(arguments.length > 1){ // duplicate code required because of Opera
32123 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
32130 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
32131 * @param {Object} container A Roo.form.Layout or subclass of Layout
32132 * @return {Form} this
32134 start : function(c){
32135 // cascade label info
32136 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
32137 this.active.stack.push(c);
32138 c.ownerCt = this.active;
32144 * Closes the current open container
32145 * @return {Form} this
32148 if(this.active == this.root){
32151 this.active = this.active.ownerCt;
32156 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
32157 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
32158 * as the label of the field.
32159 * @param {Field} field1
32160 * @param {Field} field2 (optional)
32161 * @param {Field} etc. (optional)
32162 * @return {Form} this
32165 this.active.stack.push.apply(this.active.stack, arguments);
32166 this.allItems.push.apply(this.allItems,arguments);
32168 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
32169 if(a[i].isFormField){
32174 Roo.form.Form.superclass.add.apply(this, r);
32184 * Find any element that has been added to a form, using it's ID or name
32185 * This can include framesets, columns etc. along with regular fields..
32186 * @param {String} id - id or name to find.
32188 * @return {Element} e - or false if nothing found.
32190 findbyId : function(id)
32196 Roo.each(this.allItems, function(f){
32197 if (f.id == id || f.name == id ){
32208 * Render this form into the passed container. This should only be called once!
32209 * @param {String/HTMLElement/Element} container The element this component should be rendered into
32210 * @return {Form} this
32212 render : function(ct)
32218 var o = this.autoCreate || {
32220 method : this.method || 'POST',
32221 id : this.id || Roo.id()
32223 this.initEl(ct.createChild(o));
32225 this.root.render(this.el);
32229 this.items.each(function(f){
32230 f.render('x-form-el-'+f.id);
32233 if(this.buttons.length > 0){
32234 // tables are required to maintain order and for correct IE layout
32235 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
32236 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
32237 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32239 var tr = tb.getElementsByTagName('tr')[0];
32240 for(var i = 0, len = this.buttons.length; i < len; i++) {
32241 var b = this.buttons[i];
32242 var td = document.createElement('td');
32243 td.className = 'x-form-btn-td';
32244 b.render(tr.appendChild(td));
32247 if(this.monitorValid){ // initialize after render
32248 this.startMonitoring();
32250 this.fireEvent('rendered', this);
32255 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
32256 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32257 * object or a valid Roo.DomHelper element config
32258 * @param {Function} handler The function called when the button is clicked
32259 * @param {Object} scope (optional) The scope of the handler function
32260 * @return {Roo.Button}
32262 addButton : function(config, handler, scope){
32266 minWidth: this.minButtonWidth,
32269 if(typeof config == "string"){
32272 Roo.apply(bc, config);
32274 var btn = new Roo.Button(null, bc);
32275 this.buttons.push(btn);
32280 * Adds a series of form elements (using the xtype property as the factory method.
32281 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
32282 * @param {Object} config
32285 addxtype : function()
32287 var ar = Array.prototype.slice.call(arguments, 0);
32289 for(var i = 0; i < ar.length; i++) {
32291 continue; // skip -- if this happends something invalid got sent, we
32292 // should ignore it, as basically that interface element will not show up
32293 // and that should be pretty obvious!!
32296 if (Roo.form[ar[i].xtype]) {
32298 var fe = Roo.factory(ar[i], Roo.form);
32304 fe.store.form = this;
32309 this.allItems.push(fe);
32310 if (fe.items && fe.addxtype) {
32311 fe.addxtype.apply(fe, fe.items);
32321 // console.log('adding ' + ar[i].xtype);
32323 if (ar[i].xtype == 'Button') {
32324 //console.log('adding button');
32325 //console.log(ar[i]);
32326 this.addButton(ar[i]);
32327 this.allItems.push(fe);
32331 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
32332 alert('end is not supported on xtype any more, use items');
32334 // //console.log('adding end');
32342 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
32343 * option "monitorValid"
32345 startMonitoring : function(){
32348 Roo.TaskMgr.start({
32349 run : this.bindHandler,
32350 interval : this.monitorPoll || 200,
32357 * Stops monitoring of the valid state of this form
32359 stopMonitoring : function(){
32360 this.bound = false;
32364 bindHandler : function(){
32366 return false; // stops binding
32369 this.items.each(function(f){
32370 if(!f.isValid(true)){
32375 for(var i = 0, len = this.buttons.length; i < len; i++){
32376 var btn = this.buttons[i];
32377 if(btn.formBind === true && btn.disabled === valid){
32378 btn.setDisabled(!valid);
32381 this.fireEvent('clientvalidation', this, valid);
32395 Roo.Form = Roo.form.Form;
32398 * Ext JS Library 1.1.1
32399 * Copyright(c) 2006-2007, Ext JS, LLC.
32401 * Originally Released Under LGPL - original licence link has changed is not relivant.
32404 * <script type="text/javascript">
32407 // as we use this in bootstrap.
32408 Roo.namespace('Roo.form');
32410 * @class Roo.form.Action
32411 * Internal Class used to handle form actions
32413 * @param {Roo.form.BasicForm} el The form element or its id
32414 * @param {Object} config Configuration options
32419 // define the action interface
32420 Roo.form.Action = function(form, options){
32422 this.options = options || {};
32425 * Client Validation Failed
32428 Roo.form.Action.CLIENT_INVALID = 'client';
32430 * Server Validation Failed
32433 Roo.form.Action.SERVER_INVALID = 'server';
32435 * Connect to Server Failed
32438 Roo.form.Action.CONNECT_FAILURE = 'connect';
32440 * Reading Data from Server Failed
32443 Roo.form.Action.LOAD_FAILURE = 'load';
32445 Roo.form.Action.prototype = {
32447 failureType : undefined,
32448 response : undefined,
32449 result : undefined,
32451 // interface method
32452 run : function(options){
32456 // interface method
32457 success : function(response){
32461 // interface method
32462 handleResponse : function(response){
32466 // default connection failure
32467 failure : function(response){
32469 this.response = response;
32470 this.failureType = Roo.form.Action.CONNECT_FAILURE;
32471 this.form.afterAction(this, false);
32474 processResponse : function(response){
32475 this.response = response;
32476 if(!response.responseText){
32479 this.result = this.handleResponse(response);
32480 return this.result;
32483 // utility functions used internally
32484 getUrl : function(appendParams){
32485 var url = this.options.url || this.form.url || this.form.el.dom.action;
32487 var p = this.getParams();
32489 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
32495 getMethod : function(){
32496 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
32499 getParams : function(){
32500 var bp = this.form.baseParams;
32501 var p = this.options.params;
32503 if(typeof p == "object"){
32504 p = Roo.urlEncode(Roo.applyIf(p, bp));
32505 }else if(typeof p == 'string' && bp){
32506 p += '&' + Roo.urlEncode(bp);
32509 p = Roo.urlEncode(bp);
32514 createCallback : function(){
32516 success: this.success,
32517 failure: this.failure,
32519 timeout: (this.form.timeout*1000),
32520 upload: this.form.fileUpload ? this.success : undefined
32525 Roo.form.Action.Submit = function(form, options){
32526 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
32529 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
32532 haveProgress : false,
32533 uploadComplete : false,
32535 // uploadProgress indicator.
32536 uploadProgress : function()
32538 if (!this.form.progressUrl) {
32542 if (!this.haveProgress) {
32543 Roo.MessageBox.progress("Uploading", "Uploading");
32545 if (this.uploadComplete) {
32546 Roo.MessageBox.hide();
32550 this.haveProgress = true;
32552 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
32554 var c = new Roo.data.Connection();
32556 url : this.form.progressUrl,
32561 success : function(req){
32562 //console.log(data);
32566 rdata = Roo.decode(req.responseText)
32568 Roo.log("Invalid data from server..");
32572 if (!rdata || !rdata.success) {
32574 Roo.MessageBox.alert(Roo.encode(rdata));
32577 var data = rdata.data;
32579 if (this.uploadComplete) {
32580 Roo.MessageBox.hide();
32585 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
32586 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
32589 this.uploadProgress.defer(2000,this);
32592 failure: function(data) {
32593 Roo.log('progress url failed ');
32604 // run get Values on the form, so it syncs any secondary forms.
32605 this.form.getValues();
32607 var o = this.options;
32608 var method = this.getMethod();
32609 var isPost = method == 'POST';
32610 if(o.clientValidation === false || this.form.isValid()){
32612 if (this.form.progressUrl) {
32613 this.form.findField('UPLOAD_IDENTIFIER').setValue(
32614 (new Date() * 1) + '' + Math.random());
32619 Roo.Ajax.request(Roo.apply(this.createCallback(), {
32620 form:this.form.el.dom,
32621 url:this.getUrl(!isPost),
32623 params:isPost ? this.getParams() : null,
32624 isUpload: this.form.fileUpload,
32625 formData : this.form.formData
32628 this.uploadProgress();
32630 }else if (o.clientValidation !== false){ // client validation failed
32631 this.failureType = Roo.form.Action.CLIENT_INVALID;
32632 this.form.afterAction(this, false);
32636 success : function(response)
32638 this.uploadComplete= true;
32639 if (this.haveProgress) {
32640 Roo.MessageBox.hide();
32644 var result = this.processResponse(response);
32645 if(result === true || result.success){
32646 this.form.afterAction(this, true);
32650 this.form.markInvalid(result.errors);
32651 this.failureType = Roo.form.Action.SERVER_INVALID;
32653 this.form.afterAction(this, false);
32655 failure : function(response)
32657 this.uploadComplete= true;
32658 if (this.haveProgress) {
32659 Roo.MessageBox.hide();
32662 this.response = response;
32663 this.failureType = Roo.form.Action.CONNECT_FAILURE;
32664 this.form.afterAction(this, false);
32667 handleResponse : function(response){
32668 if(this.form.errorReader){
32669 var rs = this.form.errorReader.read(response);
32672 for(var i = 0, len = rs.records.length; i < len; i++) {
32673 var r = rs.records[i];
32674 errors[i] = r.data;
32677 if(errors.length < 1){
32681 success : rs.success,
32687 ret = Roo.decode(response.responseText);
32691 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
32701 Roo.form.Action.Load = function(form, options){
32702 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
32703 this.reader = this.form.reader;
32706 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
32711 Roo.Ajax.request(Roo.apply(
32712 this.createCallback(), {
32713 method:this.getMethod(),
32714 url:this.getUrl(false),
32715 params:this.getParams()
32719 success : function(response){
32721 var result = this.processResponse(response);
32722 if(result === true || !result.success || !result.data){
32723 this.failureType = Roo.form.Action.LOAD_FAILURE;
32724 this.form.afterAction(this, false);
32727 this.form.clearInvalid();
32728 this.form.setValues(result.data);
32729 this.form.afterAction(this, true);
32732 handleResponse : function(response){
32733 if(this.form.reader){
32734 var rs = this.form.reader.read(response);
32735 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
32737 success : rs.success,
32741 return Roo.decode(response.responseText);
32745 Roo.form.Action.ACTION_TYPES = {
32746 'load' : Roo.form.Action.Load,
32747 'submit' : Roo.form.Action.Submit
32750 * Ext JS Library 1.1.1
32751 * Copyright(c) 2006-2007, Ext JS, LLC.
32753 * Originally Released Under LGPL - original licence link has changed is not relivant.
32756 * <script type="text/javascript">
32760 * @class Roo.form.Layout
32761 * @extends Roo.Component
32762 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
32763 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
32765 * @param {Object} config Configuration options
32767 Roo.form.Layout = function(config){
32769 if (config.items) {
32770 xitems = config.items;
32771 delete config.items;
32773 Roo.form.Layout.superclass.constructor.call(this, config);
32775 Roo.each(xitems, this.addxtype, this);
32779 Roo.extend(Roo.form.Layout, Roo.Component, {
32781 * @cfg {String/Object} autoCreate
32782 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
32785 * @cfg {String/Object/Function} style
32786 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
32787 * a function which returns such a specification.
32790 * @cfg {String} labelAlign (left|top|right)
32791 * Valid values are "left," "top" and "right" (defaults to "left")
32794 * @cfg {Number} labelWidth
32795 * Fixed width in pixels of all field labels (defaults to undefined)
32798 * @cfg {Boolean} clear
32799 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
32803 * @cfg {String} labelSeparator
32804 * The separator to use after field labels (defaults to ':')
32806 labelSeparator : ':',
32808 * @cfg {Boolean} hideLabels
32809 * True to suppress the display of field labels in this layout (defaults to false)
32811 hideLabels : false,
32814 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
32819 onRender : function(ct, position){
32820 if(this.el){ // from markup
32821 this.el = Roo.get(this.el);
32822 }else { // generate
32823 var cfg = this.getAutoCreate();
32824 this.el = ct.createChild(cfg, position);
32827 this.el.applyStyles(this.style);
32829 if(this.labelAlign){
32830 this.el.addClass('x-form-label-'+this.labelAlign);
32832 if(this.hideLabels){
32833 this.labelStyle = "display:none";
32834 this.elementStyle = "padding-left:0;";
32836 if(typeof this.labelWidth == 'number'){
32837 this.labelStyle = "width:"+this.labelWidth+"px;";
32838 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
32840 if(this.labelAlign == 'top'){
32841 this.labelStyle = "width:auto;";
32842 this.elementStyle = "padding-left:0;";
32845 var stack = this.stack;
32846 var slen = stack.length;
32848 if(!this.fieldTpl){
32849 var t = new Roo.Template(
32850 '<div class="x-form-item {5}">',
32851 '<label for="{0}" style="{2}">{1}{4}</label>',
32852 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
32854 '</div><div class="x-form-clear-left"></div>'
32856 t.disableFormats = true;
32858 Roo.form.Layout.prototype.fieldTpl = t;
32860 for(var i = 0; i < slen; i++) {
32861 if(stack[i].isFormField){
32862 this.renderField(stack[i]);
32864 this.renderComponent(stack[i]);
32869 this.el.createChild({cls:'x-form-clear'});
32874 renderField : function(f){
32875 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
32878 f.labelStyle||this.labelStyle||'', //2
32879 this.elementStyle||'', //3
32880 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
32881 f.itemCls||this.itemCls||'' //5
32882 ], true).getPrevSibling());
32886 renderComponent : function(c){
32887 c.render(c.isLayout ? this.el : this.el.createChild());
32890 * Adds a object form elements (using the xtype property as the factory method.)
32891 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
32892 * @param {Object} config
32894 addxtype : function(o)
32896 // create the lement.
32897 o.form = this.form;
32898 var fe = Roo.factory(o, Roo.form);
32899 this.form.allItems.push(fe);
32900 this.stack.push(fe);
32902 if (fe.isFormField) {
32903 this.form.items.add(fe);
32912 * @class Roo.form.Column
32913 * @extends Roo.form.Layout
32914 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
32915 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
32917 * @param {Object} config Configuration options
32919 Roo.form.Column = function(config){
32920 Roo.form.Column.superclass.constructor.call(this, config);
32923 Roo.extend(Roo.form.Column, Roo.form.Layout, {
32925 * @cfg {Number/String} width
32926 * The fixed width of the column in pixels or CSS value (defaults to "auto")
32929 * @cfg {String/Object} autoCreate
32930 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
32934 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
32937 onRender : function(ct, position){
32938 Roo.form.Column.superclass.onRender.call(this, ct, position);
32940 this.el.setWidth(this.width);
32946 * @class Roo.form.Row
32947 * @extends Roo.form.Layout
32948 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
32949 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
32951 * @param {Object} config Configuration options
32955 Roo.form.Row = function(config){
32956 Roo.form.Row.superclass.constructor.call(this, config);
32959 Roo.extend(Roo.form.Row, Roo.form.Layout, {
32961 * @cfg {Number/String} width
32962 * The fixed width of the column in pixels or CSS value (defaults to "auto")
32965 * @cfg {Number/String} height
32966 * The fixed height of the column in pixels or CSS value (defaults to "auto")
32968 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
32972 onRender : function(ct, position){
32973 //console.log('row render');
32975 var t = new Roo.Template(
32976 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
32977 '<label for="{0}" style="{2}">{1}{4}</label>',
32978 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
32982 t.disableFormats = true;
32984 Roo.form.Layout.prototype.rowTpl = t;
32986 this.fieldTpl = this.rowTpl;
32988 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
32989 var labelWidth = 100;
32991 if ((this.labelAlign != 'top')) {
32992 if (typeof this.labelWidth == 'number') {
32993 labelWidth = this.labelWidth
32995 this.padWidth = 20 + labelWidth;
32999 Roo.form.Column.superclass.onRender.call(this, ct, position);
33001 this.el.setWidth(this.width);
33004 this.el.setHeight(this.height);
33009 renderField : function(f){
33010 f.fieldEl = this.fieldTpl.append(this.el, [
33011 f.id, f.fieldLabel,
33012 f.labelStyle||this.labelStyle||'',
33013 this.elementStyle||'',
33014 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
33015 f.itemCls||this.itemCls||'',
33016 f.width ? f.width + this.padWidth : 160 + this.padWidth
33023 * @class Roo.form.FieldSet
33024 * @extends Roo.form.Layout
33025 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
33026 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
33028 * @param {Object} config Configuration options
33030 Roo.form.FieldSet = function(config){
33031 Roo.form.FieldSet.superclass.constructor.call(this, config);
33034 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
33036 * @cfg {String} legend
33037 * The text to display as the legend for the FieldSet (defaults to '')
33040 * @cfg {String/Object} autoCreate
33041 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
33045 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
33048 onRender : function(ct, position){
33049 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
33051 this.setLegend(this.legend);
33056 setLegend : function(text){
33058 this.el.child('legend').update(text);
33063 * Ext JS Library 1.1.1
33064 * Copyright(c) 2006-2007, Ext JS, LLC.
33066 * Originally Released Under LGPL - original licence link has changed is not relivant.
33069 * <script type="text/javascript">
33072 * @class Roo.form.VTypes
33073 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
33076 Roo.form.VTypes = function(){
33077 // closure these in so they are only created once.
33078 var alpha = /^[a-zA-Z_]+$/;
33079 var alphanum = /^[a-zA-Z0-9_]+$/;
33080 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
33081 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
33083 // All these messages and functions are configurable
33086 * The function used to validate email addresses
33087 * @param {String} value The email address
33089 'email' : function(v){
33090 return email.test(v);
33093 * The error text to display when the email validation function returns false
33096 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
33098 * The keystroke filter mask to be applied on email input
33101 'emailMask' : /[a-z0-9_\.\-@]/i,
33104 * The function used to validate URLs
33105 * @param {String} value The URL
33107 'url' : function(v){
33108 return url.test(v);
33111 * The error text to display when the url validation function returns false
33114 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
33117 * The function used to validate alpha values
33118 * @param {String} value The value
33120 'alpha' : function(v){
33121 return alpha.test(v);
33124 * The error text to display when the alpha validation function returns false
33127 'alphaText' : 'This field should only contain letters and _',
33129 * The keystroke filter mask to be applied on alpha input
33132 'alphaMask' : /[a-z_]/i,
33135 * The function used to validate alphanumeric values
33136 * @param {String} value The value
33138 'alphanum' : function(v){
33139 return alphanum.test(v);
33142 * The error text to display when the alphanumeric validation function returns false
33145 'alphanumText' : 'This field should only contain letters, numbers and _',
33147 * The keystroke filter mask to be applied on alphanumeric input
33150 'alphanumMask' : /[a-z0-9_]/i
33152 }();//<script type="text/javascript">
33155 * @class Roo.form.FCKeditor
33156 * @extends Roo.form.TextArea
33157 * Wrapper around the FCKEditor http://www.fckeditor.net
33159 * Creates a new FCKeditor
33160 * @param {Object} config Configuration options
33162 Roo.form.FCKeditor = function(config){
33163 Roo.form.FCKeditor.superclass.constructor.call(this, config);
33166 * @event editorinit
33167 * Fired when the editor is initialized - you can add extra handlers here..
33168 * @param {FCKeditor} this
33169 * @param {Object} the FCK object.
33176 Roo.form.FCKeditor.editors = { };
33177 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
33179 //defaultAutoCreate : {
33180 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
33184 * @cfg {Object} fck options - see fck manual for details.
33189 * @cfg {Object} fck toolbar set (Basic or Default)
33191 toolbarSet : 'Basic',
33193 * @cfg {Object} fck BasePath
33195 basePath : '/fckeditor/',
33203 onRender : function(ct, position)
33206 this.defaultAutoCreate = {
33208 style:"width:300px;height:60px;",
33209 autocomplete: "new-password"
33212 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
33215 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
33216 if(this.preventScrollbars){
33217 this.el.setStyle("overflow", "hidden");
33219 this.el.setHeight(this.growMin);
33222 //console.log('onrender' + this.getId() );
33223 Roo.form.FCKeditor.editors[this.getId()] = this;
33226 this.replaceTextarea() ;
33230 getEditor : function() {
33231 return this.fckEditor;
33234 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
33235 * @param {Mixed} value The value to set
33239 setValue : function(value)
33241 //console.log('setValue: ' + value);
33243 if(typeof(value) == 'undefined') { // not sure why this is happending...
33246 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
33248 //if(!this.el || !this.getEditor()) {
33249 // this.value = value;
33250 //this.setValue.defer(100,this,[value]);
33254 if(!this.getEditor()) {
33258 this.getEditor().SetData(value);
33265 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
33266 * @return {Mixed} value The field value
33268 getValue : function()
33271 if (this.frame && this.frame.dom.style.display == 'none') {
33272 return Roo.form.FCKeditor.superclass.getValue.call(this);
33275 if(!this.el || !this.getEditor()) {
33277 // this.getValue.defer(100,this);
33282 var value=this.getEditor().GetData();
33283 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
33284 return Roo.form.FCKeditor.superclass.getValue.call(this);
33290 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
33291 * @return {Mixed} value The field value
33293 getRawValue : function()
33295 if (this.frame && this.frame.dom.style.display == 'none') {
33296 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
33299 if(!this.el || !this.getEditor()) {
33300 //this.getRawValue.defer(100,this);
33307 var value=this.getEditor().GetData();
33308 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
33309 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
33313 setSize : function(w,h) {
33317 //if (this.frame && this.frame.dom.style.display == 'none') {
33318 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
33321 //if(!this.el || !this.getEditor()) {
33322 // this.setSize.defer(100,this, [w,h]);
33328 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
33330 this.frame.dom.setAttribute('width', w);
33331 this.frame.dom.setAttribute('height', h);
33332 this.frame.setSize(w,h);
33336 toggleSourceEdit : function(value) {
33340 this.el.dom.style.display = value ? '' : 'none';
33341 this.frame.dom.style.display = value ? 'none' : '';
33346 focus: function(tag)
33348 if (this.frame.dom.style.display == 'none') {
33349 return Roo.form.FCKeditor.superclass.focus.call(this);
33351 if(!this.el || !this.getEditor()) {
33352 this.focus.defer(100,this, [tag]);
33359 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
33360 this.getEditor().Focus();
33362 if (!this.getEditor().Selection.GetSelection()) {
33363 this.focus.defer(100,this, [tag]);
33368 var r = this.getEditor().EditorDocument.createRange();
33369 r.setStart(tgs[0],0);
33370 r.setEnd(tgs[0],0);
33371 this.getEditor().Selection.GetSelection().removeAllRanges();
33372 this.getEditor().Selection.GetSelection().addRange(r);
33373 this.getEditor().Focus();
33380 replaceTextarea : function()
33382 if ( document.getElementById( this.getId() + '___Frame' ) ) {
33385 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
33387 // We must check the elements firstly using the Id and then the name.
33388 var oTextarea = document.getElementById( this.getId() );
33390 var colElementsByName = document.getElementsByName( this.getId() ) ;
33392 oTextarea.style.display = 'none' ;
33394 if ( oTextarea.tabIndex ) {
33395 this.TabIndex = oTextarea.tabIndex ;
33398 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
33399 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
33400 this.frame = Roo.get(this.getId() + '___Frame')
33403 _getConfigHtml : function()
33407 for ( var o in this.fckconfig ) {
33408 sConfig += sConfig.length > 0 ? '&' : '';
33409 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
33412 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
33416 _getIFrameHtml : function()
33418 var sFile = 'fckeditor.html' ;
33419 /* no idea what this is about..
33422 if ( (/fcksource=true/i).test( window.top.location.search ) )
33423 sFile = 'fckeditor.original.html' ;
33428 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
33429 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
33432 var html = '<iframe id="' + this.getId() +
33433 '___Frame" src="' + sLink +
33434 '" width="' + this.width +
33435 '" height="' + this.height + '"' +
33436 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
33437 ' frameborder="0" scrolling="no"></iframe>' ;
33442 _insertHtmlBefore : function( html, element )
33444 if ( element.insertAdjacentHTML ) {
33446 element.insertAdjacentHTML( 'beforeBegin', html ) ;
33448 var oRange = document.createRange() ;
33449 oRange.setStartBefore( element ) ;
33450 var oFragment = oRange.createContextualFragment( html );
33451 element.parentNode.insertBefore( oFragment, element ) ;
33464 //Roo.reg('fckeditor', Roo.form.FCKeditor);
33466 function FCKeditor_OnComplete(editorInstance){
33467 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
33468 f.fckEditor = editorInstance;
33469 //console.log("loaded");
33470 f.fireEvent('editorinit', f, editorInstance);
33490 //<script type="text/javascript">
33492 * @class Roo.form.GridField
33493 * @extends Roo.form.Field
33494 * Embed a grid (or editable grid into a form)
33497 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
33499 * xgrid.store = Roo.data.Store
33500 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
33501 * xgrid.store.reader = Roo.data.JsonReader
33505 * Creates a new GridField
33506 * @param {Object} config Configuration options
33508 Roo.form.GridField = function(config){
33509 Roo.form.GridField.superclass.constructor.call(this, config);
33513 Roo.extend(Roo.form.GridField, Roo.form.Field, {
33515 * @cfg {Number} width - used to restrict width of grid..
33519 * @cfg {Number} height - used to restrict height of grid..
33523 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
33529 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
33530 * {tag: "input", type: "checkbox", autocomplete: "off"})
33532 // defaultAutoCreate : { tag: 'div' },
33533 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
33535 * @cfg {String} addTitle Text to include for adding a title.
33539 onResize : function(){
33540 Roo.form.Field.superclass.onResize.apply(this, arguments);
33543 initEvents : function(){
33544 // Roo.form.Checkbox.superclass.initEvents.call(this);
33545 // has no events...
33550 getResizeEl : function(){
33554 getPositionEl : function(){
33559 onRender : function(ct, position){
33561 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
33562 var style = this.style;
33565 Roo.form.GridField.superclass.onRender.call(this, ct, position);
33566 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
33567 this.viewEl = this.wrap.createChild({ tag: 'div' });
33569 this.viewEl.applyStyles(style);
33572 this.viewEl.setWidth(this.width);
33575 this.viewEl.setHeight(this.height);
33577 //if(this.inputValue !== undefined){
33578 //this.setValue(this.value);
33581 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
33584 this.grid.render();
33585 this.grid.getDataSource().on('remove', this.refreshValue, this);
33586 this.grid.getDataSource().on('update', this.refreshValue, this);
33587 this.grid.on('afteredit', this.refreshValue, this);
33593 * Sets the value of the item.
33594 * @param {String} either an object or a string..
33596 setValue : function(v){
33598 v = v || []; // empty set..
33599 // this does not seem smart - it really only affects memoryproxy grids..
33600 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
33601 var ds = this.grid.getDataSource();
33602 // assumes a json reader..
33604 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
33605 ds.loadData( data);
33607 // clear selection so it does not get stale.
33608 if (this.grid.sm) {
33609 this.grid.sm.clearSelections();
33612 Roo.form.GridField.superclass.setValue.call(this, v);
33613 this.refreshValue();
33614 // should load data in the grid really....
33618 refreshValue: function() {
33620 this.grid.getDataSource().each(function(r) {
33623 this.el.dom.value = Roo.encode(val);
33631 * Ext JS Library 1.1.1
33632 * Copyright(c) 2006-2007, Ext JS, LLC.
33634 * Originally Released Under LGPL - original licence link has changed is not relivant.
33637 * <script type="text/javascript">
33640 * @class Roo.form.DisplayField
33641 * @extends Roo.form.Field
33642 * A generic Field to display non-editable data.
33643 * @cfg {Boolean} closable (true|false) default false
33645 * Creates a new Display Field item.
33646 * @param {Object} config Configuration options
33648 Roo.form.DisplayField = function(config){
33649 Roo.form.DisplayField.superclass.constructor.call(this, config);
33654 * Fires after the click the close btn
33655 * @param {Roo.form.DisplayField} this
33661 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
33662 inputType: 'hidden',
33668 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
33670 focusClass : undefined,
33672 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
33674 fieldClass: 'x-form-field',
33677 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
33679 valueRenderer: undefined,
33683 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
33684 * {tag: "input", type: "checkbox", autocomplete: "off"})
33687 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
33691 onResize : function(){
33692 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
33696 initEvents : function(){
33697 // Roo.form.Checkbox.superclass.initEvents.call(this);
33698 // has no events...
33701 this.closeEl.on('click', this.onClose, this);
33707 getResizeEl : function(){
33711 getPositionEl : function(){
33716 onRender : function(ct, position){
33718 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
33719 //if(this.inputValue !== undefined){
33720 this.wrap = this.el.wrap();
33722 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
33725 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
33728 if (this.bodyStyle) {
33729 this.viewEl.applyStyles(this.bodyStyle);
33731 //this.viewEl.setStyle('padding', '2px');
33733 this.setValue(this.value);
33738 initValue : Roo.emptyFn,
33743 onClick : function(){
33748 * Sets the checked state of the checkbox.
33749 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
33751 setValue : function(v){
33753 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
33754 // this might be called before we have a dom element..
33755 if (!this.viewEl) {
33758 this.viewEl.dom.innerHTML = html;
33759 Roo.form.DisplayField.superclass.setValue.call(this, v);
33763 onClose : function(e)
33765 e.preventDefault();
33767 this.fireEvent('close', this);
33776 * @class Roo.form.DayPicker
33777 * @extends Roo.form.Field
33778 * A Day picker show [M] [T] [W] ....
33780 * Creates a new Day Picker
33781 * @param {Object} config Configuration options
33783 Roo.form.DayPicker= function(config){
33784 Roo.form.DayPicker.superclass.constructor.call(this, config);
33788 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
33790 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
33792 focusClass : undefined,
33794 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
33796 fieldClass: "x-form-field",
33799 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
33800 * {tag: "input", type: "checkbox", autocomplete: "off"})
33802 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
33805 actionMode : 'viewEl',
33809 inputType : 'hidden',
33812 inputElement: false, // real input element?
33813 basedOn: false, // ????
33815 isFormField: true, // not sure where this is needed!!!!
33817 onResize : function(){
33818 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
33819 if(!this.boxLabel){
33820 this.el.alignTo(this.wrap, 'c-c');
33824 initEvents : function(){
33825 Roo.form.Checkbox.superclass.initEvents.call(this);
33826 this.el.on("click", this.onClick, this);
33827 this.el.on("change", this.onClick, this);
33831 getResizeEl : function(){
33835 getPositionEl : function(){
33841 onRender : function(ct, position){
33842 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
33844 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
33846 var r1 = '<table><tr>';
33847 var r2 = '<tr class="x-form-daypick-icons">';
33848 for (var i=0; i < 7; i++) {
33849 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
33850 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
33853 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
33854 viewEl.select('img').on('click', this.onClick, this);
33855 this.viewEl = viewEl;
33858 // this will not work on Chrome!!!
33859 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
33860 this.el.on('propertychange', this.setFromHidden, this); //ie
33868 initValue : Roo.emptyFn,
33871 * Returns the checked state of the checkbox.
33872 * @return {Boolean} True if checked, else false
33874 getValue : function(){
33875 return this.el.dom.value;
33880 onClick : function(e){
33881 //this.setChecked(!this.checked);
33882 Roo.get(e.target).toggleClass('x-menu-item-checked');
33883 this.refreshValue();
33884 //if(this.el.dom.checked != this.checked){
33885 // this.setValue(this.el.dom.checked);
33890 refreshValue : function()
33893 this.viewEl.select('img',true).each(function(e,i,n) {
33894 val += e.is(".x-menu-item-checked") ? String(n) : '';
33896 this.setValue(val, true);
33900 * Sets the checked state of the checkbox.
33901 * On is always based on a string comparison between inputValue and the param.
33902 * @param {Boolean/String} value - the value to set
33903 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
33905 setValue : function(v,suppressEvent){
33906 if (!this.el.dom) {
33909 var old = this.el.dom.value ;
33910 this.el.dom.value = v;
33911 if (suppressEvent) {
33915 // update display..
33916 this.viewEl.select('img',true).each(function(e,i,n) {
33918 var on = e.is(".x-menu-item-checked");
33919 var newv = v.indexOf(String(n)) > -1;
33921 e.toggleClass('x-menu-item-checked');
33927 this.fireEvent('change', this, v, old);
33932 // handle setting of hidden value by some other method!!?!?
33933 setFromHidden: function()
33938 //console.log("SET FROM HIDDEN");
33939 //alert('setFrom hidden');
33940 this.setValue(this.el.dom.value);
33943 onDestroy : function()
33946 Roo.get(this.viewEl).remove();
33949 Roo.form.DayPicker.superclass.onDestroy.call(this);
33953 * RooJS Library 1.1.1
33954 * Copyright(c) 2008-2011 Alan Knowles
33961 * @class Roo.form.ComboCheck
33962 * @extends Roo.form.ComboBox
33963 * A combobox for multiple select items.
33965 * FIXME - could do with a reset button..
33968 * Create a new ComboCheck
33969 * @param {Object} config Configuration options
33971 Roo.form.ComboCheck = function(config){
33972 Roo.form.ComboCheck.superclass.constructor.call(this, config);
33973 // should verify some data...
33975 // hiddenName = required..
33976 // displayField = required
33977 // valudField == required
33978 var req= [ 'hiddenName', 'displayField', 'valueField' ];
33980 Roo.each(req, function(e) {
33981 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
33982 throw "Roo.form.ComboCheck : missing value for: " + e;
33989 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
33994 selectedClass: 'x-menu-item-checked',
33997 onRender : function(ct, position){
34003 var cls = 'x-combo-list';
34006 this.tpl = new Roo.Template({
34007 html : '<div class="'+cls+'-item x-menu-check-item">' +
34008 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
34009 '<span>{' + this.displayField + '}</span>' +
34016 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
34017 this.view.singleSelect = false;
34018 this.view.multiSelect = true;
34019 this.view.toggleSelect = true;
34020 this.pageTb.add(new Roo.Toolbar.Fill(), {
34023 handler: function()
34030 onViewOver : function(e, t){
34036 onViewClick : function(doFocus,index){
34040 select: function () {
34041 //Roo.log("SELECT CALLED");
34044 selectByValue : function(xv, scrollIntoView){
34045 var ar = this.getValueArray();
34048 Roo.each(ar, function(v) {
34049 if(v === undefined || v === null){
34052 var r = this.findRecord(this.valueField, v);
34054 sels.push(this.store.indexOf(r))
34058 this.view.select(sels);
34064 onSelect : function(record, index){
34065 // Roo.log("onselect Called");
34066 // this is only called by the clear button now..
34067 this.view.clearSelections();
34068 this.setValue('[]');
34069 if (this.value != this.valueBefore) {
34070 this.fireEvent('change', this, this.value, this.valueBefore);
34071 this.valueBefore = this.value;
34074 getValueArray : function()
34079 //Roo.log(this.value);
34080 if (typeof(this.value) == 'undefined') {
34083 var ar = Roo.decode(this.value);
34084 return ar instanceof Array ? ar : []; //?? valid?
34087 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
34092 expand : function ()
34095 Roo.form.ComboCheck.superclass.expand.call(this);
34096 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
34097 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
34102 collapse : function(){
34103 Roo.form.ComboCheck.superclass.collapse.call(this);
34104 var sl = this.view.getSelectedIndexes();
34105 var st = this.store;
34109 Roo.each(sl, function(i) {
34111 nv.push(r.get(this.valueField));
34113 this.setValue(Roo.encode(nv));
34114 if (this.value != this.valueBefore) {
34116 this.fireEvent('change', this, this.value, this.valueBefore);
34117 this.valueBefore = this.value;
34122 setValue : function(v){
34126 var vals = this.getValueArray();
34128 Roo.each(vals, function(k) {
34129 var r = this.findRecord(this.valueField, k);
34131 tv.push(r.data[this.displayField]);
34132 }else if(this.valueNotFoundText !== undefined){
34133 tv.push( this.valueNotFoundText );
34138 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
34139 this.hiddenField.value = v;
34145 * Ext JS Library 1.1.1
34146 * Copyright(c) 2006-2007, Ext JS, LLC.
34148 * Originally Released Under LGPL - original licence link has changed is not relivant.
34151 * <script type="text/javascript">
34155 * @class Roo.form.Signature
34156 * @extends Roo.form.Field
34160 * @param {Object} config Configuration options
34163 Roo.form.Signature = function(config){
34164 Roo.form.Signature.superclass.constructor.call(this, config);
34166 this.addEvents({// not in used??
34169 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
34170 * @param {Roo.form.Signature} combo This combo box
34175 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
34176 * @param {Roo.form.ComboBox} combo This combo box
34177 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
34183 Roo.extend(Roo.form.Signature, Roo.form.Field, {
34185 * @cfg {Object} labels Label to use when rendering a form.
34189 * confirm : "Confirm"
34194 confirm : "Confirm"
34197 * @cfg {Number} width The signature panel width (defaults to 300)
34201 * @cfg {Number} height The signature panel height (defaults to 100)
34205 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
34207 allowBlank : false,
34210 // {Object} signPanel The signature SVG panel element (defaults to {})
34212 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
34213 isMouseDown : false,
34214 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
34215 isConfirmed : false,
34216 // {String} signatureTmp SVG mapping string (defaults to empty string)
34220 defaultAutoCreate : { // modified by initCompnoent..
34226 onRender : function(ct, position){
34228 Roo.form.Signature.superclass.onRender.call(this, ct, position);
34230 this.wrap = this.el.wrap({
34231 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
34234 this.createToolbar(this);
34235 this.signPanel = this.wrap.createChild({
34237 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
34241 this.svgID = Roo.id();
34242 this.svgEl = this.signPanel.createChild({
34243 xmlns : 'http://www.w3.org/2000/svg',
34245 id : this.svgID + "-svg",
34247 height: this.height,
34248 viewBox: '0 0 '+this.width+' '+this.height,
34252 id: this.svgID + "-svg-r",
34254 height: this.height,
34259 id: this.svgID + "-svg-l",
34261 y1: (this.height*0.8), // start set the line in 80% of height
34262 x2: this.width, // end
34263 y2: (this.height*0.8), // end set the line in 80% of height
34265 'stroke-width': "1",
34266 'stroke-dasharray': "3",
34267 'shape-rendering': "crispEdges",
34268 'pointer-events': "none"
34272 id: this.svgID + "-svg-p",
34274 'stroke-width': "3",
34276 'pointer-events': 'none'
34281 this.svgBox = this.svgEl.dom.getScreenCTM();
34283 createSVG : function(){
34284 var svg = this.signPanel;
34285 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
34288 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
34289 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
34290 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
34291 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
34292 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
34293 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
34294 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
34297 isTouchEvent : function(e){
34298 return e.type.match(/^touch/);
34300 getCoords : function (e) {
34301 var pt = this.svgEl.dom.createSVGPoint();
34304 if (this.isTouchEvent(e)) {
34305 pt.x = e.targetTouches[0].clientX;
34306 pt.y = e.targetTouches[0].clientY;
34308 var a = this.svgEl.dom.getScreenCTM();
34309 var b = a.inverse();
34310 var mx = pt.matrixTransform(b);
34311 return mx.x + ',' + mx.y;
34313 //mouse event headler
34314 down : function (e) {
34315 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
34316 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
34318 this.isMouseDown = true;
34320 e.preventDefault();
34322 move : function (e) {
34323 if (this.isMouseDown) {
34324 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
34325 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
34328 e.preventDefault();
34330 up : function (e) {
34331 this.isMouseDown = false;
34332 var sp = this.signatureTmp.split(' ');
34335 if(!sp[sp.length-2].match(/^L/)){
34339 this.signatureTmp = sp.join(" ");
34342 if(this.getValue() != this.signatureTmp){
34343 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
34344 this.isConfirmed = false;
34346 e.preventDefault();
34350 * Protected method that will not generally be called directly. It
34351 * is called when the editor creates its toolbar. Override this method if you need to
34352 * add custom toolbar buttons.
34353 * @param {HtmlEditor} editor
34355 createToolbar : function(editor){
34356 function btn(id, toggle, handler){
34357 var xid = fid + '-'+ id ;
34361 cls : 'x-btn-icon x-edit-'+id,
34362 enableToggle:toggle !== false,
34363 scope: editor, // was editor...
34364 handler:handler||editor.relayBtnCmd,
34365 clickEvent:'mousedown',
34366 tooltip: etb.buttonTips[id] || undefined, ///tips ???
34372 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
34376 cls : ' x-signature-btn x-signature-'+id,
34377 scope: editor, // was editor...
34378 handler: this.reset,
34379 clickEvent:'mousedown',
34380 text: this.labels.clear
34387 cls : ' x-signature-btn x-signature-'+id,
34388 scope: editor, // was editor...
34389 handler: this.confirmHandler,
34390 clickEvent:'mousedown',
34391 text: this.labels.confirm
34398 * when user is clicked confirm then show this image.....
34400 * @return {String} Image Data URI
34402 getImageDataURI : function(){
34403 var svg = this.svgEl.dom.parentNode.innerHTML;
34404 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
34409 * @return {Boolean} this.isConfirmed
34411 getConfirmed : function(){
34412 return this.isConfirmed;
34416 * @return {Number} this.width
34418 getWidth : function(){
34423 * @return {Number} this.height
34425 getHeight : function(){
34426 return this.height;
34429 getSignature : function(){
34430 return this.signatureTmp;
34433 reset : function(){
34434 this.signatureTmp = '';
34435 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
34436 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
34437 this.isConfirmed = false;
34438 Roo.form.Signature.superclass.reset.call(this);
34440 setSignature : function(s){
34441 this.signatureTmp = s;
34442 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
34443 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
34445 this.isConfirmed = false;
34446 Roo.form.Signature.superclass.reset.call(this);
34449 // Roo.log(this.signPanel.dom.contentWindow.up())
34452 setConfirmed : function(){
34456 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
34459 confirmHandler : function(){
34460 if(!this.getSignature()){
34464 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
34465 this.setValue(this.getSignature());
34466 this.isConfirmed = true;
34468 this.fireEvent('confirm', this);
34471 // Subclasses should provide the validation implementation by overriding this
34472 validateValue : function(value){
34473 if(this.allowBlank){
34477 if(this.isConfirmed){
34484 * Ext JS Library 1.1.1
34485 * Copyright(c) 2006-2007, Ext JS, LLC.
34487 * Originally Released Under LGPL - original licence link has changed is not relivant.
34490 * <script type="text/javascript">
34495 * @class Roo.form.ComboBox
34496 * @extends Roo.form.TriggerField
34497 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
34499 * Create a new ComboBox.
34500 * @param {Object} config Configuration options
34502 Roo.form.Select = function(config){
34503 Roo.form.Select.superclass.constructor.call(this, config);
34507 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
34509 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
34512 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
34513 * rendering into an Roo.Editor, defaults to false)
34516 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
34517 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
34520 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
34523 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
34524 * the dropdown list (defaults to undefined, with no header element)
34528 * @cfg {String/Roo.Template} tpl The template to use to render the output
34532 defaultAutoCreate : {tag: "select" },
34534 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
34536 listWidth: undefined,
34538 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
34539 * mode = 'remote' or 'text' if mode = 'local')
34541 displayField: undefined,
34543 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
34544 * mode = 'remote' or 'value' if mode = 'local').
34545 * Note: use of a valueField requires the user make a selection
34546 * in order for a value to be mapped.
34548 valueField: undefined,
34552 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
34553 * field's data value (defaults to the underlying DOM element's name)
34555 hiddenName: undefined,
34557 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
34561 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
34563 selectedClass: 'x-combo-selected',
34565 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
34566 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
34567 * which displays a downward arrow icon).
34569 triggerClass : 'x-form-arrow-trigger',
34571 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
34575 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
34576 * anchor positions (defaults to 'tl-bl')
34578 listAlign: 'tl-bl?',
34580 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
34584 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
34585 * query specified by the allQuery config option (defaults to 'query')
34587 triggerAction: 'query',
34589 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
34590 * (defaults to 4, does not apply if editable = false)
34594 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
34595 * delay (typeAheadDelay) if it matches a known value (defaults to false)
34599 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
34600 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
34604 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
34605 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
34609 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
34610 * when editable = true (defaults to false)
34612 selectOnFocus:false,
34614 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
34616 queryParam: 'query',
34618 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
34619 * when mode = 'remote' (defaults to 'Loading...')
34621 loadingText: 'Loading...',
34623 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
34627 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
34631 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
34632 * traditional select (defaults to true)
34636 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
34640 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
34644 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
34645 * listWidth has a higher value)
34649 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
34650 * allow the user to set arbitrary text into the field (defaults to false)
34652 forceSelection:false,
34654 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
34655 * if typeAhead = true (defaults to 250)
34657 typeAheadDelay : 250,
34659 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
34660 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
34662 valueNotFoundText : undefined,
34665 * @cfg {String} defaultValue The value displayed after loading the store.
34670 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
34672 blockFocus : false,
34675 * @cfg {Boolean} disableClear Disable showing of clear button.
34677 disableClear : false,
34679 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
34681 alwaysQuery : false,
34687 // element that contains real text value.. (when hidden is used..)
34690 onRender : function(ct, position){
34691 Roo.form.Field.prototype.onRender.call(this, ct, position);
34694 this.store.on('beforeload', this.onBeforeLoad, this);
34695 this.store.on('load', this.onLoad, this);
34696 this.store.on('loadexception', this.onLoadException, this);
34697 this.store.load({});
34705 initEvents : function(){
34706 //Roo.form.ComboBox.superclass.initEvents.call(this);
34710 onDestroy : function(){
34713 this.store.un('beforeload', this.onBeforeLoad, this);
34714 this.store.un('load', this.onLoad, this);
34715 this.store.un('loadexception', this.onLoadException, this);
34717 //Roo.form.ComboBox.superclass.onDestroy.call(this);
34721 fireKey : function(e){
34722 if(e.isNavKeyPress() && !this.list.isVisible()){
34723 this.fireEvent("specialkey", this, e);
34728 onResize: function(w, h){
34736 * Allow or prevent the user from directly editing the field text. If false is passed,
34737 * the user will only be able to select from the items defined in the dropdown list. This method
34738 * is the runtime equivalent of setting the 'editable' config option at config time.
34739 * @param {Boolean} value True to allow the user to directly edit the field text
34741 setEditable : function(value){
34746 onBeforeLoad : function(){
34748 Roo.log("Select before load");
34751 this.innerList.update(this.loadingText ?
34752 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
34753 //this.restrictHeight();
34754 this.selectedIndex = -1;
34758 onLoad : function(){
34761 var dom = this.el.dom;
34762 dom.innerHTML = '';
34763 var od = dom.ownerDocument;
34765 if (this.emptyText) {
34766 var op = od.createElement('option');
34767 op.setAttribute('value', '');
34768 op.innerHTML = String.format('{0}', this.emptyText);
34769 dom.appendChild(op);
34771 if(this.store.getCount() > 0){
34773 var vf = this.valueField;
34774 var df = this.displayField;
34775 this.store.data.each(function(r) {
34776 // which colmsn to use... testing - cdoe / title..
34777 var op = od.createElement('option');
34778 op.setAttribute('value', r.data[vf]);
34779 op.innerHTML = String.format('{0}', r.data[df]);
34780 dom.appendChild(op);
34782 if (typeof(this.defaultValue != 'undefined')) {
34783 this.setValue(this.defaultValue);
34788 //this.onEmptyResults();
34793 onLoadException : function()
34795 dom.innerHTML = '';
34797 Roo.log("Select on load exception");
34801 Roo.log(this.store.reader.jsonData);
34802 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
34803 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
34809 onTypeAhead : function(){
34814 onSelect : function(record, index){
34815 Roo.log('on select?');
34817 if(this.fireEvent('beforeselect', this, record, index) !== false){
34818 this.setFromData(index > -1 ? record.data : false);
34820 this.fireEvent('select', this, record, index);
34825 * Returns the currently selected field value or empty string if no value is set.
34826 * @return {String} value The selected value
34828 getValue : function(){
34829 var dom = this.el.dom;
34830 this.value = dom.options[dom.selectedIndex].value;
34836 * Clears any text/value currently set in the field
34838 clearValue : function(){
34840 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
34845 * Sets the specified value into the field. If the value finds a match, the corresponding record text
34846 * will be displayed in the field. If the value does not match the data value of an existing item,
34847 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
34848 * Otherwise the field will be blank (although the value will still be set).
34849 * @param {String} value The value to match
34851 setValue : function(v){
34852 var d = this.el.dom;
34853 for (var i =0; i < d.options.length;i++) {
34854 if (v == d.options[i].value) {
34855 d.selectedIndex = i;
34863 * @property {Object} the last set data for the element
34868 * Sets the value of the field based on a object which is related to the record format for the store.
34869 * @param {Object} value the value to set as. or false on reset?
34871 setFromData : function(o){
34872 Roo.log('setfrom data?');
34878 reset : function(){
34882 findRecord : function(prop, value){
34887 if(this.store.getCount() > 0){
34888 this.store.each(function(r){
34889 if(r.data[prop] == value){
34899 getName: function()
34901 // returns hidden if it's set..
34902 if (!this.rendered) {return ''};
34903 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
34911 onEmptyResults : function(){
34912 Roo.log('empty results');
34917 * Returns true if the dropdown list is expanded, else false.
34919 isExpanded : function(){
34924 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
34925 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
34926 * @param {String} value The data value of the item to select
34927 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
34928 * selected item if it is not currently in view (defaults to true)
34929 * @return {Boolean} True if the value matched an item in the list, else false
34931 selectByValue : function(v, scrollIntoView){
34932 Roo.log('select By Value');
34935 if(v !== undefined && v !== null){
34936 var r = this.findRecord(this.valueField || this.displayField, v);
34938 this.select(this.store.indexOf(r), scrollIntoView);
34946 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
34947 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
34948 * @param {Number} index The zero-based index of the list item to select
34949 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
34950 * selected item if it is not currently in view (defaults to true)
34952 select : function(index, scrollIntoView){
34953 Roo.log('select ');
34956 this.selectedIndex = index;
34957 this.view.select(index);
34958 if(scrollIntoView !== false){
34959 var el = this.view.getNode(index);
34961 this.innerList.scrollChildIntoView(el, false);
34969 validateBlur : function(){
34976 initQuery : function(){
34977 this.doQuery(this.getRawValue());
34981 doForce : function(){
34982 if(this.el.dom.value.length > 0){
34983 this.el.dom.value =
34984 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
34990 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
34991 * query allowing the query action to be canceled if needed.
34992 * @param {String} query The SQL query to execute
34993 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
34994 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
34995 * saved in the current store (defaults to false)
34997 doQuery : function(q, forceAll){
34999 Roo.log('doQuery?');
35000 if(q === undefined || q === null){
35005 forceAll: forceAll,
35009 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
35013 forceAll = qe.forceAll;
35014 if(forceAll === true || (q.length >= this.minChars)){
35015 if(this.lastQuery != q || this.alwaysQuery){
35016 this.lastQuery = q;
35017 if(this.mode == 'local'){
35018 this.selectedIndex = -1;
35020 this.store.clearFilter();
35022 this.store.filter(this.displayField, q);
35026 this.store.baseParams[this.queryParam] = q;
35028 params: this.getParams(q)
35033 this.selectedIndex = -1;
35040 getParams : function(q){
35042 //p[this.queryParam] = q;
35045 p.limit = this.pageSize;
35051 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
35053 collapse : function(){
35058 collapseIf : function(e){
35063 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
35065 expand : function(){
35073 * @cfg {Boolean} grow
35077 * @cfg {Number} growMin
35081 * @cfg {Number} growMax
35089 setWidth : function()
35093 getResizeEl : function(){
35096 });//<script type="text/javasscript">
35100 * @class Roo.DDView
35101 * A DnD enabled version of Roo.View.
35102 * @param {Element/String} container The Element in which to create the View.
35103 * @param {String} tpl The template string used to create the markup for each element of the View
35104 * @param {Object} config The configuration properties. These include all the config options of
35105 * {@link Roo.View} plus some specific to this class.<br>
35107 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
35108 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
35110 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
35111 .x-view-drag-insert-above {
35112 border-top:1px dotted #3366cc;
35114 .x-view-drag-insert-below {
35115 border-bottom:1px dotted #3366cc;
35121 Roo.DDView = function(container, tpl, config) {
35122 Roo.DDView.superclass.constructor.apply(this, arguments);
35123 this.getEl().setStyle("outline", "0px none");
35124 this.getEl().unselectable();
35125 if (this.dragGroup) {
35126 this.setDraggable(this.dragGroup.split(","));
35128 if (this.dropGroup) {
35129 this.setDroppable(this.dropGroup.split(","));
35131 if (this.deletable) {
35132 this.setDeletable();
35134 this.isDirtyFlag = false;
35140 Roo.extend(Roo.DDView, Roo.View, {
35141 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
35142 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
35143 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
35144 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
35148 reset: Roo.emptyFn,
35150 clearInvalid: Roo.form.Field.prototype.clearInvalid,
35152 validate: function() {
35156 destroy: function() {
35157 this.purgeListeners();
35158 this.getEl.removeAllListeners();
35159 this.getEl().remove();
35160 if (this.dragZone) {
35161 if (this.dragZone.destroy) {
35162 this.dragZone.destroy();
35165 if (this.dropZone) {
35166 if (this.dropZone.destroy) {
35167 this.dropZone.destroy();
35172 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
35173 getName: function() {
35177 /** Loads the View from a JSON string representing the Records to put into the Store. */
35178 setValue: function(v) {
35180 throw "DDView.setValue(). DDView must be constructed with a valid Store";
35183 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
35184 this.store.proxy = new Roo.data.MemoryProxy(data);
35188 /** @return {String} a parenthesised list of the ids of the Records in the View. */
35189 getValue: function() {
35191 this.store.each(function(rec) {
35192 result += rec.id + ',';
35194 return result.substr(0, result.length - 1) + ')';
35197 getIds: function() {
35198 var i = 0, result = new Array(this.store.getCount());
35199 this.store.each(function(rec) {
35200 result[i++] = rec.id;
35205 isDirty: function() {
35206 return this.isDirtyFlag;
35210 * Part of the Roo.dd.DropZone interface. If no target node is found, the
35211 * whole Element becomes the target, and this causes the drop gesture to append.
35213 getTargetFromEvent : function(e) {
35214 var target = e.getTarget();
35215 while ((target !== null) && (target.parentNode != this.el.dom)) {
35216 target = target.parentNode;
35219 target = this.el.dom.lastChild || this.el.dom;
35225 * Create the drag data which consists of an object which has the property "ddel" as
35226 * the drag proxy element.
35228 getDragData : function(e) {
35229 var target = this.findItemFromChild(e.getTarget());
35231 this.handleSelection(e);
35232 var selNodes = this.getSelectedNodes();
35235 copy: this.copy || (this.allowCopy && e.ctrlKey),
35239 var selectedIndices = this.getSelectedIndexes();
35240 for (var i = 0; i < selectedIndices.length; i++) {
35241 dragData.records.push(this.store.getAt(selectedIndices[i]));
35243 if (selNodes.length == 1) {
35244 dragData.ddel = target.cloneNode(true); // the div element
35246 var div = document.createElement('div'); // create the multi element drag "ghost"
35247 div.className = 'multi-proxy';
35248 for (var i = 0, len = selNodes.length; i < len; i++) {
35249 div.appendChild(selNodes[i].cloneNode(true));
35251 dragData.ddel = div;
35253 //console.log(dragData)
35254 //console.log(dragData.ddel.innerHTML)
35257 //console.log('nodragData')
35261 /** Specify to which ddGroup items in this DDView may be dragged. */
35262 setDraggable: function(ddGroup) {
35263 if (ddGroup instanceof Array) {
35264 Roo.each(ddGroup, this.setDraggable, this);
35267 if (this.dragZone) {
35268 this.dragZone.addToGroup(ddGroup);
35270 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
35271 containerScroll: true,
35275 // Draggability implies selection. DragZone's mousedown selects the element.
35276 if (!this.multiSelect) { this.singleSelect = true; }
35278 // Wire the DragZone's handlers up to methods in *this*
35279 this.dragZone.getDragData = this.getDragData.createDelegate(this);
35283 /** Specify from which ddGroup this DDView accepts drops. */
35284 setDroppable: function(ddGroup) {
35285 if (ddGroup instanceof Array) {
35286 Roo.each(ddGroup, this.setDroppable, this);
35289 if (this.dropZone) {
35290 this.dropZone.addToGroup(ddGroup);
35292 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
35293 containerScroll: true,
35297 // Wire the DropZone's handlers up to methods in *this*
35298 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
35299 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
35300 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
35301 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
35302 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
35306 /** Decide whether to drop above or below a View node. */
35307 getDropPoint : function(e, n, dd){
35308 if (n == this.el.dom) { return "above"; }
35309 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
35310 var c = t + (b - t) / 2;
35311 var y = Roo.lib.Event.getPageY(e);
35319 onNodeEnter : function(n, dd, e, data){
35323 onNodeOver : function(n, dd, e, data){
35324 var pt = this.getDropPoint(e, n, dd);
35325 // set the insert point style on the target node
35326 var dragElClass = this.dropNotAllowed;
35329 if (pt == "above"){
35330 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
35331 targetElClass = "x-view-drag-insert-above";
35333 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
35334 targetElClass = "x-view-drag-insert-below";
35336 if (this.lastInsertClass != targetElClass){
35337 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
35338 this.lastInsertClass = targetElClass;
35341 return dragElClass;
35344 onNodeOut : function(n, dd, e, data){
35345 this.removeDropIndicators(n);
35348 onNodeDrop : function(n, dd, e, data){
35349 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
35352 var pt = this.getDropPoint(e, n, dd);
35353 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
35354 if (pt == "below") { insertAt++; }
35355 for (var i = 0; i < data.records.length; i++) {
35356 var r = data.records[i];
35357 var dup = this.store.getById(r.id);
35358 if (dup && (dd != this.dragZone)) {
35359 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
35362 this.store.insert(insertAt++, r.copy());
35364 data.source.isDirtyFlag = true;
35366 this.store.insert(insertAt++, r);
35368 this.isDirtyFlag = true;
35371 this.dragZone.cachedTarget = null;
35375 removeDropIndicators : function(n){
35377 Roo.fly(n).removeClass([
35378 "x-view-drag-insert-above",
35379 "x-view-drag-insert-below"]);
35380 this.lastInsertClass = "_noclass";
35385 * Utility method. Add a delete option to the DDView's context menu.
35386 * @param {String} imageUrl The URL of the "delete" icon image.
35388 setDeletable: function(imageUrl) {
35389 if (!this.singleSelect && !this.multiSelect) {
35390 this.singleSelect = true;
35392 var c = this.getContextMenu();
35393 this.contextMenu.on("itemclick", function(item) {
35396 this.remove(this.getSelectedIndexes());
35400 this.contextMenu.add({
35407 /** Return the context menu for this DDView. */
35408 getContextMenu: function() {
35409 if (!this.contextMenu) {
35410 // Create the View's context menu
35411 this.contextMenu = new Roo.menu.Menu({
35412 id: this.id + "-contextmenu"
35414 this.el.on("contextmenu", this.showContextMenu, this);
35416 return this.contextMenu;
35419 disableContextMenu: function() {
35420 if (this.contextMenu) {
35421 this.el.un("contextmenu", this.showContextMenu, this);
35425 showContextMenu: function(e, item) {
35426 item = this.findItemFromChild(e.getTarget());
35429 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
35430 this.contextMenu.showAt(e.getXY());
35435 * Remove {@link Roo.data.Record}s at the specified indices.
35436 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
35438 remove: function(selectedIndices) {
35439 selectedIndices = [].concat(selectedIndices);
35440 for (var i = 0; i < selectedIndices.length; i++) {
35441 var rec = this.store.getAt(selectedIndices[i]);
35442 this.store.remove(rec);
35447 * Double click fires the event, but also, if this is draggable, and there is only one other
35448 * related DropZone, it transfers the selected node.
35450 onDblClick : function(e){
35451 var item = this.findItemFromChild(e.getTarget());
35453 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
35456 if (this.dragGroup) {
35457 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
35458 while (targets.indexOf(this.dropZone) > -1) {
35459 targets.remove(this.dropZone);
35461 if (targets.length == 1) {
35462 this.dragZone.cachedTarget = null;
35463 var el = Roo.get(targets[0].getEl());
35464 var box = el.getBox(true);
35465 targets[0].onNodeDrop(el.dom, {
35467 xy: [box.x, box.y + box.height - 1]
35468 }, null, this.getDragData(e));
35474 handleSelection: function(e) {
35475 this.dragZone.cachedTarget = null;
35476 var item = this.findItemFromChild(e.getTarget());
35478 this.clearSelections(true);
35481 if (item && (this.multiSelect || this.singleSelect)){
35482 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
35483 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
35484 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
35485 this.unselect(item);
35487 this.select(item, this.multiSelect && e.ctrlKey);
35488 this.lastSelection = item;
35493 onItemClick : function(item, index, e){
35494 if(this.fireEvent("beforeclick", this, index, item, e) === false){
35500 unselect : function(nodeInfo, suppressEvent){
35501 var node = this.getNode(nodeInfo);
35502 if(node && this.isSelected(node)){
35503 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
35504 Roo.fly(node).removeClass(this.selectedClass);
35505 this.selections.remove(node);
35506 if(!suppressEvent){
35507 this.fireEvent("selectionchange", this, this.selections);
35515 * Ext JS Library 1.1.1
35516 * Copyright(c) 2006-2007, Ext JS, LLC.
35518 * Originally Released Under LGPL - original licence link has changed is not relivant.
35521 * <script type="text/javascript">
35525 * @class Roo.LayoutManager
35526 * @extends Roo.util.Observable
35527 * Base class for layout managers.
35529 Roo.LayoutManager = function(container, config){
35530 Roo.LayoutManager.superclass.constructor.call(this);
35531 this.el = Roo.get(container);
35532 // ie scrollbar fix
35533 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35534 document.body.scroll = "no";
35535 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35536 this.el.position('relative');
35538 this.id = this.el.id;
35539 this.el.addClass("x-layout-container");
35540 /** false to disable window resize monitoring @type Boolean */
35541 this.monitorWindowResize = true;
35546 * Fires when a layout is performed.
35547 * @param {Roo.LayoutManager} this
35551 * @event regionresized
35552 * Fires when the user resizes a region.
35553 * @param {Roo.LayoutRegion} region The resized region
35554 * @param {Number} newSize The new size (width for east/west, height for north/south)
35556 "regionresized" : true,
35558 * @event regioncollapsed
35559 * Fires when a region is collapsed.
35560 * @param {Roo.LayoutRegion} region The collapsed region
35562 "regioncollapsed" : true,
35564 * @event regionexpanded
35565 * Fires when a region is expanded.
35566 * @param {Roo.LayoutRegion} region The expanded region
35568 "regionexpanded" : true
35570 this.updating = false;
35571 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35574 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
35576 * Returns true if this layout is currently being updated
35577 * @return {Boolean}
35579 isUpdating : function(){
35580 return this.updating;
35584 * Suspend the LayoutManager from doing auto-layouts while
35585 * making multiple add or remove calls
35587 beginUpdate : function(){
35588 this.updating = true;
35592 * Restore auto-layouts and optionally disable the manager from performing a layout
35593 * @param {Boolean} noLayout true to disable a layout update
35595 endUpdate : function(noLayout){
35596 this.updating = false;
35602 layout: function(){
35606 onRegionResized : function(region, newSize){
35607 this.fireEvent("regionresized", region, newSize);
35611 onRegionCollapsed : function(region){
35612 this.fireEvent("regioncollapsed", region);
35615 onRegionExpanded : function(region){
35616 this.fireEvent("regionexpanded", region);
35620 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35621 * performs box-model adjustments.
35622 * @return {Object} The size as an object {width: (the width), height: (the height)}
35624 getViewSize : function(){
35626 if(this.el.dom != document.body){
35627 size = this.el.getSize();
35629 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35631 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35632 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35637 * Returns the Element this layout is bound to.
35638 * @return {Roo.Element}
35640 getEl : function(){
35645 * Returns the specified region.
35646 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35647 * @return {Roo.LayoutRegion}
35649 getRegion : function(target){
35650 return this.regions[target.toLowerCase()];
35653 onWindowResize : function(){
35654 if(this.monitorWindowResize){
35660 * Ext JS Library 1.1.1
35661 * Copyright(c) 2006-2007, Ext JS, LLC.
35663 * Originally Released Under LGPL - original licence link has changed is not relivant.
35666 * <script type="text/javascript">
35669 * @class Roo.BorderLayout
35670 * @extends Roo.LayoutManager
35671 * @children Roo.panel.Content
35672 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35673 * please see: <br><br>
35674 * <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>
35675 * <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>
35678 var layout = new Roo.BorderLayout(document.body, {
35712 preferredTabWidth: 150
35717 var CP = Roo.panel.Content;
35719 layout.beginUpdate();
35720 layout.add("north", new CP("north", "North"));
35721 layout.add("south", new CP("south", {title: "South", closable: true}));
35722 layout.add("west", new CP("west", {title: "West"}));
35723 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
35724 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
35725 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
35726 layout.getRegion("center").showPanel("center1");
35727 layout.endUpdate();
35730 <b>The container the layout is rendered into can be either the body element or any other element.
35731 If it is not the body element, the container needs to either be an absolute positioned element,
35732 or you will need to add "position:relative" to the css of the container. You will also need to specify
35733 the container size if it is not the body element.</b>
35736 * Create a new BorderLayout
35737 * @param {String/HTMLElement/Element} container The container this layout is bound to
35738 * @param {Object} config Configuration options
35740 Roo.BorderLayout = function(container, config){
35741 config = config || {};
35742 Roo.BorderLayout.superclass.constructor.call(this, container, config);
35743 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
35744 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
35745 var target = this.factory.validRegions[i];
35746 if(config[target]){
35747 this.addRegion(target, config[target]);
35752 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
35755 * @cfg {Roo.LayoutRegion} east
35758 * @cfg {Roo.LayoutRegion} west
35761 * @cfg {Roo.LayoutRegion} north
35764 * @cfg {Roo.LayoutRegion} south
35767 * @cfg {Roo.LayoutRegion} center
35770 * Creates and adds a new region if it doesn't already exist.
35771 * @param {String} target The target region key (north, south, east, west or center).
35772 * @param {Object} config The regions config object
35773 * @return {BorderLayoutRegion} The new region
35775 addRegion : function(target, config){
35776 if(!this.regions[target]){
35777 var r = this.factory.create(target, this, config);
35778 this.bindRegion(target, r);
35780 return this.regions[target];
35784 bindRegion : function(name, r){
35785 this.regions[name] = r;
35786 r.on("visibilitychange", this.layout, this);
35787 r.on("paneladded", this.layout, this);
35788 r.on("panelremoved", this.layout, this);
35789 r.on("invalidated", this.layout, this);
35790 r.on("resized", this.onRegionResized, this);
35791 r.on("collapsed", this.onRegionCollapsed, this);
35792 r.on("expanded", this.onRegionExpanded, this);
35796 * Performs a layout update.
35798 layout : function(){
35799 if(this.updating) {
35802 var size = this.getViewSize();
35803 var w = size.width;
35804 var h = size.height;
35809 //var x = 0, y = 0;
35811 var rs = this.regions;
35812 var north = rs["north"];
35813 var south = rs["south"];
35814 var west = rs["west"];
35815 var east = rs["east"];
35816 var center = rs["center"];
35817 //if(this.hideOnLayout){ // not supported anymore
35818 //c.el.setStyle("display", "none");
35820 if(north && north.isVisible()){
35821 var b = north.getBox();
35822 var m = north.getMargins();
35823 b.width = w - (m.left+m.right);
35826 centerY = b.height + b.y + m.bottom;
35827 centerH -= centerY;
35828 north.updateBox(this.safeBox(b));
35830 if(south && south.isVisible()){
35831 var b = south.getBox();
35832 var m = south.getMargins();
35833 b.width = w - (m.left+m.right);
35835 var totalHeight = (b.height + m.top + m.bottom);
35836 b.y = h - totalHeight + m.top;
35837 centerH -= totalHeight;
35838 south.updateBox(this.safeBox(b));
35840 if(west && west.isVisible()){
35841 var b = west.getBox();
35842 var m = west.getMargins();
35843 b.height = centerH - (m.top+m.bottom);
35845 b.y = centerY + m.top;
35846 var totalWidth = (b.width + m.left + m.right);
35847 centerX += totalWidth;
35848 centerW -= totalWidth;
35849 west.updateBox(this.safeBox(b));
35851 if(east && east.isVisible()){
35852 var b = east.getBox();
35853 var m = east.getMargins();
35854 b.height = centerH - (m.top+m.bottom);
35855 var totalWidth = (b.width + m.left + m.right);
35856 b.x = w - totalWidth + m.left;
35857 b.y = centerY + m.top;
35858 centerW -= totalWidth;
35859 east.updateBox(this.safeBox(b));
35862 var m = center.getMargins();
35864 x: centerX + m.left,
35865 y: centerY + m.top,
35866 width: centerW - (m.left+m.right),
35867 height: centerH - (m.top+m.bottom)
35869 //if(this.hideOnLayout){
35870 //center.el.setStyle("display", "block");
35872 center.updateBox(this.safeBox(centerBox));
35875 this.fireEvent("layout", this);
35879 safeBox : function(box){
35880 box.width = Math.max(0, box.width);
35881 box.height = Math.max(0, box.height);
35886 * Adds a ContentPanel (or subclass) to this layout.
35887 * @param {String} target The target region key (north, south, east, west or center).
35888 * @param {Roo.panel.Content} panel The panel to add
35889 * @return {Roo.panel.Content} The added panel
35891 add : function(target, panel){
35893 target = target.toLowerCase();
35894 return this.regions[target].add(panel);
35898 * Remove a ContentPanel (or subclass) to this layout.
35899 * @param {String} target The target region key (north, south, east, west or center).
35900 * @param {Number/String/Roo.panel.Content} panel The index, id or panel to remove
35901 * @return {Roo.panel.Content} The removed panel
35903 remove : function(target, panel){
35904 target = target.toLowerCase();
35905 return this.regions[target].remove(panel);
35909 * Searches all regions for a panel with the specified id
35910 * @param {String} panelId
35911 * @return {Roo.panel.Content} The panel or null if it wasn't found
35913 findPanel : function(panelId){
35914 var rs = this.regions;
35915 for(var target in rs){
35916 if(typeof rs[target] != "function"){
35917 var p = rs[target].getPanel(panelId);
35927 * Searches all regions for a panel with the specified id and activates (shows) it.
35928 * @param {String/panel.Content} panelId The panels id or the panel itself
35929 * @return {Roo.panel.Content} The shown panel or null
35931 showPanel : function(panelId) {
35932 var rs = this.regions;
35933 for(var target in rs){
35934 var r = rs[target];
35935 if(typeof r != "function"){
35936 if(r.hasPanel(panelId)){
35937 return r.showPanel(panelId);
35945 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35946 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35948 restoreState : function(provider){
35950 provider = Roo.state.Manager;
35952 var sm = new Roo.LayoutStateManager();
35953 sm.init(this, provider);
35957 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
35958 * object should contain properties for each region to add ContentPanels to, and each property's value should be
35959 * a valid ContentPanel config object. Example:
35961 // Create the main layout
35962 var layout = new Roo.BorderLayout('main-ct', {
35973 // Create and add multiple ContentPanels at once via configs
35976 id: 'source-files',
35978 title:'Ext Source Files',
35991 * @param {Object} regions An object containing ContentPanel configs by region name
35993 batchAdd : function(regions){
35994 this.beginUpdate();
35995 for(var rname in regions){
35996 var lr = this.regions[rname];
35998 this.addTypedPanels(lr, regions[rname]);
36005 addTypedPanels : function(lr, ps){
36006 if(typeof ps == 'string'){
36007 lr.add(new Roo.panel.Content(ps));
36009 else if(ps instanceof Array){
36010 for(var i =0, len = ps.length; i < len; i++){
36011 this.addTypedPanels(lr, ps[i]);
36014 else if(!ps.events){ // raw config?
36016 delete ps.el; // prevent conflict
36017 lr.add(new Roo.panel.Content(el || Roo.id(), ps));
36019 else { // panel object assumed!
36024 * Adds a xtype elements to the layout.
36028 xtype : 'ContentPanel',
36035 xtype : 'NestedLayoutPanel',
36041 items : [ ... list of content panels or nested layout panels.. ]
36045 * @param {Object} cfg Xtype definition of item to add.
36047 addxtype : function(cfg)
36049 // basically accepts a pannel...
36050 // can accept a layout region..!?!?
36051 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36053 // if (!cfg.xtype.match(/Panel$/)) {
36058 if (typeof(cfg.region) == 'undefined') {
36059 Roo.log("Failed to add Panel, region was not set");
36063 var region = cfg.region;
36069 xitems = cfg.items;
36077 if(cfg.autoCreate) {
36078 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
36080 var el = this.el.createChild();
36081 ret = new Roo.panel[cfg.xtype](el, cfg); // new panel!!!!!
36084 this.add(region, ret);
36087 // needs grid and region
36089 //var el = this.getRegion(region).el.createChild();
36090 var el = this.el.createChild();
36091 // create the grid first...
36093 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
36095 if (region == 'center' && this.active ) {
36096 cfg.background = false;
36098 ret = new Roo.panel[cfg.xtype](grid, cfg); // new panel!!!!!
36100 this.add(region, ret);
36101 if (cfg.background) {
36102 ret.on('activate', function(gp) {
36103 if (!gp.grid.rendered) {
36111 case 'NestedLayout':
36112 // create a new Layout (which is a Border Layout...
36113 var el = this.el.createChild();
36114 var clayout = cfg.layout;
36116 clayout.items = clayout.items || [];
36117 // replace this exitems with the clayout ones..
36118 xitems = clayout.items;
36121 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36122 cfg.background = false;
36124 var layout = new Roo.BorderLayout(el, clayout);
36126 ret = new Roo.panel[cfg.xtype](layout, cfg); // new panel!!!!!
36127 //console.log('adding nested layout panel ' + cfg.toSource());
36128 this.add(region, ret);
36129 nb = {}; /// find first...
36133 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
36134 this.add(region, ret);
36136 case 'Tree': // our new panel!
36137 cfg.el = this.el.createChild();
36138 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
36139 this.add(region, ret);
36141 case 'ContentPanel':
36142 case 'ScrollPanel': // ContentPanel (el, cfg)
36144 if(cfg.autoCreate) {
36145 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36147 var el = this.el.createChild();
36148 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36151 this.add(region, ret);
36155 case 'TreePanel': // our new panel!
36156 cfg.el = this.el.createChild();
36157 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36158 this.add(region, ret);
36161 case 'NestedLayoutPanel':
36162 // create a new Layout (which is a Border Layout...
36163 var el = this.el.createChild();
36164 var clayout = cfg.layout;
36166 clayout.items = clayout.items || [];
36167 // replace this exitems with the clayout ones..
36168 xitems = clayout.items;
36171 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36172 cfg.background = false;
36174 var layout = new Roo.BorderLayout(el, clayout);
36176 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
36177 //console.log('adding nested layout panel ' + cfg.toSource());
36178 this.add(region, ret);
36179 nb = {}; /// find first...
36184 // needs grid and region
36186 //var el = this.getRegion(region).el.createChild();
36187 var el = this.el.createChild();
36188 // create the grid first...
36190 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
36192 if (region == 'center' && this.active ) {
36193 cfg.background = false;
36195 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
36197 this.add(region, ret);
36198 if (cfg.background) {
36199 ret.on('activate', function(gp) {
36200 if (!gp.grid.rendered) {
36215 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36217 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36218 this.add(region, ret);
36221 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
36225 // GridPanel (grid, cfg)
36228 this.beginUpdate();
36232 Roo.each(xitems, function(i) {
36233 region = nb && i.region ? i.region : false;
36235 var add = ret.addxtype(i);
36238 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36239 if (!i.background) {
36240 abn[region] = nb[region] ;
36247 // make the last non-background panel active..
36248 //if (nb) { Roo.log(abn); }
36251 for(var r in abn) {
36252 region = this.getRegion(r);
36254 // tried using nb[r], but it does not work..
36256 region.showPanel(abn[r]);
36267 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
36268 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
36269 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
36270 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
36273 var CP = Roo.ContentPanel;
36275 var layout = Roo.BorderLayout.create({
36279 panels: [new CP("north", "North")]
36288 panels: [new CP("west", {title: "West"})]
36297 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
36306 panels: [new CP("south", {title: "South", closable: true})]
36313 preferredTabWidth: 150,
36315 new CP("center1", {title: "Close Me", closable: true}),
36316 new CP("center2", {title: "Center Panel", closable: false})
36321 layout.getRegion("center").showPanel("center1");
36326 Roo.BorderLayout.create = function(config, targetEl){
36327 var layout = new Roo.BorderLayout(targetEl || document.body, config);
36328 layout.beginUpdate();
36329 var regions = Roo.BorderLayout.RegionFactory.validRegions;
36330 for(var j = 0, jlen = regions.length; j < jlen; j++){
36331 var lr = regions[j];
36332 if(layout.regions[lr] && config[lr].panels){
36333 var r = layout.regions[lr];
36334 var ps = config[lr].panels;
36335 layout.addTypedPanels(r, ps);
36338 layout.endUpdate();
36343 Roo.BorderLayout.RegionFactory = {
36345 validRegions : ["north","south","east","west","center"],
36348 create : function(target, mgr, config){
36349 target = target.toLowerCase();
36350 if(config.lightweight || config.basic){
36351 return new Roo.BasicLayoutRegion(mgr, config, target);
36355 return new Roo.NorthLayoutRegion(mgr, config);
36357 return new Roo.SouthLayoutRegion(mgr, config);
36359 return new Roo.EastLayoutRegion(mgr, config);
36361 return new Roo.WestLayoutRegion(mgr, config);
36363 return new Roo.CenterLayoutRegion(mgr, config);
36365 throw 'Layout region "'+target+'" not supported.';
36369 * Ext JS Library 1.1.1
36370 * Copyright(c) 2006-2007, Ext JS, LLC.
36372 * Originally Released Under LGPL - original licence link has changed is not relivant.
36375 * <script type="text/javascript">
36379 * @class Roo.BasicLayoutRegion
36380 * @extends Roo.util.Observable
36381 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36382 * and does not have a titlebar, tabs or any other features. All it does is size and position
36383 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36385 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
36387 this.position = pos;
36390 * @scope Roo.BasicLayoutRegion
36394 * @event beforeremove
36395 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36396 * @param {Roo.LayoutRegion} this
36397 * @param {Roo.panel.Content} panel The panel
36398 * @param {Object} e The cancel event object
36400 "beforeremove" : true,
36402 * @event invalidated
36403 * Fires when the layout for this region is changed.
36404 * @param {Roo.LayoutRegion} this
36406 "invalidated" : true,
36408 * @event visibilitychange
36409 * Fires when this region is shown or hidden
36410 * @param {Roo.LayoutRegion} this
36411 * @param {Boolean} visibility true or false
36413 "visibilitychange" : true,
36415 * @event paneladded
36416 * Fires when a panel is added.
36417 * @param {Roo.LayoutRegion} this
36418 * @param {Roo.panel.Content} panel The panel
36420 "paneladded" : true,
36422 * @event panelremoved
36423 * Fires when a panel is removed.
36424 * @param {Roo.LayoutRegion} this
36425 * @param {Roo.panel.Content} panel The panel
36427 "panelremoved" : true,
36429 * @event beforecollapse
36430 * Fires when this region before collapse.
36431 * @param {Roo.LayoutRegion} this
36433 "beforecollapse" : true,
36436 * Fires when this region is collapsed.
36437 * @param {Roo.LayoutRegion} this
36439 "collapsed" : true,
36442 * Fires when this region is expanded.
36443 * @param {Roo.LayoutRegion} this
36448 * Fires when this region is slid into view.
36449 * @param {Roo.LayoutRegion} this
36451 "slideshow" : true,
36454 * Fires when this region slides out of view.
36455 * @param {Roo.LayoutRegion} this
36457 "slidehide" : true,
36459 * @event panelactivated
36460 * Fires when a panel is activated.
36461 * @param {Roo.LayoutRegion} this
36462 * @param {Roo.panel.Content} panel The activated panel
36464 "panelactivated" : true,
36467 * Fires when the user resizes this region.
36468 * @param {Roo.LayoutRegion} this
36469 * @param {Number} newSize The new size (width for east/west, height for north/south)
36473 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36474 this.panels = new Roo.util.MixedCollection();
36475 this.panels.getKey = this.getPanelId.createDelegate(this);
36477 this.activePanel = null;
36478 // ensure listeners are added...
36480 if (config.listeners || config.events) {
36481 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
36482 listeners : config.listeners || {},
36483 events : config.events || {}
36487 if(skipConfig !== true){
36488 this.applyConfig(config);
36492 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
36493 getPanelId : function(p){
36497 applyConfig : function(config){
36498 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36499 this.config = config;
36504 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36505 * the width, for horizontal (north, south) the height.
36506 * @param {Number} newSize The new width or height
36508 resizeTo : function(newSize){
36509 var el = this.el ? this.el :
36510 (this.activePanel ? this.activePanel.getEl() : null);
36512 switch(this.position){
36515 el.setWidth(newSize);
36516 this.fireEvent("resized", this, newSize);
36520 el.setHeight(newSize);
36521 this.fireEvent("resized", this, newSize);
36527 getBox : function(){
36528 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36531 getMargins : function(){
36532 return this.margins;
36535 updateBox : function(box){
36537 var el = this.activePanel.getEl();
36538 el.dom.style.left = box.x + "px";
36539 el.dom.style.top = box.y + "px";
36540 this.activePanel.setSize(box.width, box.height);
36544 * Returns the container element for this region.
36545 * @return {Roo.Element}
36547 getEl : function(){
36548 return this.activePanel;
36552 * Returns true if this region is currently visible.
36553 * @return {Boolean}
36555 isVisible : function(){
36556 return this.activePanel ? true : false;
36559 setActivePanel : function(panel){
36560 panel = this.getPanel(panel);
36561 if(this.activePanel && this.activePanel != panel){
36562 this.activePanel.setActiveState(false);
36563 this.activePanel.getEl().setLeftTop(-10000,-10000);
36565 this.activePanel = panel;
36566 panel.setActiveState(true);
36568 panel.setSize(this.box.width, this.box.height);
36570 this.fireEvent("panelactivated", this, panel);
36571 this.fireEvent("invalidated");
36575 * Show the specified panel.
36576 * @param {Number/String/panel.Content} panelId The panels index, id or the panel itself
36577 * @return {Roo.panel.Content} The shown panel or null
36579 showPanel : function(panel){
36580 if(panel = this.getPanel(panel)){
36581 this.setActivePanel(panel);
36587 * Get the active panel for this region.
36588 * @return {Roo.panel.Content} The active panel or null
36590 getActivePanel : function(){
36591 return this.activePanel;
36595 * Add the passed ContentPanel(s)
36596 * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
36597 * @return {Roo.panel.Content} The panel added (if only one was added)
36599 add : function(panel){
36600 if(arguments.length > 1){
36601 for(var i = 0, len = arguments.length; i < len; i++) {
36602 this.add(arguments[i]);
36606 if(this.hasPanel(panel)){
36607 this.showPanel(panel);
36610 var el = panel.getEl();
36611 if(el.dom.parentNode != this.mgr.el.dom){
36612 this.mgr.el.dom.appendChild(el.dom);
36614 if(panel.setRegion){
36615 panel.setRegion(this);
36617 this.panels.add(panel);
36618 el.setStyle("position", "absolute");
36619 if(!panel.background){
36620 this.setActivePanel(panel);
36621 if(this.config.initialSize && this.panels.getCount()==1){
36622 this.resizeTo(this.config.initialSize);
36625 this.fireEvent("paneladded", this, panel);
36630 * Returns true if the panel is in this region.
36631 * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
36632 * @return {Boolean}
36634 hasPanel : function(panel){
36635 if(typeof panel == "object"){ // must be panel obj
36636 panel = panel.getId();
36638 return this.getPanel(panel) ? true : false;
36642 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36643 * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
36644 * @param {Boolean} preservePanel Overrides the config preservePanel option
36645 * @return {Roo.panel.Content} The panel that was removed
36647 remove : function(panel, preservePanel){
36648 panel = this.getPanel(panel);
36653 this.fireEvent("beforeremove", this, panel, e);
36654 if(e.cancel === true){
36657 var panelId = panel.getId();
36658 this.panels.removeKey(panelId);
36663 * Returns the panel specified or null if it's not in this region.
36664 * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
36665 * @return {Roo.panel.Content}
36667 getPanel : function(id){
36668 if(typeof id == "object"){ // must be panel obj
36671 return this.panels.get(id);
36675 * Returns this regions position (north/south/east/west/center).
36678 getPosition: function(){
36679 return this.position;
36683 * Ext JS Library 1.1.1
36684 * Copyright(c) 2006-2007, Ext JS, LLC.
36686 * Originally Released Under LGPL - original licence link has changed is not relivant.
36689 * <script type="text/javascript">
36693 * @class Roo.LayoutRegion
36694 * @extends Roo.BasicLayoutRegion
36695 * This class represents a region in a layout manager.
36696 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
36697 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
36698 * @cfg {Boolean} floatable False to disable floating (defaults to true)
36699 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36700 * @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})
36701 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36702 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
36703 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36704 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36705 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36706 * @cfg {String} title The title for the region (overrides panel titles)
36707 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36708 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36709 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36710 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36711 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36712 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36713 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36714 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36715 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36716 * @cfg {Boolean} showPin True to show a pin button
36717 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36718 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36719 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36720 * @cfg {Number} width For East/West panels
36721 * @cfg {Number} height For North/South panels
36722 * @cfg {Boolean} split To show the splitter
36723 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36725 Roo.LayoutRegion = function(mgr, config, pos){
36726 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
36727 var dh = Roo.DomHelper;
36728 /** This region's container element
36729 * @type Roo.Element */
36730 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
36731 /** This region's title element
36732 * @type Roo.Element */
36734 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
36735 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
36736 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
36738 this.titleEl.enableDisplayMode();
36739 /** This region's title text element
36740 * @type HTMLElement */
36741 this.titleTextEl = this.titleEl.dom.firstChild;
36742 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36743 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
36744 this.closeBtn.enableDisplayMode();
36745 this.closeBtn.on("click", this.closeClicked, this);
36746 this.closeBtn.hide();
36748 this.createBody(config);
36749 this.visible = true;
36750 this.collapsed = false;
36752 if(config.hideWhenEmpty){
36754 this.on("paneladded", this.validateVisibility, this);
36755 this.on("panelremoved", this.validateVisibility, this);
36757 this.applyConfig(config);
36760 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
36762 createBody : function(){
36763 /** This region's body element
36764 * @type Roo.Element */
36765 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
36768 applyConfig : function(c){
36769 if(c.collapsible && this.position != "center" && !this.collapsedEl){
36770 var dh = Roo.DomHelper;
36771 if(c.titlebar !== false){
36772 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
36773 this.collapseBtn.on("click", this.collapse, this);
36774 this.collapseBtn.enableDisplayMode();
36776 if(c.showPin === true || this.showPin){
36777 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
36778 this.stickBtn.enableDisplayMode();
36779 this.stickBtn.on("click", this.expand, this);
36780 this.stickBtn.hide();
36783 /** This region's collapsed element
36784 * @type Roo.Element */
36785 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36786 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36788 if(c.floatable !== false){
36789 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36790 this.collapsedEl.on("click", this.collapseClick, this);
36793 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36794 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36795 id: "message", unselectable: "on", style:{"float":"left"}});
36796 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36798 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36799 this.expandBtn.on("click", this.expand, this);
36801 if(this.collapseBtn){
36802 this.collapseBtn.setVisible(c.collapsible == true);
36804 this.cmargins = c.cmargins || this.cmargins ||
36805 (this.position == "west" || this.position == "east" ?
36806 {top: 0, left: 2, right:2, bottom: 0} :
36807 {top: 2, left: 0, right:0, bottom: 2});
36808 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36809 this.bottomTabs = c.tabPosition != "top";
36810 this.autoScroll = c.autoScroll || false;
36811 if(this.autoScroll){
36812 this.bodyEl.setStyle("overflow", "auto");
36814 this.bodyEl.setStyle("overflow", "hidden");
36816 //if(c.titlebar !== false){
36817 if((!c.titlebar && !c.title) || c.titlebar === false){
36818 this.titleEl.hide();
36820 this.titleEl.show();
36822 this.titleTextEl.innerHTML = c.title;
36826 this.duration = c.duration || .30;
36827 this.slideDuration = c.slideDuration || .45;
36830 this.collapse(true);
36837 * Returns true if this region is currently visible.
36838 * @return {Boolean}
36840 isVisible : function(){
36841 return this.visible;
36845 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36846 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36848 setCollapsedTitle : function(title){
36849 title = title || " ";
36850 if(this.collapsedTitleTextEl){
36851 this.collapsedTitleTextEl.innerHTML = title;
36855 getBox : function(){
36857 if(!this.collapsed){
36858 b = this.el.getBox(false, true);
36860 b = this.collapsedEl.getBox(false, true);
36865 getMargins : function(){
36866 return this.collapsed ? this.cmargins : this.margins;
36869 highlight : function(){
36870 this.el.addClass("x-layout-panel-dragover");
36873 unhighlight : function(){
36874 this.el.removeClass("x-layout-panel-dragover");
36877 updateBox : function(box){
36879 if(!this.collapsed){
36880 this.el.dom.style.left = box.x + "px";
36881 this.el.dom.style.top = box.y + "px";
36882 this.updateBody(box.width, box.height);
36884 this.collapsedEl.dom.style.left = box.x + "px";
36885 this.collapsedEl.dom.style.top = box.y + "px";
36886 this.collapsedEl.setSize(box.width, box.height);
36889 this.tabs.autoSizeTabs();
36893 updateBody : function(w, h){
36895 this.el.setWidth(w);
36896 w -= this.el.getBorderWidth("rl");
36897 if(this.config.adjustments){
36898 w += this.config.adjustments[0];
36902 this.el.setHeight(h);
36903 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36904 h -= this.el.getBorderWidth("tb");
36905 if(this.config.adjustments){
36906 h += this.config.adjustments[1];
36908 this.bodyEl.setHeight(h);
36910 h = this.tabs.syncHeight(h);
36913 if(this.panelSize){
36914 w = w !== null ? w : this.panelSize.width;
36915 h = h !== null ? h : this.panelSize.height;
36917 if(this.activePanel){
36918 var el = this.activePanel.getEl();
36919 w = w !== null ? w : el.getWidth();
36920 h = h !== null ? h : el.getHeight();
36921 this.panelSize = {width: w, height: h};
36922 this.activePanel.setSize(w, h);
36924 if(Roo.isIE && this.tabs){
36925 this.tabs.el.repaint();
36930 * Returns the container element for this region.
36931 * @return {Roo.Element}
36933 getEl : function(){
36938 * Hides this region.
36941 if(!this.collapsed){
36942 this.el.dom.style.left = "-2000px";
36945 this.collapsedEl.dom.style.left = "-2000px";
36946 this.collapsedEl.hide();
36948 this.visible = false;
36949 this.fireEvent("visibilitychange", this, false);
36953 * Shows this region if it was previously hidden.
36956 if(!this.collapsed){
36959 this.collapsedEl.show();
36961 this.visible = true;
36962 this.fireEvent("visibilitychange", this, true);
36965 closeClicked : function(){
36966 if(this.activePanel){
36967 this.remove(this.activePanel);
36971 collapseClick : function(e){
36973 e.stopPropagation();
36976 e.stopPropagation();
36982 * Collapses this region.
36983 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36985 collapse : function(skipAnim, skipCheck){
36986 if(this.collapsed) {
36990 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36992 this.collapsed = true;
36994 this.split.el.hide();
36996 if(this.config.animate && skipAnim !== true){
36997 this.fireEvent("invalidated", this);
36998 this.animateCollapse();
37000 this.el.setLocation(-20000,-20000);
37002 this.collapsedEl.show();
37003 this.fireEvent("collapsed", this);
37004 this.fireEvent("invalidated", this);
37010 animateCollapse : function(){
37015 * Expands this region if it was previously collapsed.
37016 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37017 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37019 expand : function(e, skipAnim){
37021 e.stopPropagation();
37023 if(!this.collapsed || this.el.hasActiveFx()) {
37027 this.afterSlideIn();
37030 this.collapsed = false;
37031 if(this.config.animate && skipAnim !== true){
37032 this.animateExpand();
37036 this.split.el.show();
37038 this.collapsedEl.setLocation(-2000,-2000);
37039 this.collapsedEl.hide();
37040 this.fireEvent("invalidated", this);
37041 this.fireEvent("expanded", this);
37045 animateExpand : function(){
37049 initTabs : function()
37051 this.bodyEl.setStyle("overflow", "hidden");
37052 var ts = new Roo.panel.Tab(
37055 tabPosition: this.bottomTabs ? 'bottom' : 'top',
37056 disableTooltips: this.config.disableTabTips,
37057 toolbar : this.config.toolbar
37060 if(this.config.hideTabs){
37061 ts.stripWrap.setDisplayed(false);
37064 ts.resizeTabs = this.config.resizeTabs === true;
37065 ts.minTabWidth = this.config.minTabWidth || 40;
37066 ts.maxTabWidth = this.config.maxTabWidth || 250;
37067 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37068 ts.monitorResize = false;
37069 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37070 ts.bodyEl.addClass('x-layout-tabs-body');
37071 this.panels.each(this.initPanelAsTab, this);
37074 initPanelAsTab : function(panel){
37075 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
37076 this.config.closeOnTab && panel.isClosable());
37077 if(panel.tabTip !== undefined){
37078 ti.setTooltip(panel.tabTip);
37080 ti.on("activate", function(){
37081 this.setActivePanel(panel);
37083 if(this.config.closeOnTab){
37084 ti.on("beforeclose", function(t, e){
37086 this.remove(panel);
37092 updatePanelTitle : function(panel, title){
37093 if(this.activePanel == panel){
37094 this.updateTitle(title);
37097 var ti = this.tabs.getTab(panel.getEl().id);
37099 if(panel.tabTip !== undefined){
37100 ti.setTooltip(panel.tabTip);
37105 updateTitle : function(title){
37106 if(this.titleTextEl && !this.config.title){
37107 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
37111 setActivePanel : function(panel){
37112 panel = this.getPanel(panel);
37113 if(this.activePanel && this.activePanel != panel){
37114 this.activePanel.setActiveState(false);
37116 this.activePanel = panel;
37117 panel.setActiveState(true);
37118 if(this.panelSize){
37119 panel.setSize(this.panelSize.width, this.panelSize.height);
37122 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37124 this.updateTitle(panel.getTitle());
37126 this.fireEvent("invalidated", this);
37128 this.fireEvent("panelactivated", this, panel);
37132 * Shows the specified panel.
37133 * @param {Number/String/panel.Content} panelId The panel's index, id or the panel itself
37134 * @return {Roo.panel.Content} The shown panel, or null if a panel could not be found from panelId
37136 showPanel : function(panel)
37138 panel = this.getPanel(panel);
37141 var tab = this.tabs.getTab(panel.getEl().id);
37142 if(tab.isHidden()){
37143 this.tabs.unhideTab(tab.id);
37147 this.setActivePanel(panel);
37154 * Get the active panel for this region.
37155 * @return {Roo.panel.Content} The active panel or null
37157 getActivePanel : function(){
37158 return this.activePanel;
37161 validateVisibility : function(){
37162 if(this.panels.getCount() < 1){
37163 this.updateTitle(" ");
37164 this.closeBtn.hide();
37167 if(!this.isVisible()){
37174 * Adds the passed ContentPanel(s) to this region.
37175 * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
37176 * @return {Roo.panel.Content} The panel added (if only one was added; null otherwise)
37178 add : function(panel){
37179 if(arguments.length > 1){
37180 for(var i = 0, len = arguments.length; i < len; i++) {
37181 this.add(arguments[i]);
37185 if(this.hasPanel(panel)){
37186 this.showPanel(panel);
37189 panel.setRegion(this);
37190 this.panels.add(panel);
37191 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37192 this.bodyEl.dom.appendChild(panel.getEl().dom);
37193 if(panel.background !== true){
37194 this.setActivePanel(panel);
37196 this.fireEvent("paneladded", this, panel);
37202 this.initPanelAsTab(panel);
37204 if(panel.background !== true){
37205 this.tabs.activate(panel.getEl().id);
37207 this.fireEvent("paneladded", this, panel);
37212 * Hides the tab for the specified panel.
37213 * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
37215 hidePanel : function(panel){
37216 if(this.tabs && (panel = this.getPanel(panel))){
37217 this.tabs.hideTab(panel.getEl().id);
37222 * Unhides the tab for a previously hidden panel.
37223 * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
37225 unhidePanel : function(panel){
37226 if(this.tabs && (panel = this.getPanel(panel))){
37227 this.tabs.unhideTab(panel.getEl().id);
37231 clearPanels : function(){
37232 while(this.panels.getCount() > 0){
37233 this.remove(this.panels.first());
37238 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37239 * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
37240 * @param {Boolean} preservePanel Overrides the config preservePanel option
37241 * @return {Roo.panel.Content} The panel that was removed
37243 remove : function(panel, preservePanel){
37244 panel = this.getPanel(panel);
37249 this.fireEvent("beforeremove", this, panel, e);
37250 if(e.cancel === true){
37253 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37254 var panelId = panel.getId();
37255 this.panels.removeKey(panelId);
37257 document.body.appendChild(panel.getEl().dom);
37260 this.tabs.removeTab(panel.getEl().id);
37261 }else if (!preservePanel){
37262 this.bodyEl.dom.removeChild(panel.getEl().dom);
37264 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37265 var p = this.panels.first();
37266 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37267 tempEl.appendChild(p.getEl().dom);
37268 this.bodyEl.update("");
37269 this.bodyEl.dom.appendChild(p.getEl().dom);
37271 this.updateTitle(p.getTitle());
37273 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37274 this.setActivePanel(p);
37276 panel.setRegion(null);
37277 if(this.activePanel == panel){
37278 this.activePanel = null;
37280 if(this.config.autoDestroy !== false && preservePanel !== true){
37281 try{panel.destroy();}catch(e){}
37283 this.fireEvent("panelremoved", this, panel);
37288 * Returns the TabPanel component used by this region
37289 * @return {Roo.panel.Tab}
37291 getTabs : function(){
37295 createTool : function(parentEl, className){
37296 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
37297 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
37298 btn.addClassOnOver("x-layout-tools-button-over");
37303 * Ext JS Library 1.1.1
37304 * Copyright(c) 2006-2007, Ext JS, LLC.
37306 * Originally Released Under LGPL - original licence link has changed is not relivant.
37309 * <script type="text/javascript">
37315 * @class Roo.SplitLayoutRegion
37316 * @extends Roo.LayoutRegion
37317 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37319 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
37320 this.cursor = cursor;
37321 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
37324 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
37325 splitTip : "Drag to resize.",
37326 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37327 useSplitTips : false,
37329 applyConfig : function(config){
37330 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
37333 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
37334 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
37335 /** The SplitBar for this region
37336 * @type Roo.SplitBar */
37337 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
37338 this.split.on("moved", this.onSplitMove, this);
37339 this.split.useShim = config.useShim === true;
37340 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37341 if(this.useSplitTips){
37342 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37344 if(config.collapsible){
37345 this.split.el.on("dblclick", this.collapse, this);
37348 if(typeof config.minSize != "undefined"){
37349 this.split.minSize = config.minSize;
37351 if(typeof config.maxSize != "undefined"){
37352 this.split.maxSize = config.maxSize;
37354 if(config.hideWhenEmpty || config.hidden || config.collapsed){
37355 this.hideSplitter();
37360 getHMaxSize : function(){
37361 var cmax = this.config.maxSize || 10000;
37362 var center = this.mgr.getRegion("center");
37363 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37366 getVMaxSize : function(){
37367 var cmax = this.config.maxSize || 10000;
37368 var center = this.mgr.getRegion("center");
37369 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37372 onSplitMove : function(split, newSize){
37373 this.fireEvent("resized", this, newSize);
37377 * Returns the {@link Roo.SplitBar} for this region.
37378 * @return {Roo.SplitBar}
37380 getSplitBar : function(){
37385 this.hideSplitter();
37386 Roo.SplitLayoutRegion.superclass.hide.call(this);
37389 hideSplitter : function(){
37391 this.split.el.setLocation(-2000,-2000);
37392 this.split.el.hide();
37398 this.split.el.show();
37400 Roo.SplitLayoutRegion.superclass.show.call(this);
37403 beforeSlide: function(){
37404 if(Roo.isGecko){// firefox overflow auto bug workaround
37405 this.bodyEl.clip();
37407 this.tabs.bodyEl.clip();
37409 if(this.activePanel){
37410 this.activePanel.getEl().clip();
37412 if(this.activePanel.beforeSlide){
37413 this.activePanel.beforeSlide();
37419 afterSlide : function(){
37420 if(Roo.isGecko){// firefox overflow auto bug workaround
37421 this.bodyEl.unclip();
37423 this.tabs.bodyEl.unclip();
37425 if(this.activePanel){
37426 this.activePanel.getEl().unclip();
37427 if(this.activePanel.afterSlide){
37428 this.activePanel.afterSlide();
37434 initAutoHide : function(){
37435 if(this.autoHide !== false){
37436 if(!this.autoHideHd){
37437 var st = new Roo.util.DelayedTask(this.slideIn, this);
37438 this.autoHideHd = {
37439 "mouseout": function(e){
37440 if(!e.within(this.el, true)){
37444 "mouseover" : function(e){
37450 this.el.on(this.autoHideHd);
37454 clearAutoHide : function(){
37455 if(this.autoHide !== false){
37456 this.el.un("mouseout", this.autoHideHd.mouseout);
37457 this.el.un("mouseover", this.autoHideHd.mouseover);
37461 clearMonitor : function(){
37462 Roo.get(document).un("click", this.slideInIf, this);
37465 // these names are backwards but not changed for compat
37466 slideOut : function(){
37467 if(this.isSlid || this.el.hasActiveFx()){
37470 this.isSlid = true;
37471 if(this.collapseBtn){
37472 this.collapseBtn.hide();
37474 this.closeBtnState = this.closeBtn.getStyle('display');
37475 this.closeBtn.hide();
37477 this.stickBtn.show();
37480 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37481 this.beforeSlide();
37482 this.el.setStyle("z-index", 10001);
37483 this.el.slideIn(this.getSlideAnchor(), {
37484 callback: function(){
37486 this.initAutoHide();
37487 Roo.get(document).on("click", this.slideInIf, this);
37488 this.fireEvent("slideshow", this);
37495 afterSlideIn : function(){
37496 this.clearAutoHide();
37497 this.isSlid = false;
37498 this.clearMonitor();
37499 this.el.setStyle("z-index", "");
37500 if(this.collapseBtn){
37501 this.collapseBtn.show();
37503 this.closeBtn.setStyle('display', this.closeBtnState);
37505 this.stickBtn.hide();
37507 this.fireEvent("slidehide", this);
37510 slideIn : function(cb){
37511 if(!this.isSlid || this.el.hasActiveFx()){
37515 this.isSlid = false;
37516 this.beforeSlide();
37517 this.el.slideOut(this.getSlideAnchor(), {
37518 callback: function(){
37519 this.el.setLeftTop(-10000, -10000);
37521 this.afterSlideIn();
37529 slideInIf : function(e){
37530 if(!e.within(this.el)){
37535 animateCollapse : function(){
37536 this.beforeSlide();
37537 this.el.setStyle("z-index", 20000);
37538 var anchor = this.getSlideAnchor();
37539 this.el.slideOut(anchor, {
37540 callback : function(){
37541 this.el.setStyle("z-index", "");
37542 this.collapsedEl.slideIn(anchor, {duration:.3});
37544 this.el.setLocation(-10000,-10000);
37546 this.fireEvent("collapsed", this);
37553 animateExpand : function(){
37554 this.beforeSlide();
37555 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37556 this.el.setStyle("z-index", 20000);
37557 this.collapsedEl.hide({
37560 this.el.slideIn(this.getSlideAnchor(), {
37561 callback : function(){
37562 this.el.setStyle("z-index", "");
37565 this.split.el.show();
37567 this.fireEvent("invalidated", this);
37568 this.fireEvent("expanded", this);
37596 getAnchor : function(){
37597 return this.anchors[this.position];
37600 getCollapseAnchor : function(){
37601 return this.canchors[this.position];
37604 getSlideAnchor : function(){
37605 return this.sanchors[this.position];
37608 getAlignAdj : function(){
37609 var cm = this.cmargins;
37610 switch(this.position){
37626 getExpandAdj : function(){
37627 var c = this.collapsedEl, cm = this.cmargins;
37628 switch(this.position){
37630 return [-(cm.right+c.getWidth()+cm.left), 0];
37633 return [cm.right+c.getWidth()+cm.left, 0];
37636 return [0, -(cm.top+cm.bottom+c.getHeight())];
37639 return [0, cm.top+cm.bottom+c.getHeight()];
37645 * Ext JS Library 1.1.1
37646 * Copyright(c) 2006-2007, Ext JS, LLC.
37648 * Originally Released Under LGPL - original licence link has changed is not relivant.
37651 * <script type="text/javascript">
37654 * These classes are private internal classes
37656 Roo.CenterLayoutRegion = function(mgr, config){
37657 Roo.LayoutRegion.call(this, mgr, config, "center");
37658 this.visible = true;
37659 this.minWidth = config.minWidth || 20;
37660 this.minHeight = config.minHeight || 20;
37663 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
37665 // center panel can't be hidden
37669 // center panel can't be hidden
37672 getMinWidth: function(){
37673 return this.minWidth;
37676 getMinHeight: function(){
37677 return this.minHeight;
37682 Roo.NorthLayoutRegion = function(mgr, config){
37683 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
37685 this.split.placement = Roo.SplitBar.TOP;
37686 this.split.orientation = Roo.SplitBar.VERTICAL;
37687 this.split.el.addClass("x-layout-split-v");
37689 var size = config.initialSize || config.height;
37690 if(typeof size != "undefined"){
37691 this.el.setHeight(size);
37694 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
37695 orientation: Roo.SplitBar.VERTICAL,
37696 getBox : function(){
37697 if(this.collapsed){
37698 return this.collapsedEl.getBox();
37700 var box = this.el.getBox();
37702 box.height += this.split.el.getHeight();
37707 updateBox : function(box){
37708 if(this.split && !this.collapsed){
37709 box.height -= this.split.el.getHeight();
37710 this.split.el.setLeft(box.x);
37711 this.split.el.setTop(box.y+box.height);
37712 this.split.el.setWidth(box.width);
37714 if(this.collapsed){
37715 this.updateBody(box.width, null);
37717 Roo.LayoutRegion.prototype.updateBox.call(this, box);
37721 Roo.SouthLayoutRegion = function(mgr, config){
37722 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
37724 this.split.placement = Roo.SplitBar.BOTTOM;
37725 this.split.orientation = Roo.SplitBar.VERTICAL;
37726 this.split.el.addClass("x-layout-split-v");
37728 var size = config.initialSize || config.height;
37729 if(typeof size != "undefined"){
37730 this.el.setHeight(size);
37733 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
37734 orientation: Roo.SplitBar.VERTICAL,
37735 getBox : function(){
37736 if(this.collapsed){
37737 return this.collapsedEl.getBox();
37739 var box = this.el.getBox();
37741 var sh = this.split.el.getHeight();
37748 updateBox : function(box){
37749 if(this.split && !this.collapsed){
37750 var sh = this.split.el.getHeight();
37753 this.split.el.setLeft(box.x);
37754 this.split.el.setTop(box.y-sh);
37755 this.split.el.setWidth(box.width);
37757 if(this.collapsed){
37758 this.updateBody(box.width, null);
37760 Roo.LayoutRegion.prototype.updateBox.call(this, box);
37764 Roo.EastLayoutRegion = function(mgr, config){
37765 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
37767 this.split.placement = Roo.SplitBar.RIGHT;
37768 this.split.orientation = Roo.SplitBar.HORIZONTAL;
37769 this.split.el.addClass("x-layout-split-h");
37771 var size = config.initialSize || config.width;
37772 if(typeof size != "undefined"){
37773 this.el.setWidth(size);
37776 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
37777 orientation: Roo.SplitBar.HORIZONTAL,
37778 getBox : function(){
37779 if(this.collapsed){
37780 return this.collapsedEl.getBox();
37782 var box = this.el.getBox();
37784 var sw = this.split.el.getWidth();
37791 updateBox : function(box){
37792 if(this.split && !this.collapsed){
37793 var sw = this.split.el.getWidth();
37795 this.split.el.setLeft(box.x);
37796 this.split.el.setTop(box.y);
37797 this.split.el.setHeight(box.height);
37800 if(this.collapsed){
37801 this.updateBody(null, box.height);
37803 Roo.LayoutRegion.prototype.updateBox.call(this, box);
37807 Roo.WestLayoutRegion = function(mgr, config){
37808 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
37810 this.split.placement = Roo.SplitBar.LEFT;
37811 this.split.orientation = Roo.SplitBar.HORIZONTAL;
37812 this.split.el.addClass("x-layout-split-h");
37814 var size = config.initialSize || config.width;
37815 if(typeof size != "undefined"){
37816 this.el.setWidth(size);
37819 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
37820 orientation: Roo.SplitBar.HORIZONTAL,
37821 getBox : function(){
37822 if(this.collapsed){
37823 return this.collapsedEl.getBox();
37825 var box = this.el.getBox();
37827 box.width += this.split.el.getWidth();
37832 updateBox : function(box){
37833 if(this.split && !this.collapsed){
37834 var sw = this.split.el.getWidth();
37836 this.split.el.setLeft(box.x+box.width);
37837 this.split.el.setTop(box.y);
37838 this.split.el.setHeight(box.height);
37840 if(this.collapsed){
37841 this.updateBody(null, box.height);
37843 Roo.LayoutRegion.prototype.updateBox.call(this, box);
37848 * Ext JS Library 1.1.1
37849 * Copyright(c) 2006-2007, Ext JS, LLC.
37851 * Originally Released Under LGPL - original licence link has changed is not relivant.
37854 * <script type="text/javascript">
37859 * Private internal class for reading and applying state
37861 Roo.LayoutStateManager = function(layout){
37862 // default empty state
37871 Roo.LayoutStateManager.prototype = {
37872 init : function(layout, provider){
37873 this.provider = provider;
37874 var state = provider.get(layout.id+"-layout-state");
37876 var wasUpdating = layout.isUpdating();
37878 layout.beginUpdate();
37880 for(var key in state){
37881 if(typeof state[key] != "function"){
37882 var rstate = state[key];
37883 var r = layout.getRegion(key);
37886 r.resizeTo(rstate.size);
37888 if(rstate.collapsed == true){
37891 r.expand(null, true);
37897 layout.endUpdate();
37899 this.state = state;
37901 this.layout = layout;
37902 layout.on("regionresized", this.onRegionResized, this);
37903 layout.on("regioncollapsed", this.onRegionCollapsed, this);
37904 layout.on("regionexpanded", this.onRegionExpanded, this);
37907 storeState : function(){
37908 this.provider.set(this.layout.id+"-layout-state", this.state);
37911 onRegionResized : function(region, newSize){
37912 this.state[region.getPosition()].size = newSize;
37916 onRegionCollapsed : function(region){
37917 this.state[region.getPosition()].collapsed = true;
37921 onRegionExpanded : function(region){
37922 this.state[region.getPosition()].collapsed = false;
37927 * Ext JS Library 1.1.1
37928 * Copyright(c) 2006-2007, Ext JS, LLC.
37930 * Originally Released Under LGPL - original licence link has changed is not relivant.
37933 * <script type="text/javascript">
37936 * @class Roo.panel.Content
37937 * @extends Roo.util.Observable
37938 * @children Roo.form.Form Roo.JsonView Roo.View
37939 * @parent Roo.BorderLayout Roo.LayoutDialog builder
37940 * A basic Content Panel element.
37941 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37942 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37943 * @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
37944 * @cfg {Boolean} closable True if the panel can be closed/removed
37945 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37946 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37947 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
37948 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37949 * @cfg {String} title The title for this panel
37950 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37951 * @cfg {String} url Calls {@link #setUrl} with this value
37952 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
37953 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37954 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37955 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37956 * @cfg {String} style Extra style to add to the content panel
37957 * @cfg {Roo.menu.Menu} menu popup menu
37960 * Create a new Content Panel.
37961 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37962 * @param {String/Object} config A string to set only the title or a config object
37963 * @param {String} content (optional) Set the HTML content for this panel
37964 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37966 Roo.panel.Content = function(el, config, content){
37969 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
37973 if (config && config.parentLayout) {
37974 el = config.parentLayout.el.createChild();
37977 if(el.autoCreate){ // xtype is available if this is called from factory
37981 this.el = Roo.get(el);
37982 if(!this.el && config && config.autoCreate){
37983 if(typeof config.autoCreate == "object"){
37984 if(!config.autoCreate.id){
37985 config.autoCreate.id = config.id||el;
37987 this.el = Roo.DomHelper.append(document.body,
37988 config.autoCreate, true);
37990 this.el = Roo.DomHelper.append(document.body,
37991 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
37996 this.closable = false;
37997 this.loaded = false;
37998 this.active = false;
37999 if(typeof config == "string"){
38000 this.title = config;
38002 Roo.apply(this, config);
38005 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
38006 this.wrapEl = this.el.wrap();
38007 this.toolbar.container = this.el.insertSibling(false, 'before');
38008 this.toolbar = new Roo.Toolbar(this.toolbar);
38011 // xtype created footer. - not sure if will work as we normally have to render first..
38012 if (this.footer && !this.footer.el && this.footer.xtype) {
38013 if (!this.wrapEl) {
38014 this.wrapEl = this.el.wrap();
38017 this.footer.container = this.wrapEl.createChild();
38019 this.footer = Roo.factory(this.footer, Roo);
38024 this.resizeEl = Roo.get(this.resizeEl, true);
38026 this.resizeEl = this.el;
38028 // handle view.xtype
38036 * Fires when this panel is activated.
38037 * @param {Roo.panel.Content} this
38041 * @event deactivate
38042 * Fires when this panel is activated.
38043 * @param {Roo.panel.Content} this
38045 "deactivate" : true,
38049 * Fires when this panel is resized if fitToFrame is true.
38050 * @param {Roo.panel.Content} this
38051 * @param {Number} width The width after any component adjustments
38052 * @param {Number} height The height after any component adjustments
38058 * Fires when this tab is created
38059 * @param {Roo.panel.Content} this
38069 if(this.autoScroll){
38070 this.resizeEl.setStyle("overflow", "auto");
38072 // fix randome scrolling
38073 this.el.on('scroll', function() {
38074 Roo.log('fix random scolling');
38075 this.scrollTo('top',0);
38078 content = content || this.content;
38080 this.setContent(content);
38082 if(config && config.url){
38083 this.setUrl(this.url, this.params, this.loadOnce);
38088 Roo.panel.Content.superclass.constructor.call(this);
38090 if (this.view && typeof(this.view.xtype) != 'undefined') {
38091 this.view.el = this.el.appendChild(document.createElement("div"));
38092 this.view = Roo.factory(this.view);
38093 this.view.render && this.view.render(false, '');
38097 this.fireEvent('render', this);
38100 Roo.extend(Roo.panel.Content, Roo.util.Observable, {
38102 setRegion : function(region){
38103 this.region = region;
38105 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
38107 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
38112 * Returns the toolbar for this Panel if one was configured.
38113 * @return {Roo.Toolbar}
38115 getToolbar : function(){
38116 return this.toolbar;
38119 setActiveState : function(active){
38120 this.active = active;
38122 this.fireEvent("deactivate", this);
38124 this.fireEvent("activate", this);
38128 * Updates this panel's element
38129 * @param {String} content The new content
38130 * @param {Boolean} loadScripts (optional) true to look for and process scripts
38132 setContent : function(content, loadScripts){
38133 this.el.update(content, loadScripts);
38136 ignoreResize : function(w, h){
38137 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38140 this.lastSize = {width: w, height: h};
38145 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38146 * @return {Roo.UpdateManager} The UpdateManager
38148 getUpdateManager : function(){
38149 return this.el.getUpdateManager();
38152 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38153 * @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:
38156 url: "your-url.php",
38157 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38158 callback: yourFunction,
38159 scope: yourObject, //(optional scope)
38162 text: "Loading...",
38167 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38168 * 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.
38169 * @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}
38170 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38171 * @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.
38172 * @return {Roo.panel.Content} this
38175 var um = this.el.getUpdateManager();
38176 um.update.apply(um, arguments);
38182 * 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.
38183 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38184 * @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)
38185 * @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)
38186 * @return {Roo.UpdateManager} The UpdateManager
38188 setUrl : function(url, params, loadOnce){
38189 if(this.refreshDelegate){
38190 this.removeListener("activate", this.refreshDelegate);
38192 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38193 this.on("activate", this.refreshDelegate);
38194 return this.el.getUpdateManager();
38197 _handleRefresh : function(url, params, loadOnce){
38198 if(!loadOnce || !this.loaded){
38199 var updater = this.el.getUpdateManager();
38200 updater.update(url, params, this._setLoaded.createDelegate(this));
38204 _setLoaded : function(){
38205 this.loaded = true;
38209 * Returns this panel's id
38212 getId : function(){
38217 * Returns this panel's element - used by regiosn to add.
38218 * @return {Roo.Element}
38220 getEl : function(){
38221 return this.wrapEl || this.el;
38224 adjustForComponents : function(width, height)
38226 //Roo.log('adjustForComponents ');
38227 if(this.resizeEl != this.el){
38228 width -= this.el.getFrameWidth('lr');
38229 height -= this.el.getFrameWidth('tb');
38232 var te = this.toolbar.getEl();
38233 height -= te.getHeight();
38234 te.setWidth(width);
38237 var te = this.footer.getEl();
38238 //Roo.log("footer:" + te.getHeight());
38240 height -= te.getHeight();
38241 te.setWidth(width);
38245 if(this.adjustments){
38246 width += this.adjustments[0];
38247 height += this.adjustments[1];
38249 return {"width": width, "height": height};
38252 setSize : function(width, height){
38253 if(this.fitToFrame && !this.ignoreResize(width, height)){
38254 if(this.fitContainer && this.resizeEl != this.el){
38255 this.el.setSize(width, height);
38257 var size = this.adjustForComponents(width, height);
38258 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38259 this.fireEvent('resize', this, size.width, size.height);
38264 * Returns this panel's title
38267 getTitle : function(){
38272 * Set this panel's title
38273 * @param {String} title
38275 setTitle : function(title){
38276 this.title = title;
38278 this.region.updatePanelTitle(this, title);
38283 * Returns true is this panel was configured to be closable
38284 * @return {Boolean}
38286 isClosable : function(){
38287 return this.closable;
38290 beforeSlide : function(){
38292 this.resizeEl.clip();
38295 afterSlide : function(){
38297 this.resizeEl.unclip();
38301 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38302 * Will fail silently if the {@link #setUrl} method has not been called.
38303 * This does not activate the panel, just updates its content.
38305 refresh : function(){
38306 if(this.refreshDelegate){
38307 this.loaded = false;
38308 this.refreshDelegate();
38313 * Destroys this panel
38315 destroy : function(){
38316 this.el.removeAllListeners();
38317 var tempEl = document.createElement("span");
38318 tempEl.appendChild(this.el.dom);
38319 tempEl.innerHTML = "";
38325 * form - if the content panel contains a form - this is a reference to it.
38326 * @type {Roo.form.Form}
38330 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38331 * This contains a reference to it.
38337 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38347 * @param {Object} cfg Xtype definition of item to add.
38350 addxtype : function(cfg) {
38351 if(cfg.xtype.match(/^Cropbox$/)) {
38353 this.cropbox = new Roo.factory(cfg);
38355 this.cropbox.render(this.el);
38357 return this.cropbox;
38360 if (cfg.xtype.match(/^Form$/)) {
38363 //if (this.footer) {
38364 // el = this.footer.container.insertSibling(false, 'before');
38366 el = this.el.createChild();
38369 this.form = new Roo.form.Form(cfg);
38372 if ( this.form.allItems.length) {
38373 this.form.render(el.dom);
38377 // should only have one of theses..
38378 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38379 // views.. should not be just added - used named prop 'view''
38381 cfg.el = this.el.appendChild(document.createElement("div"));
38384 var ret = new Roo.factory(cfg);
38386 ret.render && ret.render(false, ''); // render blank..
38406 * @class Roo.panel.Grid
38407 * @extends Roo.panel.Content
38408 * @parent Roo.BorderLayout Roo.LayoutDialog builder
38410 * Create a new GridPanel.
38411 * @cfg {Roo.grid.Grid} grid The grid for this panel
38413 Roo.panel.Grid = function(grid, config){
38415 // universal ctor...
38416 if (typeof(grid.grid) != 'undefined') {
38418 grid = config.grid;
38420 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38421 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
38423 this.wrapper.dom.appendChild(grid.getGridEl().dom);
38425 Roo.panel.Grid.superclass.constructor.call(this, this.wrapper, config);
38428 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
38430 // xtype created footer. - not sure if will work as we normally have to render first..
38431 if (this.footer && !this.footer.el && this.footer.xtype) {
38433 this.footer.container = this.grid.getView().getFooterPanel(true);
38434 this.footer.dataSource = this.grid.dataSource;
38435 this.footer = Roo.factory(this.footer, Roo);
38439 grid.monitorWindowResize = false; // turn off autosizing
38440 grid.autoHeight = false;
38441 grid.autoWidth = false;
38443 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
38446 Roo.extend(Roo.panel.Grid, Roo.panel.Content, {
38447 getId : function(){
38448 return this.grid.id;
38452 * Returns the grid for this panel
38453 * @return {Roo.grid.Grid}
38455 getGrid : function(){
38459 setSize : function(width, height){
38460 if(!this.ignoreResize(width, height)){
38461 var grid = this.grid;
38462 var size = this.adjustForComponents(width, height);
38463 grid.getGridEl().setSize(size.width, size.height);
38468 beforeSlide : function(){
38469 this.grid.getView().scroller.clip();
38472 afterSlide : function(){
38473 this.grid.getView().scroller.unclip();
38476 destroy : function(){
38477 this.grid.destroy();
38479 Roo.panel.Grid.superclass.destroy.call(this);
38485 * @class Roo.panel.NestedLayout
38486 * @extends Roo.panel.Content
38487 * @parent Roo.BorderLayout Roo.LayoutDialog builder
38488 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
38492 * Create a new NestedLayoutPanel.
38495 * @param {Roo.BorderLayout} layout [required] The layout for this panel
38496 * @param {String/Object} config A string to set only the title or a config object
38498 Roo.panel.NestedLayout = function(layout, config)
38500 // construct with only one argument..
38501 /* FIXME - implement nicer consturctors
38502 if (layout.layout) {
38504 layout = config.layout;
38505 delete config.layout;
38507 if (layout.xtype && !layout.getEl) {
38508 // then layout needs constructing..
38509 layout = Roo.factory(layout, Roo);
38514 Roo.panel.NestedLayout.superclass.constructor.call(this, layout.getEl(), config);
38516 layout.monitorWindowResize = false; // turn off autosizing
38517 this.layout = layout;
38518 this.layout.getEl().addClass("x-layout-nested-layout");
38525 Roo.extend(Roo.panel.NestedLayout, Roo.panel.Content, {
38529 setSize : function(width, height){
38530 if(!this.ignoreResize(width, height)){
38531 var size = this.adjustForComponents(width, height);
38532 var el = this.layout.getEl();
38533 el.setSize(size.width, size.height);
38534 var touch = el.dom.offsetWidth;
38535 this.layout.layout();
38536 // ie requires a double layout on the first pass
38537 if(Roo.isIE && !this.initialized){
38538 this.initialized = true;
38539 this.layout.layout();
38544 // activate all subpanels if not currently active..
38546 setActiveState : function(active){
38547 this.active = active;
38549 this.fireEvent("deactivate", this);
38553 this.fireEvent("activate", this);
38554 // not sure if this should happen before or after..
38555 if (!this.layout) {
38556 return; // should not happen..
38559 for (var r in this.layout.regions) {
38560 reg = this.layout.getRegion(r);
38561 if (reg.getActivePanel()) {
38562 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38563 reg.setActivePanel(reg.getActivePanel());
38566 if (!reg.panels.length) {
38569 reg.showPanel(reg.getPanel(0));
38578 * Returns the nested BorderLayout for this panel
38579 * @return {Roo.BorderLayout}
38581 getLayout : function(){
38582 return this.layout;
38586 * Adds a xtype elements to the layout of the nested panel
38590 xtype : 'ContentPanel',
38597 xtype : 'panel.NestedLayout',
38603 items : [ ... list of content panels or nested layout panels.. ]
38607 * @param {Object} cfg Xtype definition of item to add.
38609 addxtype : function(cfg) {
38610 return this.layout.addxtype(cfg);
38615 Roo.ScrollPanel = function(el, config, content){
38616 config = config || {};
38617 config.fitToFrame = true;
38618 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
38620 this.el.dom.style.overflow = "hidden";
38621 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
38622 this.el.removeClass("x-layout-inactive-content");
38623 this.el.on("mousewheel", this.onWheel, this);
38625 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
38626 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
38627 up.unselectable(); down.unselectable();
38628 up.on("click", this.scrollUp, this);
38629 down.on("click", this.scrollDown, this);
38630 up.addClassOnOver("x-scroller-btn-over");
38631 down.addClassOnOver("x-scroller-btn-over");
38632 up.addClassOnClick("x-scroller-btn-click");
38633 down.addClassOnClick("x-scroller-btn-click");
38634 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
38636 this.resizeEl = this.el;
38637 this.el = wrap; this.up = up; this.down = down;
38640 Roo.extend(Roo.ScrollPanel, Roo.panel.Content, {
38642 wheelIncrement : 5,
38643 scrollUp : function(){
38644 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
38647 scrollDown : function(){
38648 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
38651 afterScroll : function(){
38652 var el = this.resizeEl;
38653 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
38654 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
38655 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
38658 setSize : function(){
38659 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
38660 this.afterScroll();
38663 onWheel : function(e){
38664 var d = e.getWheelDelta();
38665 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
38666 this.afterScroll();
38670 setContent : function(content, loadScripts){
38671 this.resizeEl.update(content, loadScripts);
38679 * @class Roo.panel.Tree
38680 * @extends Roo.panel.Content
38681 * @parent Roo.BorderLayout Roo.LayoutDialog builder
38682 * Treepanel component
38685 * Create a new TreePanel. - defaults to fit/scoll contents.
38686 * @param {String/Object} config A string to set only the panel's title, or a config object
38688 Roo.panel.Tree = function(config){
38689 var el = config.el;
38690 var tree = config.tree;
38691 delete config.tree;
38692 delete config.el; // hopefull!
38694 // wrapper for IE7 strict & safari scroll issue
38696 var treeEl = el.createChild();
38697 config.resizeEl = treeEl;
38701 Roo.panel.Tree.superclass.constructor.call(this, el, config);
38704 this.tree = new Roo.tree.TreePanel(treeEl , tree);
38705 //console.log(tree);
38706 this.on('activate', function()
38708 if (this.tree.rendered) {
38711 //console.log('render tree');
38712 this.tree.render();
38714 // this should not be needed.. - it's actually the 'el' that resizes?
38715 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
38717 //this.on('resize', function (cp, w, h) {
38718 // this.tree.innerCt.setWidth(w);
38719 // this.tree.innerCt.setHeight(h);
38720 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
38727 Roo.extend(Roo.panel.Tree, Roo.panel.Content, {
38731 * @cfg {Roo.tree.panel.Tree} tree [required] The tree TreePanel, with config etc.
38738 * Ext JS Library 1.1.1
38739 * Copyright(c) 2006-2007, Ext JS, LLC.
38741 * Originally Released Under LGPL - original licence link has changed is not relivant.
38744 * <script type="text/javascript">
38749 * @class Roo.ReaderLayout
38750 * @extends Roo.BorderLayout
38751 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
38752 * center region containing two nested regions (a top one for a list view and one for item preview below),
38753 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
38754 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
38755 * expedites the setup of the overall layout and regions for this common application style.
38758 var reader = new Roo.ReaderLayout();
38759 var CP = Roo.panel.Content; // shortcut for adding
38761 reader.beginUpdate();
38762 reader.add("north", new CP("north", "North"));
38763 reader.add("west", new CP("west", {title: "West"}));
38764 reader.add("east", new CP("east", {title: "East"}));
38766 reader.regions.listView.add(new CP("listView", "List"));
38767 reader.regions.preview.add(new CP("preview", "Preview"));
38768 reader.endUpdate();
38771 * Create a new ReaderLayout
38772 * @param {Object} config Configuration options
38773 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
38774 * document.body if omitted)
38776 Roo.ReaderLayout = function(config, renderTo){
38777 var c = config || {size:{}};
38778 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
38779 north: c.north !== false ? Roo.apply({
38783 }, c.north) : false,
38784 west: c.west !== false ? Roo.apply({
38792 margins:{left:5,right:0,bottom:5,top:5},
38793 cmargins:{left:5,right:5,bottom:5,top:5}
38794 }, c.west) : false,
38795 east: c.east !== false ? Roo.apply({
38803 margins:{left:0,right:5,bottom:5,top:5},
38804 cmargins:{left:5,right:5,bottom:5,top:5}
38805 }, c.east) : false,
38806 center: Roo.apply({
38807 tabPosition: 'top',
38811 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
38815 this.el.addClass('x-reader');
38817 this.beginUpdate();
38819 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
38820 south: c.preview !== false ? Roo.apply({
38827 cmargins:{top:5,left:0, right:0, bottom:0}
38828 }, c.preview) : false,
38829 center: Roo.apply({
38835 this.add('center', new Roo.panel.NestedLayout(inner,
38836 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
38840 this.regions.preview = inner.getRegion('south');
38841 this.regions.listView = inner.getRegion('center');
38844 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
38846 * Ext JS Library 1.1.1
38847 * Copyright(c) 2006-2007, Ext JS, LLC.
38849 * Originally Released Under LGPL - original licence link has changed is not relivant.
38852 * <script type="text/javascript">
38856 * @class Roo.grid.Grid
38857 * @extends Roo.util.Observable
38858 * This class represents the primary interface of a component based grid control.
38859 * <br><br>Usage:<pre><code>
38860 var grid = new Roo.grid.Grid("my-container-id", {
38863 selModel: mySelectionModel,
38864 autoSizeColumns: true,
38865 monitorWindowResize: false,
38866 trackMouseOver: true
38871 * <b>Common Problems:</b><br/>
38872 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
38873 * element will correct this<br/>
38874 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
38875 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
38876 * are unpredictable.<br/>
38877 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
38878 * grid to calculate dimensions/offsets.<br/>
38880 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38881 * The container MUST have some type of size defined for the grid to fill. The container will be
38882 * automatically set to position relative if it isn't already.
38883 * @param {Object} config A config object that sets properties on this grid.
38885 Roo.grid.Grid = function(container, config){
38886 // initialize the container
38887 this.container = Roo.get(container);
38888 this.container.update("");
38889 this.container.setStyle("overflow", "hidden");
38890 this.container.addClass('x-grid-container');
38892 this.id = this.container.id;
38894 Roo.apply(this, config);
38895 // check and correct shorthanded configs
38897 this.dataSource = this.ds;
38901 this.colModel = this.cm;
38905 this.selModel = this.sm;
38909 if (this.selModel) {
38910 this.selModel = Roo.factory(this.selModel, Roo.grid);
38911 this.sm = this.selModel;
38912 this.sm.xmodule = this.xmodule || false;
38914 if (typeof(this.colModel.config) == 'undefined') {
38915 this.colModel = new Roo.grid.ColumnModel(this.colModel);
38916 this.cm = this.colModel;
38917 this.cm.xmodule = this.xmodule || false;
38919 if (this.dataSource) {
38920 this.dataSource= Roo.factory(this.dataSource, Roo.data);
38921 this.ds = this.dataSource;
38922 this.ds.xmodule = this.xmodule || false;
38929 this.container.setWidth(this.width);
38933 this.container.setHeight(this.height);
38940 * The raw click event for the entire grid.
38941 * @param {Roo.EventObject} e
38946 * The raw dblclick event for the entire grid.
38947 * @param {Roo.EventObject} e
38951 * @event contextmenu
38952 * The raw contextmenu event for the entire grid.
38953 * @param {Roo.EventObject} e
38955 "contextmenu" : true,
38958 * The raw mousedown event for the entire grid.
38959 * @param {Roo.EventObject} e
38961 "mousedown" : true,
38964 * The raw mouseup event for the entire grid.
38965 * @param {Roo.EventObject} e
38970 * The raw mouseover event for the entire grid.
38971 * @param {Roo.EventObject} e
38973 "mouseover" : true,
38976 * The raw mouseout event for the entire grid.
38977 * @param {Roo.EventObject} e
38982 * The raw keypress event for the entire grid.
38983 * @param {Roo.EventObject} e
38988 * The raw keydown event for the entire grid.
38989 * @param {Roo.EventObject} e
38997 * Fires when a cell is clicked
38998 * @param {Grid} this
38999 * @param {Number} rowIndex
39000 * @param {Number} columnIndex
39001 * @param {Roo.EventObject} e
39003 "cellclick" : true,
39005 * @event celldblclick
39006 * Fires when a cell is double clicked
39007 * @param {Grid} this
39008 * @param {Number} rowIndex
39009 * @param {Number} columnIndex
39010 * @param {Roo.EventObject} e
39012 "celldblclick" : true,
39015 * Fires when a row is clicked
39016 * @param {Grid} this
39017 * @param {Number} rowIndex
39018 * @param {Roo.EventObject} e
39022 * @event rowdblclick
39023 * Fires when a row is double clicked
39024 * @param {Grid} this
39025 * @param {Number} rowIndex
39026 * @param {Roo.EventObject} e
39028 "rowdblclick" : true,
39030 * @event headerclick
39031 * Fires when a header is clicked
39032 * @param {Grid} this
39033 * @param {Number} columnIndex
39034 * @param {Roo.EventObject} e
39036 "headerclick" : true,
39038 * @event headerdblclick
39039 * Fires when a header cell is double clicked
39040 * @param {Grid} this
39041 * @param {Number} columnIndex
39042 * @param {Roo.EventObject} e
39044 "headerdblclick" : true,
39046 * @event rowcontextmenu
39047 * Fires when a row is right clicked
39048 * @param {Grid} this
39049 * @param {Number} rowIndex
39050 * @param {Roo.EventObject} e
39052 "rowcontextmenu" : true,
39054 * @event cellcontextmenu
39055 * Fires when a cell is right clicked
39056 * @param {Grid} this
39057 * @param {Number} rowIndex
39058 * @param {Number} cellIndex
39059 * @param {Roo.EventObject} e
39061 "cellcontextmenu" : true,
39063 * @event headercontextmenu
39064 * Fires when a header is right clicked
39065 * @param {Grid} this
39066 * @param {Number} columnIndex
39067 * @param {Roo.EventObject} e
39069 "headercontextmenu" : true,
39071 * @event bodyscroll
39072 * Fires when the body element is scrolled
39073 * @param {Number} scrollLeft
39074 * @param {Number} scrollTop
39076 "bodyscroll" : true,
39078 * @event columnresize
39079 * Fires when the user resizes a column
39080 * @param {Number} columnIndex
39081 * @param {Number} newSize
39083 "columnresize" : true,
39085 * @event columnmove
39086 * Fires when the user moves a column
39087 * @param {Number} oldIndex
39088 * @param {Number} newIndex
39090 "columnmove" : true,
39093 * Fires when row(s) start being dragged
39094 * @param {Grid} this
39095 * @param {Roo.GridDD} dd The drag drop object
39096 * @param {event} e The raw browser event
39098 "startdrag" : true,
39101 * Fires when a drag operation is complete
39102 * @param {Grid} this
39103 * @param {Roo.GridDD} dd The drag drop object
39104 * @param {event} e The raw browser event
39109 * Fires when dragged row(s) are dropped on a valid DD target
39110 * @param {Grid} this
39111 * @param {Roo.GridDD} dd The drag drop object
39112 * @param {String} targetId The target drag drop object
39113 * @param {event} e The raw browser event
39118 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
39119 * @param {Grid} this
39120 * @param {Roo.GridDD} dd The drag drop object
39121 * @param {String} targetId The target drag drop object
39122 * @param {event} e The raw browser event
39127 * Fires when the dragged row(s) first cross another DD target while being dragged
39128 * @param {Grid} this
39129 * @param {Roo.GridDD} dd The drag drop object
39130 * @param {String} targetId The target drag drop object
39131 * @param {event} e The raw browser event
39133 "dragenter" : true,
39136 * Fires when the dragged row(s) leave another DD target while being dragged
39137 * @param {Grid} this
39138 * @param {Roo.GridDD} dd The drag drop object
39139 * @param {String} targetId The target drag drop object
39140 * @param {event} e The raw browser event
39145 * Fires when a row is rendered, so you can change add a style to it.
39146 * @param {GridView} gridview The grid view
39147 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
39153 * Fires when the grid is rendered
39154 * @param {Grid} grid
39159 Roo.grid.Grid.superclass.constructor.call(this);
39161 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
39164 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
39167 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
39170 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
39173 * @cfg {Roo.data.Store} ds The data store for the grid
39176 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
39179 * @cfg {String} ddGroup - drag drop group.
39182 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
39186 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
39188 minColumnWidth : 25,
39191 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
39192 * <b>on initial render.</b> It is more efficient to explicitly size the columns
39193 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
39195 autoSizeColumns : false,
39198 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
39200 autoSizeHeaders : true,
39203 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
39205 monitorWindowResize : true,
39208 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
39209 * rows measured to get a columns size. Default is 0 (all rows).
39211 maxRowsToMeasure : 0,
39214 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
39216 trackMouseOver : true,
39219 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
39222 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
39226 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
39228 enableDragDrop : false,
39231 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
39233 enableColumnMove : true,
39236 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
39238 enableColumnHide : true,
39241 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
39243 enableRowHeightSync : false,
39246 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
39251 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
39253 autoHeight : false,
39256 * @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.
39258 autoExpandColumn : false,
39261 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
39264 autoExpandMin : 50,
39267 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
39269 autoExpandMax : 1000,
39272 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
39277 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
39281 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
39285 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
39287 sortColMenu : false,
39293 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
39294 * of a fixed width. Default is false.
39297 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
39302 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
39303 * %0 is replaced with the number of selected rows.
39305 ddText : "{0} selected row{1}",
39309 * Called once after all setup has been completed and the grid is ready to be rendered.
39310 * @return {Roo.grid.Grid} this
39312 render : function()
39314 var c = this.container;
39315 // try to detect autoHeight/width mode
39316 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
39317 this.autoHeight = true;
39319 var view = this.getView();
39322 c.on("click", this.onClick, this);
39323 c.on("dblclick", this.onDblClick, this);
39324 c.on("contextmenu", this.onContextMenu, this);
39325 c.on("keydown", this.onKeyDown, this);
39327 c.on("touchstart", this.onTouchStart, this);
39330 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
39332 this.getSelectionModel().init(this);
39337 this.loadMask = new Roo.LoadMask(this.container,
39338 Roo.apply({store:this.dataSource}, this.loadMask));
39342 if (this.toolbar && this.toolbar.xtype) {
39343 this.toolbar.container = this.getView().getHeaderPanel(true);
39344 this.toolbar = new Roo.Toolbar(this.toolbar);
39346 if (this.footer && this.footer.xtype) {
39347 this.footer.dataSource = this.getDataSource();
39348 this.footer.container = this.getView().getFooterPanel(true);
39349 this.footer = Roo.factory(this.footer, Roo);
39351 if (this.dropTarget && this.dropTarget.xtype) {
39352 delete this.dropTarget.xtype;
39353 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
39357 this.rendered = true;
39358 this.fireEvent('render', this);
39363 * Reconfigures the grid to use a different Store and Column Model.
39364 * The View will be bound to the new objects and refreshed.
39365 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
39366 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
39368 reconfigure : function(dataSource, colModel){
39370 this.loadMask.destroy();
39371 this.loadMask = new Roo.LoadMask(this.container,
39372 Roo.apply({store:dataSource}, this.loadMask));
39374 this.view.bind(dataSource, colModel);
39375 this.dataSource = dataSource;
39376 this.colModel = colModel;
39377 this.view.refresh(true);
39381 * Add's a column, default at the end..
39383 * @param {int} position to add (default end)
39384 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
39386 addColumns : function(pos, ar)
39389 for (var i =0;i< ar.length;i++) {
39391 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
39392 this.cm.lookup[cfg.id] = cfg;
39396 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
39397 pos = this.cm.config.length; //this.cm.config.push(cfg);
39399 pos = Math.max(0,pos);
39402 this.cm.config.splice.apply(this.cm.config, ar);
39406 this.view.generateRules(this.cm);
39407 this.view.refresh(true);
39415 onKeyDown : function(e){
39416 this.fireEvent("keydown", e);
39420 * Destroy this grid.
39421 * @param {Boolean} removeEl True to remove the element
39423 destroy : function(removeEl, keepListeners){
39425 this.loadMask.destroy();
39427 var c = this.container;
39428 c.removeAllListeners();
39429 this.view.destroy();
39430 this.colModel.purgeListeners();
39431 if(!keepListeners){
39432 this.purgeListeners();
39435 if(removeEl === true){
39441 processEvent : function(name, e){
39442 // does this fire select???
39443 //Roo.log('grid:processEvent ' + name);
39445 if (name != 'touchstart' ) {
39446 this.fireEvent(name, e);
39449 var t = e.getTarget();
39451 var header = v.findHeaderIndex(t);
39452 if(header !== false){
39453 var ename = name == 'touchstart' ? 'click' : name;
39455 this.fireEvent("header" + ename, this, header, e);
39457 var row = v.findRowIndex(t);
39458 var cell = v.findCellIndex(t);
39459 if (name == 'touchstart') {
39460 // first touch is always a click.
39461 // hopefull this happens after selection is updated.?
39464 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
39465 var cs = this.selModel.getSelectedCell();
39466 if (row == cs[0] && cell == cs[1]){
39470 if (typeof(this.selModel.getSelections) != 'undefined') {
39471 var cs = this.selModel.getSelections();
39472 var ds = this.dataSource;
39473 if (cs.length == 1 && ds.getAt(row) == cs[0]){
39484 this.fireEvent("row" + name, this, row, e);
39485 if(cell !== false){
39486 this.fireEvent("cell" + name, this, row, cell, e);
39493 onClick : function(e){
39494 this.processEvent("click", e);
39497 onTouchStart : function(e){
39498 this.processEvent("touchstart", e);
39502 onContextMenu : function(e, t){
39503 this.processEvent("contextmenu", e);
39507 onDblClick : function(e){
39508 this.processEvent("dblclick", e);
39512 walkCells : function(row, col, step, fn, scope){
39513 var cm = this.colModel, clen = cm.getColumnCount();
39514 var ds = this.dataSource, rlen = ds.getCount(), first = true;
39526 if(fn.call(scope || this, row, col, cm) === true){
39544 if(fn.call(scope || this, row, col, cm) === true){
39556 getSelections : function(){
39557 return this.selModel.getSelections();
39561 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
39562 * but if manual update is required this method will initiate it.
39564 autoSize : function(){
39566 this.view.layout();
39567 if(this.view.adjustForScroll){
39568 this.view.adjustForScroll();
39574 * Returns the grid's underlying element.
39575 * @return {Element} The element
39577 getGridEl : function(){
39578 return this.container;
39581 // private for compatibility, overridden by editor grid
39582 stopEditing : function(){},
39585 * Returns the grid's SelectionModel.
39586 * @return {SelectionModel}
39588 getSelectionModel : function(){
39589 if(!this.selModel){
39590 this.selModel = new Roo.grid.RowSelectionModel();
39592 return this.selModel;
39596 * Returns the grid's DataSource.
39597 * @return {DataSource}
39599 getDataSource : function(){
39600 return this.dataSource;
39604 * Returns the grid's ColumnModel.
39605 * @return {ColumnModel}
39607 getColumnModel : function(){
39608 return this.colModel;
39612 * Returns the grid's GridView object.
39613 * @return {GridView}
39615 getView : function(){
39617 this.view = new Roo.grid.GridView(this.viewConfig);
39618 this.relayEvents(this.view, [
39619 "beforerowremoved", "beforerowsinserted",
39620 "beforerefresh", "rowremoved",
39621 "rowsinserted", "rowupdated" ,"refresh"
39627 * Called to get grid's drag proxy text, by default returns this.ddText.
39628 * Override this to put something different in the dragged text.
39631 getDragDropText : function(){
39632 var count = this.selModel.getCount();
39633 return String.format(this.ddText, count, count == 1 ? '' : 's');
39638 * Ext JS Library 1.1.1
39639 * Copyright(c) 2006-2007, Ext JS, LLC.
39641 * Originally Released Under LGPL - original licence link has changed is not relivant.
39644 * <script type="text/javascript">
39647 * @class Roo.grid.AbstractGridView
39648 * @extends Roo.util.Observable
39650 * Abstract base class for grid Views
39653 Roo.grid.AbstractGridView = function(){
39657 "beforerowremoved" : true,
39658 "beforerowsinserted" : true,
39659 "beforerefresh" : true,
39660 "rowremoved" : true,
39661 "rowsinserted" : true,
39662 "rowupdated" : true,
39665 Roo.grid.AbstractGridView.superclass.constructor.call(this);
39668 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
39669 rowClass : "x-grid-row",
39670 cellClass : "x-grid-cell",
39671 tdClass : "x-grid-td",
39672 hdClass : "x-grid-hd",
39673 splitClass : "x-grid-hd-split",
39675 init: function(grid){
39677 var cid = this.grid.getGridEl().id;
39678 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
39679 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
39680 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
39681 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
39684 getColumnRenderers : function(){
39685 var renderers = [];
39686 var cm = this.grid.colModel;
39687 var colCount = cm.getColumnCount();
39688 for(var i = 0; i < colCount; i++){
39689 renderers[i] = cm.getRenderer(i);
39694 getColumnIds : function(){
39696 var cm = this.grid.colModel;
39697 var colCount = cm.getColumnCount();
39698 for(var i = 0; i < colCount; i++){
39699 ids[i] = cm.getColumnId(i);
39704 getDataIndexes : function(){
39705 if(!this.indexMap){
39706 this.indexMap = this.buildIndexMap();
39708 return this.indexMap.colToData;
39711 getColumnIndexByDataIndex : function(dataIndex){
39712 if(!this.indexMap){
39713 this.indexMap = this.buildIndexMap();
39715 return this.indexMap.dataToCol[dataIndex];
39719 * Set a css style for a column dynamically.
39720 * @param {Number} colIndex The index of the column
39721 * @param {String} name The css property name
39722 * @param {String} value The css value
39724 setCSSStyle : function(colIndex, name, value){
39725 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
39726 Roo.util.CSS.updateRule(selector, name, value);
39729 generateRules : function(cm){
39730 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
39731 Roo.util.CSS.removeStyleSheet(rulesId);
39732 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
39733 var cid = cm.getColumnId(i);
39734 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
39735 this.tdSelector, cid, " {\n}\n",
39736 this.hdSelector, cid, " {\n}\n",
39737 this.splitSelector, cid, " {\n}\n");
39739 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
39743 * Ext JS Library 1.1.1
39744 * Copyright(c) 2006-2007, Ext JS, LLC.
39746 * Originally Released Under LGPL - original licence link has changed is not relivant.
39749 * <script type="text/javascript">
39753 // This is a support class used internally by the Grid components
39754 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
39756 this.view = grid.getView();
39757 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
39758 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
39760 this.setHandleElId(Roo.id(hd));
39761 this.setOuterHandleElId(Roo.id(hd2));
39763 this.scroll = false;
39765 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
39767 getDragData : function(e){
39768 var t = Roo.lib.Event.getTarget(e);
39769 var h = this.view.findHeaderCell(t);
39771 return {ddel: h.firstChild, header:h};
39776 onInitDrag : function(e){
39777 this.view.headersDisabled = true;
39778 var clone = this.dragData.ddel.cloneNode(true);
39779 clone.id = Roo.id();
39780 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
39781 this.proxy.update(clone);
39785 afterValidDrop : function(){
39787 setTimeout(function(){
39788 v.headersDisabled = false;
39792 afterInvalidDrop : function(){
39794 setTimeout(function(){
39795 v.headersDisabled = false;
39801 * Ext JS Library 1.1.1
39802 * Copyright(c) 2006-2007, Ext JS, LLC.
39804 * Originally Released Under LGPL - original licence link has changed is not relivant.
39807 * <script type="text/javascript">
39810 // This is a support class used internally by the Grid components
39811 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
39813 this.view = grid.getView();
39814 // split the proxies so they don't interfere with mouse events
39815 this.proxyTop = Roo.DomHelper.append(document.body, {
39816 cls:"col-move-top", html:" "
39818 this.proxyBottom = Roo.DomHelper.append(document.body, {
39819 cls:"col-move-bottom", html:" "
39821 this.proxyTop.hide = this.proxyBottom.hide = function(){
39822 this.setLeftTop(-100,-100);
39823 this.setStyle("visibility", "hidden");
39825 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
39826 // temporarily disabled
39827 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
39828 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
39830 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
39831 proxyOffsets : [-4, -9],
39832 fly: Roo.Element.fly,
39834 getTargetFromEvent : function(e){
39835 var t = Roo.lib.Event.getTarget(e);
39836 var cindex = this.view.findCellIndex(t);
39837 if(cindex !== false){
39838 return this.view.getHeaderCell(cindex);
39843 nextVisible : function(h){
39844 var v = this.view, cm = this.grid.colModel;
39847 if(!cm.isHidden(v.getCellIndex(h))){
39855 prevVisible : function(h){
39856 var v = this.view, cm = this.grid.colModel;
39859 if(!cm.isHidden(v.getCellIndex(h))){
39867 positionIndicator : function(h, n, e){
39868 var x = Roo.lib.Event.getPageX(e);
39869 var r = Roo.lib.Dom.getRegion(n.firstChild);
39870 var px, pt, py = r.top + this.proxyOffsets[1];
39871 if((r.right - x) <= (r.right-r.left)/2){
39872 px = r.right+this.view.borderWidth;
39878 var oldIndex = this.view.getCellIndex(h);
39879 var newIndex = this.view.getCellIndex(n);
39881 if(this.grid.colModel.isFixed(newIndex)){
39885 var locked = this.grid.colModel.isLocked(newIndex);
39890 if(oldIndex < newIndex){
39893 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
39896 px += this.proxyOffsets[0];
39897 this.proxyTop.setLeftTop(px, py);
39898 this.proxyTop.show();
39899 if(!this.bottomOffset){
39900 this.bottomOffset = this.view.mainHd.getHeight();
39902 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
39903 this.proxyBottom.show();
39907 onNodeEnter : function(n, dd, e, data){
39908 if(data.header != n){
39909 this.positionIndicator(data.header, n, e);
39913 onNodeOver : function(n, dd, e, data){
39914 var result = false;
39915 if(data.header != n){
39916 result = this.positionIndicator(data.header, n, e);
39919 this.proxyTop.hide();
39920 this.proxyBottom.hide();
39922 return result ? this.dropAllowed : this.dropNotAllowed;
39925 onNodeOut : function(n, dd, e, data){
39926 this.proxyTop.hide();
39927 this.proxyBottom.hide();
39930 onNodeDrop : function(n, dd, e, data){
39931 var h = data.header;
39933 var cm = this.grid.colModel;
39934 var x = Roo.lib.Event.getPageX(e);
39935 var r = Roo.lib.Dom.getRegion(n.firstChild);
39936 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
39937 var oldIndex = this.view.getCellIndex(h);
39938 var newIndex = this.view.getCellIndex(n);
39939 var locked = cm.isLocked(newIndex);
39943 if(oldIndex < newIndex){
39946 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
39949 cm.setLocked(oldIndex, locked, true);
39950 cm.moveColumn(oldIndex, newIndex);
39951 this.grid.fireEvent("columnmove", oldIndex, newIndex);
39959 * Ext JS Library 1.1.1
39960 * Copyright(c) 2006-2007, Ext JS, LLC.
39962 * Originally Released Under LGPL - original licence link has changed is not relivant.
39965 * <script type="text/javascript">
39969 * @class Roo.grid.GridView
39970 * @extends Roo.util.Observable
39973 * @param {Object} config
39975 Roo.grid.GridView = function(config){
39976 Roo.grid.GridView.superclass.constructor.call(this);
39979 Roo.apply(this, config);
39982 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
39984 unselectable : 'unselectable="on"',
39985 unselectableCls : 'x-unselectable',
39988 rowClass : "x-grid-row",
39990 cellClass : "x-grid-col",
39992 tdClass : "x-grid-td",
39994 hdClass : "x-grid-hd",
39996 splitClass : "x-grid-split",
39998 sortClasses : ["sort-asc", "sort-desc"],
40000 enableMoveAnim : false,
40004 dh : Roo.DomHelper,
40006 fly : Roo.Element.fly,
40008 css : Roo.util.CSS,
40014 scrollIncrement : 22,
40016 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
40018 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
40020 bind : function(ds, cm){
40022 this.ds.un("load", this.onLoad, this);
40023 this.ds.un("datachanged", this.onDataChange, this);
40024 this.ds.un("add", this.onAdd, this);
40025 this.ds.un("remove", this.onRemove, this);
40026 this.ds.un("update", this.onUpdate, this);
40027 this.ds.un("clear", this.onClear, this);
40030 ds.on("load", this.onLoad, this);
40031 ds.on("datachanged", this.onDataChange, this);
40032 ds.on("add", this.onAdd, this);
40033 ds.on("remove", this.onRemove, this);
40034 ds.on("update", this.onUpdate, this);
40035 ds.on("clear", this.onClear, this);
40040 this.cm.un("widthchange", this.onColWidthChange, this);
40041 this.cm.un("headerchange", this.onHeaderChange, this);
40042 this.cm.un("hiddenchange", this.onHiddenChange, this);
40043 this.cm.un("columnmoved", this.onColumnMove, this);
40044 this.cm.un("columnlockchange", this.onColumnLock, this);
40047 this.generateRules(cm);
40048 cm.on("widthchange", this.onColWidthChange, this);
40049 cm.on("headerchange", this.onHeaderChange, this);
40050 cm.on("hiddenchange", this.onHiddenChange, this);
40051 cm.on("columnmoved", this.onColumnMove, this);
40052 cm.on("columnlockchange", this.onColumnLock, this);
40057 init: function(grid){
40058 Roo.grid.GridView.superclass.init.call(this, grid);
40060 this.bind(grid.dataSource, grid.colModel);
40062 grid.on("headerclick", this.handleHeaderClick, this);
40064 if(grid.trackMouseOver){
40065 grid.on("mouseover", this.onRowOver, this);
40066 grid.on("mouseout", this.onRowOut, this);
40068 grid.cancelTextSelection = function(){};
40069 this.gridId = grid.id;
40071 var tpls = this.templates || {};
40074 tpls.master = new Roo.Template(
40075 '<div class="x-grid" hidefocus="true">',
40076 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
40077 '<div class="x-grid-topbar"></div>',
40078 '<div class="x-grid-scroller"><div></div></div>',
40079 '<div class="x-grid-locked">',
40080 '<div class="x-grid-header">{lockedHeader}</div>',
40081 '<div class="x-grid-body">{lockedBody}</div>',
40083 '<div class="x-grid-viewport">',
40084 '<div class="x-grid-header">{header}</div>',
40085 '<div class="x-grid-body">{body}</div>',
40087 '<div class="x-grid-bottombar"></div>',
40089 '<div class="x-grid-resize-proxy"> </div>',
40092 tpls.master.disableformats = true;
40096 tpls.header = new Roo.Template(
40097 '<table border="0" cellspacing="0" cellpadding="0">',
40098 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
40101 tpls.header.disableformats = true;
40103 tpls.header.compile();
40106 tpls.hcell = new Roo.Template(
40107 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
40108 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
40111 tpls.hcell.disableFormats = true;
40113 tpls.hcell.compile();
40116 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
40117 this.unselectableCls + '" ' + this.unselectable +'> </div>');
40118 tpls.hsplit.disableFormats = true;
40120 tpls.hsplit.compile();
40123 tpls.body = new Roo.Template(
40124 '<table border="0" cellspacing="0" cellpadding="0">',
40125 "<tbody>{rows}</tbody>",
40128 tpls.body.disableFormats = true;
40130 tpls.body.compile();
40133 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
40134 tpls.row.disableFormats = true;
40136 tpls.row.compile();
40139 tpls.cell = new Roo.Template(
40140 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
40141 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
40142 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
40145 tpls.cell.disableFormats = true;
40147 tpls.cell.compile();
40149 this.templates = tpls;
40152 // remap these for backwards compat
40153 onColWidthChange : function(){
40154 this.updateColumns.apply(this, arguments);
40156 onHeaderChange : function(){
40157 this.updateHeaders.apply(this, arguments);
40159 onHiddenChange : function(){
40160 this.handleHiddenChange.apply(this, arguments);
40162 onColumnMove : function(){
40163 this.handleColumnMove.apply(this, arguments);
40165 onColumnLock : function(){
40166 this.handleLockChange.apply(this, arguments);
40169 onDataChange : function(){
40171 this.updateHeaderSortState();
40174 onClear : function(){
40178 onUpdate : function(ds, record){
40179 this.refreshRow(record);
40182 refreshRow : function(record){
40183 var ds = this.ds, index;
40184 if(typeof record == 'number'){
40186 record = ds.getAt(index);
40188 index = ds.indexOf(record);
40190 this.insertRows(ds, index, index, true);
40191 this.onRemove(ds, record, index+1, true);
40192 this.syncRowHeights(index, index);
40194 this.fireEvent("rowupdated", this, index, record);
40197 onAdd : function(ds, records, index){
40198 this.insertRows(ds, index, index + (records.length-1));
40201 onRemove : function(ds, record, index, isUpdate){
40202 if(isUpdate !== true){
40203 this.fireEvent("beforerowremoved", this, index, record);
40205 var bt = this.getBodyTable(), lt = this.getLockedTable();
40206 if(bt.rows[index]){
40207 bt.firstChild.removeChild(bt.rows[index]);
40209 if(lt.rows[index]){
40210 lt.firstChild.removeChild(lt.rows[index]);
40212 if(isUpdate !== true){
40213 this.stripeRows(index);
40214 this.syncRowHeights(index, index);
40216 this.fireEvent("rowremoved", this, index, record);
40220 onLoad : function(){
40221 this.scrollToTop();
40225 * Scrolls the grid to the top
40227 scrollToTop : function(){
40229 this.scroller.dom.scrollTop = 0;
40235 * Gets a panel in the header of the grid that can be used for toolbars etc.
40236 * After modifying the contents of this panel a call to grid.autoSize() may be
40237 * required to register any changes in size.
40238 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
40239 * @return Roo.Element
40241 getHeaderPanel : function(doShow){
40243 this.headerPanel.show();
40245 return this.headerPanel;
40249 * Gets a panel in the footer of the grid that can be used for toolbars etc.
40250 * After modifying the contents of this panel a call to grid.autoSize() may be
40251 * required to register any changes in size.
40252 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
40253 * @return Roo.Element
40255 getFooterPanel : function(doShow){
40257 this.footerPanel.show();
40259 return this.footerPanel;
40262 initElements : function(){
40263 var E = Roo.Element;
40264 var el = this.grid.getGridEl().dom.firstChild;
40265 var cs = el.childNodes;
40267 this.el = new E(el);
40269 this.focusEl = new E(el.firstChild);
40270 this.focusEl.swallowEvent("click", true);
40272 this.headerPanel = new E(cs[1]);
40273 this.headerPanel.enableDisplayMode("block");
40275 this.scroller = new E(cs[2]);
40276 this.scrollSizer = new E(this.scroller.dom.firstChild);
40278 this.lockedWrap = new E(cs[3]);
40279 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
40280 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
40282 this.mainWrap = new E(cs[4]);
40283 this.mainHd = new E(this.mainWrap.dom.firstChild);
40284 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
40286 this.footerPanel = new E(cs[5]);
40287 this.footerPanel.enableDisplayMode("block");
40289 this.resizeProxy = new E(cs[6]);
40291 this.headerSelector = String.format(
40292 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
40293 this.lockedHd.id, this.mainHd.id
40296 this.splitterSelector = String.format(
40297 '#{0} div.x-grid-split, #{1} div.x-grid-split',
40298 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
40301 idToCssName : function(s)
40303 return s.replace(/[^a-z0-9]+/ig, '-');
40306 getHeaderCell : function(index){
40307 return Roo.DomQuery.select(this.headerSelector)[index];
40310 getHeaderCellMeasure : function(index){
40311 return this.getHeaderCell(index).firstChild;
40314 getHeaderCellText : function(index){
40315 return this.getHeaderCell(index).firstChild.firstChild;
40318 getLockedTable : function(){
40319 return this.lockedBody.dom.firstChild;
40322 getBodyTable : function(){
40323 return this.mainBody.dom.firstChild;
40326 getLockedRow : function(index){
40327 return this.getLockedTable().rows[index];
40330 getRow : function(index){
40331 return this.getBodyTable().rows[index];
40334 getRowComposite : function(index){
40336 this.rowEl = new Roo.CompositeElementLite();
40338 var els = [], lrow, mrow;
40339 if(lrow = this.getLockedRow(index)){
40342 if(mrow = this.getRow(index)){
40345 this.rowEl.elements = els;
40349 * Gets the 'td' of the cell
40351 * @param {Integer} rowIndex row to select
40352 * @param {Integer} colIndex column to select
40356 getCell : function(rowIndex, colIndex){
40357 var locked = this.cm.getLockedCount();
40359 if(colIndex < locked){
40360 source = this.lockedBody.dom.firstChild;
40362 source = this.mainBody.dom.firstChild;
40363 colIndex -= locked;
40365 return source.rows[rowIndex].childNodes[colIndex];
40368 getCellText : function(rowIndex, colIndex){
40369 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
40372 getCellBox : function(cell){
40373 var b = this.fly(cell).getBox();
40374 if(Roo.isOpera){ // opera fails to report the Y
40375 b.y = cell.offsetTop + this.mainBody.getY();
40380 getCellIndex : function(cell){
40381 var id = String(cell.className).match(this.cellRE);
40383 return parseInt(id[1], 10);
40388 findHeaderIndex : function(n){
40389 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
40390 return r ? this.getCellIndex(r) : false;
40393 findHeaderCell : function(n){
40394 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
40395 return r ? r : false;
40398 findRowIndex : function(n){
40402 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
40403 return r ? r.rowIndex : false;
40406 findCellIndex : function(node){
40407 var stop = this.el.dom;
40408 while(node && node != stop){
40409 if(this.findRE.test(node.className)){
40410 return this.getCellIndex(node);
40412 node = node.parentNode;
40417 getColumnId : function(index){
40418 return this.cm.getColumnId(index);
40421 getSplitters : function()
40423 if(this.splitterSelector){
40424 return Roo.DomQuery.select(this.splitterSelector);
40430 getSplitter : function(index){
40431 return this.getSplitters()[index];
40434 onRowOver : function(e, t){
40436 if((row = this.findRowIndex(t)) !== false){
40437 this.getRowComposite(row).addClass("x-grid-row-over");
40441 onRowOut : function(e, t){
40443 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
40444 this.getRowComposite(row).removeClass("x-grid-row-over");
40448 renderHeaders : function(){
40450 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
40451 var cb = [], lb = [], sb = [], lsb = [], p = {};
40452 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
40453 p.cellId = "x-grid-hd-0-" + i;
40454 p.splitId = "x-grid-csplit-0-" + i;
40455 p.id = cm.getColumnId(i);
40456 p.value = cm.getColumnHeader(i) || "";
40457 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
40458 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
40459 if(!cm.isLocked(i)){
40460 cb[cb.length] = ct.apply(p);
40461 sb[sb.length] = st.apply(p);
40463 lb[lb.length] = ct.apply(p);
40464 lsb[lsb.length] = st.apply(p);
40467 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
40468 ht.apply({cells: cb.join(""), splits:sb.join("")})];
40471 updateHeaders : function(){
40472 var html = this.renderHeaders();
40473 this.lockedHd.update(html[0]);
40474 this.mainHd.update(html[1]);
40478 * Focuses the specified row.
40479 * @param {Number} row The row index
40481 focusRow : function(row)
40483 //Roo.log('GridView.focusRow');
40484 var x = this.scroller.dom.scrollLeft;
40485 this.focusCell(row, 0, false);
40486 this.scroller.dom.scrollLeft = x;
40490 * Focuses the specified cell.
40491 * @param {Number} row The row index
40492 * @param {Number} col The column index
40493 * @param {Boolean} hscroll false to disable horizontal scrolling
40495 focusCell : function(row, col, hscroll)
40497 //Roo.log('GridView.focusCell');
40498 var el = this.ensureVisible(row, col, hscroll);
40499 this.focusEl.alignTo(el, "tl-tl");
40501 this.focusEl.focus();
40503 this.focusEl.focus.defer(1, this.focusEl);
40508 * Scrolls the specified cell into view
40509 * @param {Number} row The row index
40510 * @param {Number} col The column index
40511 * @param {Boolean} hscroll false to disable horizontal scrolling
40513 ensureVisible : function(row, col, hscroll)
40515 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
40516 //return null; //disable for testing.
40517 if(typeof row != "number"){
40518 row = row.rowIndex;
40520 if(row < 0 && row >= this.ds.getCount()){
40523 col = (col !== undefined ? col : 0);
40524 var cm = this.grid.colModel;
40525 while(cm.isHidden(col)){
40529 var el = this.getCell(row, col);
40533 var c = this.scroller.dom;
40535 var ctop = parseInt(el.offsetTop, 10);
40536 var cleft = parseInt(el.offsetLeft, 10);
40537 var cbot = ctop + el.offsetHeight;
40538 var cright = cleft + el.offsetWidth;
40540 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
40541 var stop = parseInt(c.scrollTop, 10);
40542 var sleft = parseInt(c.scrollLeft, 10);
40543 var sbot = stop + ch;
40544 var sright = sleft + c.clientWidth;
40546 Roo.log('GridView.ensureVisible:' +
40548 ' c.clientHeight:' + c.clientHeight +
40549 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
40557 c.scrollTop = ctop;
40558 //Roo.log("set scrolltop to ctop DISABLE?");
40559 }else if(cbot > sbot){
40560 //Roo.log("set scrolltop to cbot-ch");
40561 c.scrollTop = cbot-ch;
40564 if(hscroll !== false){
40566 c.scrollLeft = cleft;
40567 }else if(cright > sright){
40568 c.scrollLeft = cright-c.clientWidth;
40575 updateColumns : function(){
40576 this.grid.stopEditing();
40577 var cm = this.grid.colModel, colIds = this.getColumnIds();
40578 //var totalWidth = cm.getTotalWidth();
40580 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
40581 //if(cm.isHidden(i)) continue;
40582 var w = cm.getColumnWidth(i);
40583 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
40584 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
40586 this.updateSplitters();
40589 generateRules : function(cm){
40590 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
40591 Roo.util.CSS.removeStyleSheet(rulesId);
40592 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
40593 var cid = cm.getColumnId(i);
40595 if(cm.config[i].align){
40596 align = 'text-align:'+cm.config[i].align+';';
40599 if(cm.isHidden(i)){
40600 hidden = 'display:none;';
40602 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
40604 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
40605 this.hdSelector, cid, " {\n", align, width, "}\n",
40606 this.tdSelector, cid, " {\n",hidden,"\n}\n",
40607 this.splitSelector, cid, " {\n", hidden , "\n}\n");
40609 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
40612 updateSplitters : function(){
40613 var cm = this.cm, s = this.getSplitters();
40614 if(s){ // splitters not created yet
40615 var pos = 0, locked = true;
40616 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
40617 if(cm.isHidden(i)) {
40620 var w = cm.getColumnWidth(i); // make sure it's a number
40621 if(!cm.isLocked(i) && locked){
40626 s[i].style.left = (pos-this.splitOffset) + "px";
40631 handleHiddenChange : function(colModel, colIndex, hidden){
40633 this.hideColumn(colIndex);
40635 this.unhideColumn(colIndex);
40639 hideColumn : function(colIndex){
40640 var cid = this.getColumnId(colIndex);
40641 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
40642 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
40644 this.updateHeaders();
40646 this.updateSplitters();
40650 unhideColumn : function(colIndex){
40651 var cid = this.getColumnId(colIndex);
40652 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
40653 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
40656 this.updateHeaders();
40658 this.updateSplitters();
40662 insertRows : function(dm, firstRow, lastRow, isUpdate){
40663 if(firstRow == 0 && lastRow == dm.getCount()-1){
40667 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
40669 var s = this.getScrollState();
40670 var markup = this.renderRows(firstRow, lastRow);
40671 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
40672 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
40673 this.restoreScroll(s);
40675 this.fireEvent("rowsinserted", this, firstRow, lastRow);
40676 this.syncRowHeights(firstRow, lastRow);
40677 this.stripeRows(firstRow);
40683 bufferRows : function(markup, target, index){
40684 var before = null, trows = target.rows, tbody = target.tBodies[0];
40685 if(index < trows.length){
40686 before = trows[index];
40688 var b = document.createElement("div");
40689 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
40690 var rows = b.firstChild.rows;
40691 for(var i = 0, len = rows.length; i < len; i++){
40693 tbody.insertBefore(rows[0], before);
40695 tbody.appendChild(rows[0]);
40702 deleteRows : function(dm, firstRow, lastRow){
40703 if(dm.getRowCount()<1){
40704 this.fireEvent("beforerefresh", this);
40705 this.mainBody.update("");
40706 this.lockedBody.update("");
40707 this.fireEvent("refresh", this);
40709 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
40710 var bt = this.getBodyTable();
40711 var tbody = bt.firstChild;
40712 var rows = bt.rows;
40713 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
40714 tbody.removeChild(rows[firstRow]);
40716 this.stripeRows(firstRow);
40717 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
40721 updateRows : function(dataSource, firstRow, lastRow){
40722 var s = this.getScrollState();
40724 this.restoreScroll(s);
40727 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
40731 this.updateHeaderSortState();
40734 getScrollState : function(){
40736 var sb = this.scroller.dom;
40737 return {left: sb.scrollLeft, top: sb.scrollTop};
40740 stripeRows : function(startRow){
40741 if(!this.grid.stripeRows || this.ds.getCount() < 1){
40744 startRow = startRow || 0;
40745 var rows = this.getBodyTable().rows;
40746 var lrows = this.getLockedTable().rows;
40747 var cls = ' x-grid-row-alt ';
40748 for(var i = startRow, len = rows.length; i < len; i++){
40749 var row = rows[i], lrow = lrows[i];
40750 var isAlt = ((i+1) % 2 == 0);
40751 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
40752 if(isAlt == hasAlt){
40756 row.className += " x-grid-row-alt";
40758 row.className = row.className.replace("x-grid-row-alt", "");
40761 lrow.className = row.className;
40766 restoreScroll : function(state){
40767 //Roo.log('GridView.restoreScroll');
40768 var sb = this.scroller.dom;
40769 sb.scrollLeft = state.left;
40770 sb.scrollTop = state.top;
40774 syncScroll : function(){
40775 //Roo.log('GridView.syncScroll');
40776 var sb = this.scroller.dom;
40777 var sh = this.mainHd.dom;
40778 var bs = this.mainBody.dom;
40779 var lv = this.lockedBody.dom;
40780 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
40781 lv.scrollTop = bs.scrollTop = sb.scrollTop;
40784 handleScroll : function(e){
40786 var sb = this.scroller.dom;
40787 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
40791 handleWheel : function(e){
40792 var d = e.getWheelDelta();
40793 this.scroller.dom.scrollTop -= d*22;
40794 // set this here to prevent jumpy scrolling on large tables
40795 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
40799 renderRows : function(startRow, endRow){
40800 // pull in all the crap needed to render rows
40801 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
40802 var colCount = cm.getColumnCount();
40804 if(ds.getCount() < 1){
40808 // build a map for all the columns
40810 for(var i = 0; i < colCount; i++){
40811 var name = cm.getDataIndex(i);
40813 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
40814 renderer : cm.getRenderer(i),
40815 id : cm.getColumnId(i),
40816 locked : cm.isLocked(i),
40817 has_editor : cm.isCellEditable(i)
40821 startRow = startRow || 0;
40822 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
40824 // records to render
40825 var rs = ds.getRange(startRow, endRow);
40827 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
40830 // As much as I hate to duplicate code, this was branched because FireFox really hates
40831 // [].join("") on strings. The performance difference was substantial enough to
40832 // branch this function
40833 doRender : Roo.isGecko ?
40834 function(cs, rs, ds, startRow, colCount, stripe){
40835 var ts = this.templates, ct = ts.cell, rt = ts.row;
40837 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
40839 var hasListener = this.grid.hasListener('rowclass');
40841 for(var j = 0, len = rs.length; j < len; j++){
40842 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
40843 for(var i = 0; i < colCount; i++){
40845 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
40847 p.css = p.attr = "";
40848 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
40849 if(p.value == undefined || p.value === "") {
40850 p.value = " ";
40853 p.css += ' x-grid-editable-cell';
40855 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
40856 p.css += ' x-grid-dirty-cell';
40858 var markup = ct.apply(p);
40866 if(stripe && ((rowIndex+1) % 2 == 0)){
40867 alt.push("x-grid-row-alt")
40870 alt.push( " x-grid-dirty-row");
40873 if(this.getRowClass){
40874 alt.push(this.getRowClass(r, rowIndex));
40880 rowIndex : rowIndex,
40883 this.grid.fireEvent('rowclass', this, rowcfg);
40884 alt.push(rowcfg.rowClass);
40886 rp.alt = alt.join(" ");
40887 lbuf+= rt.apply(rp);
40889 buf+= rt.apply(rp);
40891 return [lbuf, buf];
40893 function(cs, rs, ds, startRow, colCount, stripe){
40894 var ts = this.templates, ct = ts.cell, rt = ts.row;
40896 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
40897 var hasListener = this.grid.hasListener('rowclass');
40900 for(var j = 0, len = rs.length; j < len; j++){
40901 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
40902 for(var i = 0; i < colCount; i++){
40904 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
40906 p.css = p.attr = "";
40907 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
40908 if(p.value == undefined || p.value === "") {
40909 p.value = " ";
40913 p.css += ' x-grid-editable-cell';
40915 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
40916 p.css += ' x-grid-dirty-cell'
40919 var markup = ct.apply(p);
40921 cb[cb.length] = markup;
40923 lcb[lcb.length] = markup;
40927 if(stripe && ((rowIndex+1) % 2 == 0)){
40928 alt.push( "x-grid-row-alt");
40931 alt.push(" x-grid-dirty-row");
40934 if(this.getRowClass){
40935 alt.push( this.getRowClass(r, rowIndex));
40941 rowIndex : rowIndex,
40944 this.grid.fireEvent('rowclass', this, rowcfg);
40945 alt.push(rowcfg.rowClass);
40948 rp.alt = alt.join(" ");
40949 rp.cells = lcb.join("");
40950 lbuf[lbuf.length] = rt.apply(rp);
40951 rp.cells = cb.join("");
40952 buf[buf.length] = rt.apply(rp);
40954 return [lbuf.join(""), buf.join("")];
40957 renderBody : function(){
40958 var markup = this.renderRows();
40959 var bt = this.templates.body;
40960 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
40964 * Refreshes the grid
40965 * @param {Boolean} headersToo
40967 refresh : function(headersToo){
40968 this.fireEvent("beforerefresh", this);
40969 this.grid.stopEditing();
40970 var result = this.renderBody();
40971 this.lockedBody.update(result[0]);
40972 this.mainBody.update(result[1]);
40973 if(headersToo === true){
40974 this.updateHeaders();
40975 this.updateColumns();
40976 this.updateSplitters();
40977 this.updateHeaderSortState();
40979 this.syncRowHeights();
40981 this.fireEvent("refresh", this);
40984 handleColumnMove : function(cm, oldIndex, newIndex){
40985 this.indexMap = null;
40986 var s = this.getScrollState();
40987 this.refresh(true);
40988 this.restoreScroll(s);
40989 this.afterMove(newIndex);
40992 afterMove : function(colIndex){
40993 if(this.enableMoveAnim && Roo.enableFx){
40994 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
40996 // if multisort - fix sortOrder, and reload..
40997 if (this.grid.dataSource.multiSort) {
40998 // the we can call sort again..
40999 var dm = this.grid.dataSource;
41000 var cm = this.grid.colModel;
41002 for(var i = 0; i < cm.config.length; i++ ) {
41004 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
41005 continue; // dont' bother, it's not in sort list or being set.
41008 so.push(cm.config[i].dataIndex);
41011 dm.load(dm.lastOptions);
41018 updateCell : function(dm, rowIndex, dataIndex){
41019 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
41020 if(typeof colIndex == "undefined"){ // not present in grid
41023 var cm = this.grid.colModel;
41024 var cell = this.getCell(rowIndex, colIndex);
41025 var cellText = this.getCellText(rowIndex, colIndex);
41028 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
41029 id : cm.getColumnId(colIndex),
41030 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
41032 var renderer = cm.getRenderer(colIndex);
41033 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
41034 if(typeof val == "undefined" || val === "") {
41037 cellText.innerHTML = val;
41038 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
41039 this.syncRowHeights(rowIndex, rowIndex);
41042 calcColumnWidth : function(colIndex, maxRowsToMeasure){
41044 if(this.grid.autoSizeHeaders){
41045 var h = this.getHeaderCellMeasure(colIndex);
41046 maxWidth = Math.max(maxWidth, h.scrollWidth);
41049 if(this.cm.isLocked(colIndex)){
41050 tb = this.getLockedTable();
41053 tb = this.getBodyTable();
41054 index = colIndex - this.cm.getLockedCount();
41057 var rows = tb.rows;
41058 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
41059 for(var i = 0; i < stopIndex; i++){
41060 var cell = rows[i].childNodes[index].firstChild;
41061 maxWidth = Math.max(maxWidth, cell.scrollWidth);
41064 return maxWidth + /*margin for error in IE*/ 5;
41067 * Autofit a column to its content.
41068 * @param {Number} colIndex
41069 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
41071 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
41072 if(this.cm.isHidden(colIndex)){
41073 return; // can't calc a hidden column
41076 var cid = this.cm.getColumnId(colIndex);
41077 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
41078 if(this.grid.autoSizeHeaders){
41079 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
41082 var newWidth = this.calcColumnWidth(colIndex);
41083 this.cm.setColumnWidth(colIndex,
41084 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
41085 if(!suppressEvent){
41086 this.grid.fireEvent("columnresize", colIndex, newWidth);
41091 * Autofits all columns to their content and then expands to fit any extra space in the grid
41093 autoSizeColumns : function(){
41094 var cm = this.grid.colModel;
41095 var colCount = cm.getColumnCount();
41096 for(var i = 0; i < colCount; i++){
41097 this.autoSizeColumn(i, true, true);
41099 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
41102 this.updateColumns();
41108 * Autofits all columns to the grid's width proportionate with their current size
41109 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
41111 fitColumns : function(reserveScrollSpace){
41112 var cm = this.grid.colModel;
41113 var colCount = cm.getColumnCount();
41117 for (i = 0; i < colCount; i++){
41118 if(!cm.isHidden(i) && !cm.isFixed(i)){
41119 w = cm.getColumnWidth(i);
41125 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
41126 if(reserveScrollSpace){
41129 var frac = (avail - cm.getTotalWidth())/width;
41130 while (cols.length){
41133 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
41135 this.updateColumns();
41139 onRowSelect : function(rowIndex){
41140 var row = this.getRowComposite(rowIndex);
41141 row.addClass("x-grid-row-selected");
41144 onRowDeselect : function(rowIndex){
41145 var row = this.getRowComposite(rowIndex);
41146 row.removeClass("x-grid-row-selected");
41149 onCellSelect : function(row, col){
41150 var cell = this.getCell(row, col);
41152 Roo.fly(cell).addClass("x-grid-cell-selected");
41156 onCellDeselect : function(row, col){
41157 var cell = this.getCell(row, col);
41159 Roo.fly(cell).removeClass("x-grid-cell-selected");
41163 updateHeaderSortState : function(){
41165 // sort state can be single { field: xxx, direction : yyy}
41166 // or { xxx=>ASC , yyy : DESC ..... }
41169 if (!this.ds.multiSort) {
41170 var state = this.ds.getSortState();
41174 mstate[state.field] = state.direction;
41175 // FIXME... - this is not used here.. but might be elsewhere..
41176 this.sortState = state;
41179 mstate = this.ds.sortToggle;
41181 //remove existing sort classes..
41183 var sc = this.sortClasses;
41184 var hds = this.el.select(this.headerSelector).removeClass(sc);
41186 for(var f in mstate) {
41188 var sortColumn = this.cm.findColumnIndex(f);
41190 if(sortColumn != -1){
41191 var sortDir = mstate[f];
41192 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
41201 handleHeaderClick : function(g, index,e){
41203 Roo.log("header click");
41206 // touch events on header are handled by context
41207 this.handleHdCtx(g,index,e);
41212 if(this.headersDisabled){
41215 var dm = g.dataSource, cm = g.colModel;
41216 if(!cm.isSortable(index)){
41221 if (dm.multiSort) {
41222 // update the sortOrder
41224 for(var i = 0; i < cm.config.length; i++ ) {
41226 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
41227 continue; // dont' bother, it's not in sort list or being set.
41230 so.push(cm.config[i].dataIndex);
41236 dm.sort(cm.getDataIndex(index));
41240 destroy : function(){
41242 this.colMenu.removeAll();
41243 Roo.menu.MenuMgr.unregister(this.colMenu);
41244 this.colMenu.getEl().remove();
41245 delete this.colMenu;
41248 this.hmenu.removeAll();
41249 Roo.menu.MenuMgr.unregister(this.hmenu);
41250 this.hmenu.getEl().remove();
41253 if(this.grid.enableColumnMove){
41254 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
41256 for(var dd in dds){
41257 if(!dds[dd].config.isTarget && dds[dd].dragElId){
41258 var elid = dds[dd].dragElId;
41260 Roo.get(elid).remove();
41261 } else if(dds[dd].config.isTarget){
41262 dds[dd].proxyTop.remove();
41263 dds[dd].proxyBottom.remove();
41266 if(Roo.dd.DDM.locationCache[dd]){
41267 delete Roo.dd.DDM.locationCache[dd];
41270 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
41273 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
41274 this.bind(null, null);
41275 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
41278 handleLockChange : function(){
41279 this.refresh(true);
41282 onDenyColumnLock : function(){
41286 onDenyColumnHide : function(){
41290 handleHdMenuClick : function(item){
41291 var index = this.hdCtxIndex;
41292 var cm = this.cm, ds = this.ds;
41295 ds.sort(cm.getDataIndex(index), "ASC");
41298 ds.sort(cm.getDataIndex(index), "DESC");
41301 var lc = cm.getLockedCount();
41302 if(cm.getColumnCount(true) <= lc+1){
41303 this.onDenyColumnLock();
41307 cm.setLocked(index, true, true);
41308 cm.moveColumn(index, lc);
41309 this.grid.fireEvent("columnmove", index, lc);
41311 cm.setLocked(index, true);
41315 var lc = cm.getLockedCount();
41316 if((lc-1) != index){
41317 cm.setLocked(index, false, true);
41318 cm.moveColumn(index, lc-1);
41319 this.grid.fireEvent("columnmove", index, lc-1);
41321 cm.setLocked(index, false);
41324 case 'wider': // used to expand cols on touch..
41326 var cw = cm.getColumnWidth(index);
41327 cw += (item.id == 'wider' ? 1 : -1) * 50;
41328 cw = Math.max(0, cw);
41329 cw = Math.min(cw,4000);
41330 cm.setColumnWidth(index, cw);
41334 index = cm.getIndexById(item.id.substr(4));
41336 if(item.checked && cm.getColumnCount(true) <= 1){
41337 this.onDenyColumnHide();
41340 cm.setHidden(index, item.checked);
41346 beforeColMenuShow : function(){
41347 var cm = this.cm, colCount = cm.getColumnCount();
41348 this.colMenu.removeAll();
41351 for(var i = 0; i < colCount; i++){
41353 id: "col-"+cm.getColumnId(i),
41354 text: cm.getColumnHeader(i),
41355 checked: !cm.isHidden(i),
41360 if (this.grid.sortColMenu) {
41361 items.sort(function(a,b) {
41362 if (a.text == b.text) {
41365 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
41369 for(var i = 0; i < colCount; i++){
41370 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
41374 handleHdCtx : function(g, index, e){
41376 var hd = this.getHeaderCell(index);
41377 this.hdCtxIndex = index;
41378 var ms = this.hmenu.items, cm = this.cm;
41379 ms.get("asc").setDisabled(!cm.isSortable(index));
41380 ms.get("desc").setDisabled(!cm.isSortable(index));
41381 if(this.grid.enableColLock !== false){
41382 ms.get("lock").setDisabled(cm.isLocked(index));
41383 ms.get("unlock").setDisabled(!cm.isLocked(index));
41385 this.hmenu.show(hd, "tl-bl");
41388 handleHdOver : function(e){
41389 var hd = this.findHeaderCell(e.getTarget());
41390 if(hd && !this.headersDisabled){
41391 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
41392 this.fly(hd).addClass("x-grid-hd-over");
41397 handleHdOut : function(e){
41398 var hd = this.findHeaderCell(e.getTarget());
41400 this.fly(hd).removeClass("x-grid-hd-over");
41404 handleSplitDblClick : function(e, t){
41405 var i = this.getCellIndex(t);
41406 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
41407 this.autoSizeColumn(i, true);
41412 render : function(){
41415 var colCount = cm.getColumnCount();
41417 if(this.grid.monitorWindowResize === true){
41418 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
41420 var header = this.renderHeaders();
41421 var body = this.templates.body.apply({rows:""});
41422 var html = this.templates.master.apply({
41425 lockedHeader: header[0],
41429 //this.updateColumns();
41431 this.grid.getGridEl().dom.innerHTML = html;
41433 this.initElements();
41435 // a kludge to fix the random scolling effect in webkit
41436 this.el.on("scroll", function() {
41437 this.el.dom.scrollTop=0; // hopefully not recursive..
41440 this.scroller.on("scroll", this.handleScroll, this);
41441 this.lockedBody.on("mousewheel", this.handleWheel, this);
41442 this.mainBody.on("mousewheel", this.handleWheel, this);
41444 this.mainHd.on("mouseover", this.handleHdOver, this);
41445 this.mainHd.on("mouseout", this.handleHdOut, this);
41446 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
41447 {delegate: "."+this.splitClass});
41449 this.lockedHd.on("mouseover", this.handleHdOver, this);
41450 this.lockedHd.on("mouseout", this.handleHdOut, this);
41451 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
41452 {delegate: "."+this.splitClass});
41454 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
41455 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
41458 this.updateSplitters();
41460 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
41461 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
41462 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
41465 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
41466 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
41468 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
41469 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
41471 if(this.grid.enableColLock !== false){
41472 this.hmenu.add('-',
41473 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
41474 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
41478 this.hmenu.add('-',
41479 {id:"wider", text: this.columnsWiderText},
41480 {id:"narrow", text: this.columnsNarrowText }
41486 if(this.grid.enableColumnHide !== false){
41488 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
41489 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
41490 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
41492 this.hmenu.add('-',
41493 {id:"columns", text: this.columnsText, menu: this.colMenu}
41496 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
41498 this.grid.on("headercontextmenu", this.handleHdCtx, this);
41501 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
41502 this.dd = new Roo.grid.GridDragZone(this.grid, {
41503 ddGroup : this.grid.ddGroup || 'GridDD'
41509 for(var i = 0; i < colCount; i++){
41510 if(cm.isHidden(i)){
41511 this.hideColumn(i);
41513 if(cm.config[i].align){
41514 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
41515 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
41519 this.updateHeaderSortState();
41521 this.beforeInitialResize();
41524 // two part rendering gives faster view to the user
41525 this.renderPhase2.defer(1, this);
41528 renderPhase2 : function(){
41529 // render the rows now
41531 if(this.grid.autoSizeColumns){
41532 this.autoSizeColumns();
41536 beforeInitialResize : function(){
41540 onColumnSplitterMoved : function(i, w){
41541 this.userResized = true;
41542 var cm = this.grid.colModel;
41543 cm.setColumnWidth(i, w, true);
41544 var cid = cm.getColumnId(i);
41545 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
41546 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
41547 this.updateSplitters();
41549 this.grid.fireEvent("columnresize", i, w);
41552 syncRowHeights : function(startIndex, endIndex){
41553 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
41554 startIndex = startIndex || 0;
41555 var mrows = this.getBodyTable().rows;
41556 var lrows = this.getLockedTable().rows;
41557 var len = mrows.length-1;
41558 endIndex = Math.min(endIndex || len, len);
41559 for(var i = startIndex; i <= endIndex; i++){
41560 var m = mrows[i], l = lrows[i];
41561 var h = Math.max(m.offsetHeight, l.offsetHeight);
41562 m.style.height = l.style.height = h + "px";
41567 layout : function(initialRender, is2ndPass)
41570 var auto = g.autoHeight;
41571 var scrollOffset = 16;
41572 var c = g.getGridEl(), cm = this.cm,
41573 expandCol = g.autoExpandColumn,
41575 //c.beginMeasure();
41577 if(!c.dom.offsetWidth){ // display:none?
41579 this.lockedWrap.show();
41580 this.mainWrap.show();
41585 var hasLock = this.cm.isLocked(0);
41587 var tbh = this.headerPanel.getHeight();
41588 var bbh = this.footerPanel.getHeight();
41591 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
41592 var newHeight = ch + c.getBorderWidth("tb");
41594 newHeight = Math.min(g.maxHeight, newHeight);
41596 c.setHeight(newHeight);
41600 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
41603 var s = this.scroller;
41605 var csize = c.getSize(true);
41607 this.el.setSize(csize.width, csize.height);
41609 this.headerPanel.setWidth(csize.width);
41610 this.footerPanel.setWidth(csize.width);
41612 var hdHeight = this.mainHd.getHeight();
41613 var vw = csize.width;
41614 var vh = csize.height - (tbh + bbh);
41618 var bt = this.getBodyTable();
41620 if(cm.getLockedCount() == cm.config.length){
41621 bt = this.getLockedTable();
41624 var ltWidth = hasLock ?
41625 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
41627 var scrollHeight = bt.offsetHeight;
41628 var scrollWidth = ltWidth + bt.offsetWidth;
41629 var vscroll = false, hscroll = false;
41631 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
41633 var lw = this.lockedWrap, mw = this.mainWrap;
41634 var lb = this.lockedBody, mb = this.mainBody;
41636 setTimeout(function(){
41637 var t = s.dom.offsetTop;
41638 var w = s.dom.clientWidth,
41639 h = s.dom.clientHeight;
41642 lw.setSize(ltWidth, h);
41644 mw.setLeftTop(ltWidth, t);
41645 mw.setSize(w-ltWidth, h);
41647 lb.setHeight(h-hdHeight);
41648 mb.setHeight(h-hdHeight);
41650 if(is2ndPass !== true && !gv.userResized && expandCol){
41651 // high speed resize without full column calculation
41653 var ci = cm.getIndexById(expandCol);
41655 ci = cm.findColumnIndex(expandCol);
41657 ci = Math.max(0, ci); // make sure it's got at least the first col.
41658 var expandId = cm.getColumnId(ci);
41659 var tw = cm.getTotalWidth(false);
41660 var currentWidth = cm.getColumnWidth(ci);
41661 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
41662 if(currentWidth != cw){
41663 cm.setColumnWidth(ci, cw, true);
41664 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
41665 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
41666 gv.updateSplitters();
41667 gv.layout(false, true);
41679 onWindowResize : function(){
41680 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
41686 appendFooter : function(parentEl){
41690 sortAscText : "Sort Ascending",
41691 sortDescText : "Sort Descending",
41692 lockText : "Lock Column",
41693 unlockText : "Unlock Column",
41694 columnsText : "Columns",
41696 columnsWiderText : "Wider",
41697 columnsNarrowText : "Thinner"
41701 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
41702 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
41703 this.proxy.el.addClass('x-grid3-col-dd');
41706 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
41707 handleMouseDown : function(e){
41711 callHandleMouseDown : function(e){
41712 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
41717 * Ext JS Library 1.1.1
41718 * Copyright(c) 2006-2007, Ext JS, LLC.
41720 * Originally Released Under LGPL - original licence link has changed is not relivant.
41723 * <script type="text/javascript">
41726 * @extends Roo.dd.DDProxy
41727 * @class Roo.grid.SplitDragZone
41728 * Support for Column Header resizing
41730 * @param {Object} config
41733 // This is a support class used internally by the Grid components
41734 Roo.grid.SplitDragZone = function(grid, hd, hd2){
41736 this.view = grid.getView();
41737 this.proxy = this.view.resizeProxy;
41738 Roo.grid.SplitDragZone.superclass.constructor.call(
41741 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
41743 dragElId : Roo.id(this.proxy.dom),
41748 this.setHandleElId(Roo.id(hd));
41749 if (hd2 !== false) {
41750 this.setOuterHandleElId(Roo.id(hd2));
41753 this.scroll = false;
41755 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
41756 fly: Roo.Element.fly,
41758 b4StartDrag : function(x, y){
41759 this.view.headersDisabled = true;
41760 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
41761 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
41763 this.proxy.setHeight(h);
41765 // for old system colWidth really stored the actual width?
41766 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
41767 // which in reality did not work.. - it worked only for fixed sizes
41768 // for resizable we need to use actual sizes.
41769 var w = this.cm.getColumnWidth(this.cellIndex);
41770 if (!this.view.mainWrap) {
41772 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
41777 // this was w-this.grid.minColumnWidth;
41778 // doesnt really make sense? - w = thie curren width or the rendered one?
41779 var minw = Math.max(w-this.grid.minColumnWidth, 0);
41780 this.resetConstraints();
41781 this.setXConstraint(minw, 1000);
41782 this.setYConstraint(0, 0);
41783 this.minX = x - minw;
41784 this.maxX = x + 1000;
41786 if (!this.view.mainWrap) { // this is Bootstrap code..
41787 this.getDragEl().style.display='block';
41790 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
41794 handleMouseDown : function(e){
41795 ev = Roo.EventObject.setEvent(e);
41796 var t = this.fly(ev.getTarget());
41797 if(t.hasClass("x-grid-split")){
41798 this.cellIndex = this.view.getCellIndex(t.dom);
41799 this.split = t.dom;
41800 this.cm = this.grid.colModel;
41801 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
41802 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
41807 endDrag : function(e){
41808 this.view.headersDisabled = false;
41809 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
41810 var diff = endX - this.startPos;
41812 var w = this.cm.getColumnWidth(this.cellIndex);
41813 if (!this.view.mainWrap) {
41816 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
41819 autoOffset : function(){
41820 this.setDelta(0,0);
41824 * Ext JS Library 1.1.1
41825 * Copyright(c) 2006-2007, Ext JS, LLC.
41827 * Originally Released Under LGPL - original licence link has changed is not relivant.
41830 * <script type="text/javascript">
41834 // This is a support class used internally by the Grid components
41835 Roo.grid.GridDragZone = function(grid, config){
41836 this.view = grid.getView();
41837 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
41838 if(this.view.lockedBody){
41839 this.setHandleElId(Roo.id(this.view.mainBody.dom));
41840 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
41842 this.scroll = false;
41844 this.ddel = document.createElement('div');
41845 this.ddel.className = 'x-grid-dd-wrap';
41848 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
41849 ddGroup : "GridDD",
41851 getDragData : function(e){
41852 var t = Roo.lib.Event.getTarget(e);
41853 var rowIndex = this.view.findRowIndex(t);
41854 var sm = this.grid.selModel;
41856 //Roo.log(rowIndex);
41858 if (sm.getSelectedCell) {
41859 // cell selection..
41860 if (!sm.getSelectedCell()) {
41863 if (rowIndex != sm.getSelectedCell()[0]) {
41868 if (sm.getSelections && sm.getSelections().length < 1) {
41873 // before it used to all dragging of unseleted... - now we dont do that.
41874 if(rowIndex !== false){
41879 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
41881 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
41884 if (e.hasModifier()){
41885 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
41888 Roo.log("getDragData");
41893 rowIndex: rowIndex,
41894 selections: sm.getSelections ? sm.getSelections() : (
41895 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
41902 onInitDrag : function(e){
41903 var data = this.dragData;
41904 this.ddel.innerHTML = this.grid.getDragDropText();
41905 this.proxy.update(this.ddel);
41906 // fire start drag?
41909 afterRepair : function(){
41910 this.dragging = false;
41913 getRepairXY : function(e, data){
41917 onEndDrag : function(data, e){
41921 onValidDrop : function(dd, e, id){
41926 beforeInvalidDrop : function(e, id){
41931 * Ext JS Library 1.1.1
41932 * Copyright(c) 2006-2007, Ext JS, LLC.
41934 * Originally Released Under LGPL - original licence link has changed is not relivant.
41937 * <script type="text/javascript">
41942 * @class Roo.grid.ColumnModel
41943 * @extends Roo.util.Observable
41944 * This is the default implementation of a ColumnModel used by the Grid. It defines
41945 * the columns in the grid.
41948 var colModel = new Roo.grid.ColumnModel([
41949 {header: "Ticker", width: 60, sortable: true, locked: true},
41950 {header: "Company Name", width: 150, sortable: true},
41951 {header: "Market Cap.", width: 100, sortable: true},
41952 {header: "$ Sales", width: 100, sortable: true, renderer: money},
41953 {header: "Employees", width: 100, sortable: true, resizable: false}
41958 * The config options listed for this class are options which may appear in each
41959 * individual column definition.
41960 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
41962 * @param {Object} config An Array of column config objects. See this class's
41963 * config objects for details.
41965 Roo.grid.ColumnModel = function(config){
41967 * The config passed into the constructor
41969 this.config = []; //config;
41972 // if no id, create one
41973 // if the column does not have a dataIndex mapping,
41974 // map it to the order it is in the config
41975 for(var i = 0, len = config.length; i < len; i++){
41976 this.addColumn(config[i]);
41981 * The width of columns which have no width specified (defaults to 100)
41984 this.defaultWidth = 100;
41987 * Default sortable of columns which have no sortable specified (defaults to false)
41990 this.defaultSortable = false;
41994 * @event widthchange
41995 * Fires when the width of a column changes.
41996 * @param {ColumnModel} this
41997 * @param {Number} columnIndex The column index
41998 * @param {Number} newWidth The new width
42000 "widthchange": true,
42002 * @event headerchange
42003 * Fires when the text of a header changes.
42004 * @param {ColumnModel} this
42005 * @param {Number} columnIndex The column index
42006 * @param {Number} newText The new header text
42008 "headerchange": true,
42010 * @event hiddenchange
42011 * Fires when a column is hidden or "unhidden".
42012 * @param {ColumnModel} this
42013 * @param {Number} columnIndex The column index
42014 * @param {Boolean} hidden true if hidden, false otherwise
42016 "hiddenchange": true,
42018 * @event columnmoved
42019 * Fires when a column is moved.
42020 * @param {ColumnModel} this
42021 * @param {Number} oldIndex
42022 * @param {Number} newIndex
42024 "columnmoved" : true,
42026 * @event columlockchange
42027 * Fires when a column's locked state is changed
42028 * @param {ColumnModel} this
42029 * @param {Number} colIndex
42030 * @param {Boolean} locked true if locked
42032 "columnlockchange" : true
42034 Roo.grid.ColumnModel.superclass.constructor.call(this);
42036 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
42038 * @cfg {String} header [required] The header text to display in the Grid view.
42041 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
42044 * @cfg {String} smHeader Header at Bootsrap Small width
42047 * @cfg {String} mdHeader Header at Bootsrap Medium width
42050 * @cfg {String} lgHeader Header at Bootsrap Large width
42053 * @cfg {String} xlHeader Header at Bootsrap extra Large width
42056 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
42057 * {@link Roo.data.Record} definition from which to draw the column's value. If not
42058 * specified, the column's index is used as an index into the Record's data Array.
42061 * @cfg {Number} width The initial width in pixels of the column. Using this
42062 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
42065 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
42066 * Defaults to the value of the {@link #defaultSortable} property.
42067 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
42070 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
42073 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
42076 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
42079 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
42082 * @cfg {Function} renderer A function used to generate HTML markup for a cell
42083 * given the cell's data value. See {@link #setRenderer}. If not specified, the
42084 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
42085 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
42088 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
42091 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
42094 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
42097 * @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)
42100 * @cfg {String} tooltip mouse over tooltip text
42103 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
42106 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
42109 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
42112 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
42115 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
42118 * Returns the id of the column at the specified index.
42119 * @param {Number} index The column index
42120 * @return {String} the id
42122 getColumnId : function(index){
42123 return this.config[index].id;
42127 * Returns the column for a specified id.
42128 * @param {String} id The column id
42129 * @return {Object} the column
42131 getColumnById : function(id){
42132 return this.lookup[id];
42137 * Returns the column Object for a specified dataIndex.
42138 * @param {String} dataIndex The column dataIndex
42139 * @return {Object|Boolean} the column or false if not found
42141 getColumnByDataIndex: function(dataIndex){
42142 var index = this.findColumnIndex(dataIndex);
42143 return index > -1 ? this.config[index] : false;
42147 * Returns the index for a specified column id.
42148 * @param {String} id The column id
42149 * @return {Number} the index, or -1 if not found
42151 getIndexById : function(id){
42152 for(var i = 0, len = this.config.length; i < len; i++){
42153 if(this.config[i].id == id){
42161 * Returns the index for a specified column dataIndex.
42162 * @param {String} dataIndex The column dataIndex
42163 * @return {Number} the index, or -1 if not found
42166 findColumnIndex : function(dataIndex){
42167 for(var i = 0, len = this.config.length; i < len; i++){
42168 if(this.config[i].dataIndex == dataIndex){
42176 moveColumn : function(oldIndex, newIndex){
42177 var c = this.config[oldIndex];
42178 this.config.splice(oldIndex, 1);
42179 this.config.splice(newIndex, 0, c);
42180 this.dataMap = null;
42181 this.fireEvent("columnmoved", this, oldIndex, newIndex);
42184 isLocked : function(colIndex){
42185 return this.config[colIndex].locked === true;
42188 setLocked : function(colIndex, value, suppressEvent){
42189 if(this.isLocked(colIndex) == value){
42192 this.config[colIndex].locked = value;
42193 if(!suppressEvent){
42194 this.fireEvent("columnlockchange", this, colIndex, value);
42198 getTotalLockedWidth : function(){
42199 var totalWidth = 0;
42200 for(var i = 0; i < this.config.length; i++){
42201 if(this.isLocked(i) && !this.isHidden(i)){
42202 this.totalWidth += this.getColumnWidth(i);
42208 getLockedCount : function(){
42209 for(var i = 0, len = this.config.length; i < len; i++){
42210 if(!this.isLocked(i)){
42215 return this.config.length;
42219 * Returns the number of columns.
42222 getColumnCount : function(visibleOnly){
42223 if(visibleOnly === true){
42225 for(var i = 0, len = this.config.length; i < len; i++){
42226 if(!this.isHidden(i)){
42232 return this.config.length;
42236 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
42237 * @param {Function} fn
42238 * @param {Object} scope (optional)
42239 * @return {Array} result
42241 getColumnsBy : function(fn, scope){
42243 for(var i = 0, len = this.config.length; i < len; i++){
42244 var c = this.config[i];
42245 if(fn.call(scope||this, c, i) === true){
42253 * Returns true if the specified column is sortable.
42254 * @param {Number} col The column index
42255 * @return {Boolean}
42257 isSortable : function(col){
42258 if(typeof this.config[col].sortable == "undefined"){
42259 return this.defaultSortable;
42261 return this.config[col].sortable;
42265 * Returns the rendering (formatting) function defined for the column.
42266 * @param {Number} col The column index.
42267 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
42269 getRenderer : function(col){
42270 if(!this.config[col].renderer){
42271 return Roo.grid.ColumnModel.defaultRenderer;
42273 return this.config[col].renderer;
42277 * Sets the rendering (formatting) function for a column.
42278 * @param {Number} col The column index
42279 * @param {Function} fn The function to use to process the cell's raw data
42280 * to return HTML markup for the grid view. The render function is called with
42281 * the following parameters:<ul>
42282 * <li>Data value.</li>
42283 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
42284 * <li>css A CSS style string to apply to the table cell.</li>
42285 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
42286 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
42287 * <li>Row index</li>
42288 * <li>Column index</li>
42289 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
42291 setRenderer : function(col, fn){
42292 this.config[col].renderer = fn;
42296 * Returns the width for the specified column.
42297 * @param {Number} col The column index
42298 * @param (optional) {String} gridSize bootstrap width size.
42301 getColumnWidth : function(col, gridSize)
42303 var cfg = this.config[col];
42305 if (typeof(gridSize) == 'undefined') {
42306 return cfg.width * 1 || this.defaultWidth;
42308 if (gridSize === false) { // if we set it..
42309 return cfg.width || false;
42311 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
42313 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
42314 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
42317 return cfg[ sizes[i] ];
42324 * Sets the width for a column.
42325 * @param {Number} col The column index
42326 * @param {Number} width The new width
42328 setColumnWidth : function(col, width, suppressEvent){
42329 this.config[col].width = width;
42330 this.totalWidth = null;
42331 if(!suppressEvent){
42332 this.fireEvent("widthchange", this, col, width);
42337 * Returns the total width of all columns.
42338 * @param {Boolean} includeHidden True to include hidden column widths
42341 getTotalWidth : function(includeHidden){
42342 if(!this.totalWidth){
42343 this.totalWidth = 0;
42344 for(var i = 0, len = this.config.length; i < len; i++){
42345 if(includeHidden || !this.isHidden(i)){
42346 this.totalWidth += this.getColumnWidth(i);
42350 return this.totalWidth;
42354 * Returns the header for the specified column.
42355 * @param {Number} col The column index
42358 getColumnHeader : function(col){
42359 return this.config[col].header;
42363 * Sets the header for a column.
42364 * @param {Number} col The column index
42365 * @param {String} header The new header
42367 setColumnHeader : function(col, header){
42368 this.config[col].header = header;
42369 this.fireEvent("headerchange", this, col, header);
42373 * Returns the tooltip for the specified column.
42374 * @param {Number} col The column index
42377 getColumnTooltip : function(col){
42378 return this.config[col].tooltip;
42381 * Sets the tooltip for a column.
42382 * @param {Number} col The column index
42383 * @param {String} tooltip The new tooltip
42385 setColumnTooltip : function(col, tooltip){
42386 this.config[col].tooltip = tooltip;
42390 * Returns the dataIndex for the specified column.
42391 * @param {Number} col The column index
42394 getDataIndex : function(col){
42395 return this.config[col].dataIndex;
42399 * Sets the dataIndex for a column.
42400 * @param {Number} col The column index
42401 * @param {Number} dataIndex The new dataIndex
42403 setDataIndex : function(col, dataIndex){
42404 this.config[col].dataIndex = dataIndex;
42410 * Returns true if the cell is editable.
42411 * @param {Number} colIndex The column index
42412 * @param {Number} rowIndex The row index - this is nto actually used..?
42413 * @return {Boolean}
42415 isCellEditable : function(colIndex, rowIndex){
42416 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
42420 * Returns the editor defined for the cell/column.
42421 * return false or null to disable editing.
42422 * @param {Number} colIndex The column index
42423 * @param {Number} rowIndex The row index
42426 getCellEditor : function(colIndex, rowIndex){
42427 return this.config[colIndex].editor;
42431 * Sets if a column is editable.
42432 * @param {Number} col The column index
42433 * @param {Boolean} editable True if the column is editable
42435 setEditable : function(col, editable){
42436 this.config[col].editable = editable;
42441 * Returns true if the column is hidden.
42442 * @param {Number} colIndex The column index
42443 * @return {Boolean}
42445 isHidden : function(colIndex){
42446 return this.config[colIndex].hidden;
42451 * Returns true if the column width cannot be changed
42453 isFixed : function(colIndex){
42454 return this.config[colIndex].fixed;
42458 * Returns true if the column can be resized
42459 * @return {Boolean}
42461 isResizable : function(colIndex){
42462 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
42465 * Sets if a column is hidden.
42466 * @param {Number} colIndex The column index
42467 * @param {Boolean} hidden True if the column is hidden
42469 setHidden : function(colIndex, hidden){
42470 this.config[colIndex].hidden = hidden;
42471 this.totalWidth = null;
42472 this.fireEvent("hiddenchange", this, colIndex, hidden);
42476 * Sets the editor for a column.
42477 * @param {Number} col The column index
42478 * @param {Object} editor The editor object
42480 setEditor : function(col, editor){
42481 this.config[col].editor = editor;
42484 * Add a column (experimental...) - defaults to adding to the end..
42485 * @param {Object} config
42487 addColumn : function(c)
42490 var i = this.config.length;
42491 this.config[i] = c;
42493 if(typeof c.dataIndex == "undefined"){
42496 if(typeof c.renderer == "string"){
42497 c.renderer = Roo.util.Format[c.renderer];
42499 if(typeof c.id == "undefined"){
42502 if(c.editor && c.editor.xtype){
42503 c.editor = Roo.factory(c.editor, Roo.grid);
42505 if(c.editor && c.editor.isFormField){
42506 c.editor = new Roo.grid.GridEditor(c.editor);
42508 this.lookup[c.id] = c;
42513 Roo.grid.ColumnModel.defaultRenderer = function(value)
42515 if(typeof value == "object") {
42518 if(typeof value == "string" && value.length < 1){
42522 return String.format("{0}", value);
42525 // Alias for backwards compatibility
42526 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
42529 * Ext JS Library 1.1.1
42530 * Copyright(c) 2006-2007, Ext JS, LLC.
42532 * Originally Released Under LGPL - original licence link has changed is not relivant.
42535 * <script type="text/javascript">
42539 * @class Roo.grid.AbstractSelectionModel
42540 * @extends Roo.util.Observable
42542 * Abstract base class for grid SelectionModels. It provides the interface that should be
42543 * implemented by descendant classes. This class should not be directly instantiated.
42546 Roo.grid.AbstractSelectionModel = function(){
42547 this.locked = false;
42548 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
42551 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
42552 /** @ignore Called by the grid automatically. Do not call directly. */
42553 init : function(grid){
42559 * Locks the selections.
42562 this.locked = true;
42566 * Unlocks the selections.
42568 unlock : function(){
42569 this.locked = false;
42573 * Returns true if the selections are locked.
42574 * @return {Boolean}
42576 isLocked : function(){
42577 return this.locked;
42581 * Ext JS Library 1.1.1
42582 * Copyright(c) 2006-2007, Ext JS, LLC.
42584 * Originally Released Under LGPL - original licence link has changed is not relivant.
42587 * <script type="text/javascript">
42590 * @extends Roo.grid.AbstractSelectionModel
42591 * @class Roo.grid.RowSelectionModel
42592 * The default SelectionModel used by {@link Roo.grid.Grid}.
42593 * It supports multiple selections and keyboard selection/navigation.
42595 * @param {Object} config
42597 Roo.grid.RowSelectionModel = function(config){
42598 Roo.apply(this, config);
42599 this.selections = new Roo.util.MixedCollection(false, function(o){
42604 this.lastActive = false;
42608 * @event selectionchange
42609 * Fires when the selection changes
42610 * @param {SelectionModel} this
42612 "selectionchange" : true,
42614 * @event afterselectionchange
42615 * Fires after the selection changes (eg. by key press or clicking)
42616 * @param {SelectionModel} this
42618 "afterselectionchange" : true,
42620 * @event beforerowselect
42621 * Fires when a row is selected being selected, return false to cancel.
42622 * @param {SelectionModel} this
42623 * @param {Number} rowIndex The selected index
42624 * @param {Boolean} keepExisting False if other selections will be cleared
42626 "beforerowselect" : true,
42629 * Fires when a row is selected.
42630 * @param {SelectionModel} this
42631 * @param {Number} rowIndex The selected index
42632 * @param {Roo.data.Record} r The record
42634 "rowselect" : true,
42636 * @event rowdeselect
42637 * Fires when a row is deselected.
42638 * @param {SelectionModel} this
42639 * @param {Number} rowIndex The selected index
42641 "rowdeselect" : true
42643 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
42644 this.locked = false;
42647 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
42649 * @cfg {Boolean} singleSelect
42650 * True to allow selection of only one row at a time (defaults to false)
42652 singleSelect : false,
42655 initEvents : function(){
42657 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
42658 this.grid.on("mousedown", this.handleMouseDown, this);
42659 }else{ // allow click to work like normal
42660 this.grid.on("rowclick", this.handleDragableRowClick, this);
42662 // bootstrap does not have a view..
42663 var view = this.grid.view ? this.grid.view : this.grid;
42664 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
42665 "up" : function(e){
42667 this.selectPrevious(e.shiftKey);
42668 }else if(this.last !== false && this.lastActive !== false){
42669 var last = this.last;
42670 this.selectRange(this.last, this.lastActive-1);
42671 view.focusRow(this.lastActive);
42672 if(last !== false){
42676 this.selectFirstRow();
42678 this.fireEvent("afterselectionchange", this);
42680 "down" : function(e){
42682 this.selectNext(e.shiftKey);
42683 }else if(this.last !== false && this.lastActive !== false){
42684 var last = this.last;
42685 this.selectRange(this.last, this.lastActive+1);
42686 view.focusRow(this.lastActive);
42687 if(last !== false){
42691 this.selectFirstRow();
42693 this.fireEvent("afterselectionchange", this);
42699 view.on("refresh", this.onRefresh, this);
42700 view.on("rowupdated", this.onRowUpdated, this);
42701 view.on("rowremoved", this.onRemove, this);
42705 onRefresh : function(){
42706 var ds = this.grid.ds, i, v = this.grid.view;
42707 var s = this.selections;
42708 s.each(function(r){
42709 if((i = ds.indexOfId(r.id)) != -1){
42711 s.add(ds.getAt(i)); // updating the selection relate data
42719 onRemove : function(v, index, r){
42720 this.selections.remove(r);
42724 onRowUpdated : function(v, index, r){
42725 if(this.isSelected(r)){
42726 v.onRowSelect(index);
42732 * @param {Array} records The records to select
42733 * @param {Boolean} keepExisting (optional) True to keep existing selections
42735 selectRecords : function(records, keepExisting){
42737 this.clearSelections();
42739 var ds = this.grid.ds;
42740 for(var i = 0, len = records.length; i < len; i++){
42741 this.selectRow(ds.indexOf(records[i]), true);
42746 * Gets the number of selected rows.
42749 getCount : function(){
42750 return this.selections.length;
42754 * Selects the first row in the grid.
42756 selectFirstRow : function(){
42761 * Select the last row.
42762 * @param {Boolean} keepExisting (optional) True to keep existing selections
42764 selectLastRow : function(keepExisting){
42765 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
42769 * Selects the row immediately following the last selected row.
42770 * @param {Boolean} keepExisting (optional) True to keep existing selections
42772 selectNext : function(keepExisting){
42773 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
42774 this.selectRow(this.last+1, keepExisting);
42775 var view = this.grid.view ? this.grid.view : this.grid;
42776 view.focusRow(this.last);
42781 * Selects the row that precedes the last selected row.
42782 * @param {Boolean} keepExisting (optional) True to keep existing selections
42784 selectPrevious : function(keepExisting){
42786 this.selectRow(this.last-1, keepExisting);
42787 var view = this.grid.view ? this.grid.view : this.grid;
42788 view.focusRow(this.last);
42793 * Returns the selected records
42794 * @return {Array} Array of selected records
42796 getSelections : function(){
42797 return [].concat(this.selections.items);
42801 * Returns the first selected record.
42804 getSelected : function(){
42805 return this.selections.itemAt(0);
42810 * Clears all selections.
42812 clearSelections : function(fast){
42817 var ds = this.grid.ds;
42818 var s = this.selections;
42819 s.each(function(r){
42820 this.deselectRow(ds.indexOfId(r.id));
42824 this.selections.clear();
42831 * Selects all rows.
42833 selectAll : function(){
42837 this.selections.clear();
42838 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
42839 this.selectRow(i, true);
42844 * Returns True if there is a selection.
42845 * @return {Boolean}
42847 hasSelection : function(){
42848 return this.selections.length > 0;
42852 * Returns True if the specified row is selected.
42853 * @param {Number/Record} record The record or index of the record to check
42854 * @return {Boolean}
42856 isSelected : function(index){
42857 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
42858 return (r && this.selections.key(r.id) ? true : false);
42862 * Returns True if the specified record id is selected.
42863 * @param {String} id The id of record to check
42864 * @return {Boolean}
42866 isIdSelected : function(id){
42867 return (this.selections.key(id) ? true : false);
42871 handleMouseDown : function(e, t)
42873 var view = this.grid.view ? this.grid.view : this.grid;
42875 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
42878 if(e.shiftKey && this.last !== false){
42879 var last = this.last;
42880 this.selectRange(last, rowIndex, e.ctrlKey);
42881 this.last = last; // reset the last
42882 view.focusRow(rowIndex);
42884 var isSelected = this.isSelected(rowIndex);
42885 if(e.button !== 0 && isSelected){
42886 view.focusRow(rowIndex);
42887 }else if(e.ctrlKey && isSelected){
42888 this.deselectRow(rowIndex);
42889 }else if(!isSelected){
42890 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
42891 view.focusRow(rowIndex);
42894 this.fireEvent("afterselectionchange", this);
42897 handleDragableRowClick : function(grid, rowIndex, e)
42899 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
42900 this.selectRow(rowIndex, false);
42901 var view = this.grid.view ? this.grid.view : this.grid;
42902 view.focusRow(rowIndex);
42903 this.fireEvent("afterselectionchange", this);
42908 * Selects multiple rows.
42909 * @param {Array} rows Array of the indexes of the row to select
42910 * @param {Boolean} keepExisting (optional) True to keep existing selections
42912 selectRows : function(rows, keepExisting){
42914 this.clearSelections();
42916 for(var i = 0, len = rows.length; i < len; i++){
42917 this.selectRow(rows[i], true);
42922 * Selects a range of rows. All rows in between startRow and endRow are also selected.
42923 * @param {Number} startRow The index of the first row in the range
42924 * @param {Number} endRow The index of the last row in the range
42925 * @param {Boolean} keepExisting (optional) True to retain existing selections
42927 selectRange : function(startRow, endRow, keepExisting){
42932 this.clearSelections();
42934 if(startRow <= endRow){
42935 for(var i = startRow; i <= endRow; i++){
42936 this.selectRow(i, true);
42939 for(var i = startRow; i >= endRow; i--){
42940 this.selectRow(i, true);
42946 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
42947 * @param {Number} startRow The index of the first row in the range
42948 * @param {Number} endRow The index of the last row in the range
42950 deselectRange : function(startRow, endRow, preventViewNotify){
42954 for(var i = startRow; i <= endRow; i++){
42955 this.deselectRow(i, preventViewNotify);
42961 * @param {Number} row The index of the row to select
42962 * @param {Boolean} keepExisting (optional) True to keep existing selections
42964 selectRow : function(index, keepExisting, preventViewNotify){
42965 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
42968 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
42969 if(!keepExisting || this.singleSelect){
42970 this.clearSelections();
42972 var r = this.grid.ds.getAt(index);
42973 this.selections.add(r);
42974 this.last = this.lastActive = index;
42975 if(!preventViewNotify){
42976 var view = this.grid.view ? this.grid.view : this.grid;
42977 view.onRowSelect(index);
42979 this.fireEvent("rowselect", this, index, r);
42980 this.fireEvent("selectionchange", this);
42986 * @param {Number} row The index of the row to deselect
42988 deselectRow : function(index, preventViewNotify){
42992 if(this.last == index){
42995 if(this.lastActive == index){
42996 this.lastActive = false;
42998 var r = this.grid.ds.getAt(index);
42999 this.selections.remove(r);
43000 if(!preventViewNotify){
43001 var view = this.grid.view ? this.grid.view : this.grid;
43002 view.onRowDeselect(index);
43004 this.fireEvent("rowdeselect", this, index);
43005 this.fireEvent("selectionchange", this);
43009 restoreLast : function(){
43011 this.last = this._last;
43016 acceptsNav : function(row, col, cm){
43017 return !cm.isHidden(col) && cm.isCellEditable(col, row);
43021 onEditorKey : function(field, e){
43022 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
43027 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
43029 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
43031 }else if(k == e.ENTER && !e.ctrlKey){
43035 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
43037 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
43039 }else if(k == e.ESC){
43043 g.startEditing(newCell[0], newCell[1]);
43048 * Ext JS Library 1.1.1
43049 * Copyright(c) 2006-2007, Ext JS, LLC.
43051 * Originally Released Under LGPL - original licence link has changed is not relivant.
43054 * <script type="text/javascript">
43057 * @class Roo.grid.CellSelectionModel
43058 * @extends Roo.grid.AbstractSelectionModel
43059 * This class provides the basic implementation for cell selection in a grid.
43061 * @param {Object} config The object containing the configuration of this model.
43062 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
43064 Roo.grid.CellSelectionModel = function(config){
43065 Roo.apply(this, config);
43067 this.selection = null;
43071 * @event beforerowselect
43072 * Fires before a cell is selected.
43073 * @param {SelectionModel} this
43074 * @param {Number} rowIndex The selected row index
43075 * @param {Number} colIndex The selected cell index
43077 "beforecellselect" : true,
43079 * @event cellselect
43080 * Fires when a cell is selected.
43081 * @param {SelectionModel} this
43082 * @param {Number} rowIndex The selected row index
43083 * @param {Number} colIndex The selected cell index
43085 "cellselect" : true,
43087 * @event selectionchange
43088 * Fires when the active selection changes.
43089 * @param {SelectionModel} this
43090 * @param {Object} selection null for no selection or an object (o) with two properties
43092 <li>o.record: the record object for the row the selection is in</li>
43093 <li>o.cell: An array of [rowIndex, columnIndex]</li>
43096 "selectionchange" : true,
43099 * Fires when the tab (or enter) was pressed on the last editable cell
43100 * You can use this to trigger add new row.
43101 * @param {SelectionModel} this
43105 * @event beforeeditnext
43106 * Fires before the next editable sell is made active
43107 * You can use this to skip to another cell or fire the tabend
43108 * if you set cell to false
43109 * @param {Object} eventdata object : { cell : [ row, col ] }
43111 "beforeeditnext" : true
43113 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
43116 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
43118 enter_is_tab: false,
43121 initEvents : function(){
43122 this.grid.on("mousedown", this.handleMouseDown, this);
43123 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
43124 var view = this.grid.view;
43125 view.on("refresh", this.onViewChange, this);
43126 view.on("rowupdated", this.onRowUpdated, this);
43127 view.on("beforerowremoved", this.clearSelections, this);
43128 view.on("beforerowsinserted", this.clearSelections, this);
43129 if(this.grid.isEditor){
43130 this.grid.on("beforeedit", this.beforeEdit, this);
43135 beforeEdit : function(e){
43136 this.select(e.row, e.column, false, true, e.record);
43140 onRowUpdated : function(v, index, r){
43141 if(this.selection && this.selection.record == r){
43142 v.onCellSelect(index, this.selection.cell[1]);
43147 onViewChange : function(){
43148 this.clearSelections(true);
43152 * Returns the currently selected cell,.
43153 * @return {Array} The selected cell (row, column) or null if none selected.
43155 getSelectedCell : function(){
43156 return this.selection ? this.selection.cell : null;
43160 * Clears all selections.
43161 * @param {Boolean} true to prevent the gridview from being notified about the change.
43163 clearSelections : function(preventNotify){
43164 var s = this.selection;
43166 if(preventNotify !== true){
43167 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
43169 this.selection = null;
43170 this.fireEvent("selectionchange", this, null);
43175 * Returns true if there is a selection.
43176 * @return {Boolean}
43178 hasSelection : function(){
43179 return this.selection ? true : false;
43183 handleMouseDown : function(e, t){
43184 var v = this.grid.getView();
43185 if(this.isLocked()){
43188 var row = v.findRowIndex(t);
43189 var cell = v.findCellIndex(t);
43190 if(row !== false && cell !== false){
43191 this.select(row, cell);
43197 * @param {Number} rowIndex
43198 * @param {Number} collIndex
43200 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
43201 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
43202 this.clearSelections();
43203 r = r || this.grid.dataSource.getAt(rowIndex);
43206 cell : [rowIndex, colIndex]
43208 if(!preventViewNotify){
43209 var v = this.grid.getView();
43210 v.onCellSelect(rowIndex, colIndex);
43211 if(preventFocus !== true){
43212 v.focusCell(rowIndex, colIndex);
43215 this.fireEvent("cellselect", this, rowIndex, colIndex);
43216 this.fireEvent("selectionchange", this, this.selection);
43221 isSelectable : function(rowIndex, colIndex, cm){
43222 return !cm.isHidden(colIndex);
43226 handleKeyDown : function(e){
43227 //Roo.log('Cell Sel Model handleKeyDown');
43228 if(!e.isNavKeyPress()){
43231 var g = this.grid, s = this.selection;
43234 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
43236 this.select(cell[0], cell[1]);
43241 var walk = function(row, col, step){
43242 return g.walkCells(row, col, step, sm.isSelectable, sm);
43244 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
43251 // handled by onEditorKey
43252 if (g.isEditor && g.editing) {
43256 newCell = walk(r, c-1, -1);
43258 newCell = walk(r, c+1, 1);
43263 newCell = walk(r+1, c, 1);
43267 newCell = walk(r-1, c, -1);
43271 newCell = walk(r, c+1, 1);
43275 newCell = walk(r, c-1, -1);
43280 if(g.isEditor && !g.editing){
43281 g.startEditing(r, c);
43290 this.select(newCell[0], newCell[1]);
43296 acceptsNav : function(row, col, cm){
43297 return !cm.isHidden(col) && cm.isCellEditable(col, row);
43301 * @param {Number} field (not used) - as it's normally used as a listener
43302 * @param {Number} e - event - fake it by using
43304 * var e = Roo.EventObjectImpl.prototype;
43305 * e.keyCode = e.TAB
43309 onEditorKey : function(field, e){
43311 var k = e.getKey(),
43314 ed = g.activeEditor,
43316 ///Roo.log('onEditorKey' + k);
43319 if (this.enter_is_tab && k == e.ENTER) {
43325 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
43327 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
43333 } else if(k == e.ENTER && !e.ctrlKey){
43336 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
43338 } else if(k == e.ESC){
43343 var ecall = { cell : newCell, forward : forward };
43344 this.fireEvent('beforeeditnext', ecall );
43345 newCell = ecall.cell;
43346 forward = ecall.forward;
43350 //Roo.log('next cell after edit');
43351 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
43352 } else if (forward) {
43353 // tabbed past last
43354 this.fireEvent.defer(100, this, ['tabend',this]);
43359 * Ext JS Library 1.1.1
43360 * Copyright(c) 2006-2007, Ext JS, LLC.
43362 * Originally Released Under LGPL - original licence link has changed is not relivant.
43365 * <script type="text/javascript">
43369 * @class Roo.grid.EditorGrid
43370 * @extends Roo.grid.Grid
43371 * Class for creating and editable grid.
43372 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
43373 * The container MUST have some type of size defined for the grid to fill. The container will be
43374 * automatically set to position relative if it isn't already.
43375 * @param {Object} dataSource The data model to bind to
43376 * @param {Object} colModel The column model with info about this grid's columns
43378 Roo.grid.EditorGrid = function(container, config){
43379 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
43380 this.getGridEl().addClass("xedit-grid");
43382 if(!this.selModel){
43383 this.selModel = new Roo.grid.CellSelectionModel();
43386 this.activeEditor = null;
43390 * @event beforeedit
43391 * Fires before cell editing is triggered. The edit event object has the following properties <br />
43392 * <ul style="padding:5px;padding-left:16px;">
43393 * <li>grid - This grid</li>
43394 * <li>record - The record being edited</li>
43395 * <li>field - The field name being edited</li>
43396 * <li>value - The value for the field being edited.</li>
43397 * <li>row - The grid row index</li>
43398 * <li>column - The grid column index</li>
43399 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
43401 * @param {Object} e An edit event (see above for description)
43403 "beforeedit" : true,
43406 * Fires after a cell is edited. <br />
43407 * <ul style="padding:5px;padding-left:16px;">
43408 * <li>grid - This grid</li>
43409 * <li>record - The record being edited</li>
43410 * <li>field - The field name being edited</li>
43411 * <li>value - The value being set</li>
43412 * <li>originalValue - The original value for the field, before the edit.</li>
43413 * <li>row - The grid row index</li>
43414 * <li>column - The grid column index</li>
43416 * @param {Object} e An edit event (see above for description)
43418 "afteredit" : true,
43420 * @event validateedit
43421 * Fires after a cell is edited, but before the value is set in the record.
43422 * You can use this to modify the value being set in the field, Return false
43423 * to cancel the change. The edit event object has the following properties <br />
43424 * <ul style="padding:5px;padding-left:16px;">
43425 * <li>editor - This editor</li>
43426 * <li>grid - This grid</li>
43427 * <li>record - The record being edited</li>
43428 * <li>field - The field name being edited</li>
43429 * <li>value - The value being set</li>
43430 * <li>originalValue - The original value for the field, before the edit.</li>
43431 * <li>row - The grid row index</li>
43432 * <li>column - The grid column index</li>
43433 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
43435 * @param {Object} e An edit event (see above for description)
43437 "validateedit" : true
43439 this.on("bodyscroll", this.stopEditing, this);
43440 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
43443 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
43445 * @cfg {Number} clicksToEdit
43446 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
43453 trackMouseOver: false, // causes very odd FF errors
43455 onCellDblClick : function(g, row, col){
43456 this.startEditing(row, col);
43459 onEditComplete : function(ed, value, startValue){
43460 this.editing = false;
43461 this.activeEditor = null;
43462 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
43464 var field = this.colModel.getDataIndex(ed.col);
43469 originalValue: startValue,
43476 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
43479 if(String(value) !== String(startValue)){
43481 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
43482 r.set(field, e.value);
43483 // if we are dealing with a combo box..
43484 // then we also set the 'name' colum to be the displayField
43485 if (ed.field.displayField && ed.field.name) {
43486 r.set(ed.field.name, ed.field.el.dom.value);
43489 delete e.cancel; //?? why!!!
43490 this.fireEvent("afteredit", e);
43493 this.fireEvent("afteredit", e); // always fire it!
43495 this.view.focusCell(ed.row, ed.col);
43499 * Starts editing the specified for the specified row/column
43500 * @param {Number} rowIndex
43501 * @param {Number} colIndex
43503 startEditing : function(row, col){
43504 this.stopEditing();
43505 if(this.colModel.isCellEditable(col, row)){
43506 this.view.ensureVisible(row, col, true);
43508 var r = this.dataSource.getAt(row);
43509 var field = this.colModel.getDataIndex(col);
43510 var cell = Roo.get(this.view.getCell(row,col));
43515 value: r.data[field],
43520 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
43521 this.editing = true;
43522 var ed = this.colModel.getCellEditor(col, row);
43528 ed.render(ed.parentEl || document.body);
43534 (function(){ // complex but required for focus issues in safari, ie and opera
43538 ed.on("complete", this.onEditComplete, this, {single: true});
43539 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
43540 this.activeEditor = ed;
43541 var v = r.data[field];
43542 ed.startEdit(this.view.getCell(row, col), v);
43543 // combo's with 'displayField and name set
43544 if (ed.field.displayField && ed.field.name) {
43545 ed.field.el.dom.value = r.data[ed.field.name];
43549 }).defer(50, this);
43555 * Stops any active editing
43557 stopEditing : function(){
43558 if(this.activeEditor){
43559 this.activeEditor.completeEdit();
43561 this.activeEditor = null;
43565 * Called to get grid's drag proxy text, by default returns this.ddText.
43568 getDragDropText : function(){
43569 var count = this.selModel.getSelectedCell() ? 1 : 0;
43570 return String.format(this.ddText, count, count == 1 ? '' : 's');
43575 * Ext JS Library 1.1.1
43576 * Copyright(c) 2006-2007, Ext JS, LLC.
43578 * Originally Released Under LGPL - original licence link has changed is not relivant.
43581 * <script type="text/javascript">
43584 // private - not really -- you end up using it !
43585 // This is a support class used internally by the Grid components
43588 * @class Roo.grid.GridEditor
43589 * @extends Roo.Editor
43590 * Class for creating and editable grid elements.
43591 * @param {Object} config any settings (must include field)
43593 Roo.grid.GridEditor = function(field, config){
43594 if (!config && field.field) {
43596 field = Roo.factory(config.field, Roo.form);
43598 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
43599 field.monitorTab = false;
43602 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
43605 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
43608 alignment: "tl-tl",
43611 cls: "x-small-editor x-grid-editor",
43616 * Ext JS Library 1.1.1
43617 * Copyright(c) 2006-2007, Ext JS, LLC.
43619 * Originally Released Under LGPL - original licence link has changed is not relivant.
43622 * <script type="text/javascript">
43627 Roo.grid.PropertyRecord = Roo.data.Record.create([
43628 {name:'name',type:'string'}, 'value'
43632 Roo.grid.PropertyStore = function(grid, source){
43634 this.store = new Roo.data.Store({
43635 recordType : Roo.grid.PropertyRecord
43637 this.store.on('update', this.onUpdate, this);
43639 this.setSource(source);
43641 Roo.grid.PropertyStore.superclass.constructor.call(this);
43646 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
43647 setSource : function(o){
43649 this.store.removeAll();
43652 if(this.isEditableValue(o[k])){
43653 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
43656 this.store.loadRecords({records: data}, {}, true);
43659 onUpdate : function(ds, record, type){
43660 if(type == Roo.data.Record.EDIT){
43661 var v = record.data['value'];
43662 var oldValue = record.modified['value'];
43663 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
43664 this.source[record.id] = v;
43666 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
43673 getProperty : function(row){
43674 return this.store.getAt(row);
43677 isEditableValue: function(val){
43678 if(val && val instanceof Date){
43680 }else if(typeof val == 'object' || typeof val == 'function'){
43686 setValue : function(prop, value){
43687 this.source[prop] = value;
43688 this.store.getById(prop).set('value', value);
43691 getSource : function(){
43692 return this.source;
43696 Roo.grid.PropertyColumnModel = function(grid, store){
43699 g.PropertyColumnModel.superclass.constructor.call(this, [
43700 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
43701 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
43703 this.store = store;
43704 this.bselect = Roo.DomHelper.append(document.body, {
43705 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
43706 {tag: 'option', value: 'true', html: 'true'},
43707 {tag: 'option', value: 'false', html: 'false'}
43710 Roo.id(this.bselect);
43713 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
43714 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
43715 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
43716 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
43717 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
43719 this.renderCellDelegate = this.renderCell.createDelegate(this);
43720 this.renderPropDelegate = this.renderProp.createDelegate(this);
43723 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
43727 valueText : 'Value',
43729 dateFormat : 'm/j/Y',
43732 renderDate : function(dateVal){
43733 return dateVal.dateFormat(this.dateFormat);
43736 renderBool : function(bVal){
43737 return bVal ? 'true' : 'false';
43740 isCellEditable : function(colIndex, rowIndex){
43741 return colIndex == 1;
43744 getRenderer : function(col){
43746 this.renderCellDelegate : this.renderPropDelegate;
43749 renderProp : function(v){
43750 return this.getPropertyName(v);
43753 renderCell : function(val){
43755 if(val instanceof Date){
43756 rv = this.renderDate(val);
43757 }else if(typeof val == 'boolean'){
43758 rv = this.renderBool(val);
43760 return Roo.util.Format.htmlEncode(rv);
43763 getPropertyName : function(name){
43764 var pn = this.grid.propertyNames;
43765 return pn && pn[name] ? pn[name] : name;
43768 getCellEditor : function(colIndex, rowIndex){
43769 var p = this.store.getProperty(rowIndex);
43770 var n = p.data['name'], val = p.data['value'];
43772 if(typeof(this.grid.customEditors[n]) == 'string'){
43773 return this.editors[this.grid.customEditors[n]];
43775 if(typeof(this.grid.customEditors[n]) != 'undefined'){
43776 return this.grid.customEditors[n];
43778 if(val instanceof Date){
43779 return this.editors['date'];
43780 }else if(typeof val == 'number'){
43781 return this.editors['number'];
43782 }else if(typeof val == 'boolean'){
43783 return this.editors['boolean'];
43785 return this.editors['string'];
43791 * @class Roo.grid.PropertyGrid
43792 * @extends Roo.grid.EditorGrid
43793 * This class represents the interface of a component based property grid control.
43794 * <br><br>Usage:<pre><code>
43795 var grid = new Roo.grid.PropertyGrid("my-container-id", {
43803 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
43804 * The container MUST have some type of size defined for the grid to fill. The container will be
43805 * automatically set to position relative if it isn't already.
43806 * @param {Object} config A config object that sets properties on this grid.
43808 Roo.grid.PropertyGrid = function(container, config){
43809 config = config || {};
43810 var store = new Roo.grid.PropertyStore(this);
43811 this.store = store;
43812 var cm = new Roo.grid.PropertyColumnModel(this, store);
43813 store.store.sort('name', 'ASC');
43814 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
43817 enableColLock:false,
43818 enableColumnMove:false,
43820 trackMouseOver: false,
43823 this.getGridEl().addClass('x-props-grid');
43824 this.lastEditRow = null;
43825 this.on('columnresize', this.onColumnResize, this);
43828 * @event beforepropertychange
43829 * Fires before a property changes (return false to stop?)
43830 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
43831 * @param {String} id Record Id
43832 * @param {String} newval New Value
43833 * @param {String} oldval Old Value
43835 "beforepropertychange": true,
43837 * @event propertychange
43838 * Fires after a property changes
43839 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
43840 * @param {String} id Record Id
43841 * @param {String} newval New Value
43842 * @param {String} oldval Old Value
43844 "propertychange": true
43846 this.customEditors = this.customEditors || {};
43848 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
43851 * @cfg {Object} customEditors map of colnames=> custom editors.
43852 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
43853 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
43854 * false disables editing of the field.
43858 * @cfg {Object} propertyNames map of property Names to their displayed value
43861 render : function(){
43862 Roo.grid.PropertyGrid.superclass.render.call(this);
43863 this.autoSize.defer(100, this);
43866 autoSize : function(){
43867 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
43869 this.view.fitColumns();
43873 onColumnResize : function(){
43874 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
43878 * Sets the data for the Grid
43879 * accepts a Key => Value object of all the elements avaiable.
43880 * @param {Object} data to appear in grid.
43882 setSource : function(source){
43883 this.store.setSource(source);
43887 * Gets all the data from the grid.
43888 * @return {Object} data data stored in grid
43890 getSource : function(){
43891 return this.store.getSource();
43900 * @class Roo.grid.Calendar
43901 * @extends Roo.grid.Grid
43902 * This class extends the Grid to provide a calendar widget
43903 * <br><br>Usage:<pre><code>
43904 var grid = new Roo.grid.Calendar("my-container-id", {
43907 selModel: mySelectionModel,
43908 autoSizeColumns: true,
43909 monitorWindowResize: false,
43910 trackMouseOver: true
43911 eventstore : real data store..
43917 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
43918 * The container MUST have some type of size defined for the grid to fill. The container will be
43919 * automatically set to position relative if it isn't already.
43920 * @param {Object} config A config object that sets properties on this grid.
43922 Roo.grid.Calendar = function(container, config){
43923 // initialize the container
43924 this.container = Roo.get(container);
43925 this.container.update("");
43926 this.container.setStyle("overflow", "hidden");
43927 this.container.addClass('x-grid-container');
43929 this.id = this.container.id;
43931 Roo.apply(this, config);
43932 // check and correct shorthanded configs
43936 for (var r = 0;r < 6;r++) {
43939 for (var c =0;c < 7;c++) {
43943 if (this.eventStore) {
43944 this.eventStore= Roo.factory(this.eventStore, Roo.data);
43945 this.eventStore.on('load',this.onLoad, this);
43946 this.eventStore.on('beforeload',this.clearEvents, this);
43950 this.dataSource = new Roo.data.Store({
43951 proxy: new Roo.data.MemoryProxy(rows),
43952 reader: new Roo.data.ArrayReader({}, [
43953 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
43956 this.dataSource.load();
43957 this.ds = this.dataSource;
43958 this.ds.xmodule = this.xmodule || false;
43961 var cellRender = function(v,x,r)
43963 return String.format(
43964 '<div class="fc-day fc-widget-content"><div>' +
43965 '<div class="fc-event-container"></div>' +
43966 '<div class="fc-day-number">{0}</div>'+
43968 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
43969 '</div></div>', v);
43974 this.colModel = new Roo.grid.ColumnModel( [
43976 xtype: 'ColumnModel',
43978 dataIndex : 'weekday0',
43980 renderer : cellRender
43983 xtype: 'ColumnModel',
43985 dataIndex : 'weekday1',
43987 renderer : cellRender
43990 xtype: 'ColumnModel',
43992 dataIndex : 'weekday2',
43993 header : 'Tuesday',
43994 renderer : cellRender
43997 xtype: 'ColumnModel',
43999 dataIndex : 'weekday3',
44000 header : 'Wednesday',
44001 renderer : cellRender
44004 xtype: 'ColumnModel',
44006 dataIndex : 'weekday4',
44007 header : 'Thursday',
44008 renderer : cellRender
44011 xtype: 'ColumnModel',
44013 dataIndex : 'weekday5',
44015 renderer : cellRender
44018 xtype: 'ColumnModel',
44020 dataIndex : 'weekday6',
44021 header : 'Saturday',
44022 renderer : cellRender
44025 this.cm = this.colModel;
44026 this.cm.xmodule = this.xmodule || false;
44030 //this.selModel = new Roo.grid.CellSelectionModel();
44031 //this.sm = this.selModel;
44032 //this.selModel.init(this);
44036 this.container.setWidth(this.width);
44040 this.container.setHeight(this.height);
44047 * The raw click event for the entire grid.
44048 * @param {Roo.EventObject} e
44053 * The raw dblclick event for the entire grid.
44054 * @param {Roo.EventObject} e
44058 * @event contextmenu
44059 * The raw contextmenu event for the entire grid.
44060 * @param {Roo.EventObject} e
44062 "contextmenu" : true,
44065 * The raw mousedown event for the entire grid.
44066 * @param {Roo.EventObject} e
44068 "mousedown" : true,
44071 * The raw mouseup event for the entire grid.
44072 * @param {Roo.EventObject} e
44077 * The raw mouseover event for the entire grid.
44078 * @param {Roo.EventObject} e
44080 "mouseover" : true,
44083 * The raw mouseout event for the entire grid.
44084 * @param {Roo.EventObject} e
44089 * The raw keypress event for the entire grid.
44090 * @param {Roo.EventObject} e
44095 * The raw keydown event for the entire grid.
44096 * @param {Roo.EventObject} e
44104 * Fires when a cell is clicked
44105 * @param {Grid} this
44106 * @param {Number} rowIndex
44107 * @param {Number} columnIndex
44108 * @param {Roo.EventObject} e
44110 "cellclick" : true,
44112 * @event celldblclick
44113 * Fires when a cell is double clicked
44114 * @param {Grid} this
44115 * @param {Number} rowIndex
44116 * @param {Number} columnIndex
44117 * @param {Roo.EventObject} e
44119 "celldblclick" : true,
44122 * Fires when a row is clicked
44123 * @param {Grid} this
44124 * @param {Number} rowIndex
44125 * @param {Roo.EventObject} e
44129 * @event rowdblclick
44130 * Fires when a row is double clicked
44131 * @param {Grid} this
44132 * @param {Number} rowIndex
44133 * @param {Roo.EventObject} e
44135 "rowdblclick" : true,
44137 * @event headerclick
44138 * Fires when a header is clicked
44139 * @param {Grid} this
44140 * @param {Number} columnIndex
44141 * @param {Roo.EventObject} e
44143 "headerclick" : true,
44145 * @event headerdblclick
44146 * Fires when a header cell is double clicked
44147 * @param {Grid} this
44148 * @param {Number} columnIndex
44149 * @param {Roo.EventObject} e
44151 "headerdblclick" : true,
44153 * @event rowcontextmenu
44154 * Fires when a row is right clicked
44155 * @param {Grid} this
44156 * @param {Number} rowIndex
44157 * @param {Roo.EventObject} e
44159 "rowcontextmenu" : true,
44161 * @event cellcontextmenu
44162 * Fires when a cell is right clicked
44163 * @param {Grid} this
44164 * @param {Number} rowIndex
44165 * @param {Number} cellIndex
44166 * @param {Roo.EventObject} e
44168 "cellcontextmenu" : true,
44170 * @event headercontextmenu
44171 * Fires when a header is right clicked
44172 * @param {Grid} this
44173 * @param {Number} columnIndex
44174 * @param {Roo.EventObject} e
44176 "headercontextmenu" : true,
44178 * @event bodyscroll
44179 * Fires when the body element is scrolled
44180 * @param {Number} scrollLeft
44181 * @param {Number} scrollTop
44183 "bodyscroll" : true,
44185 * @event columnresize
44186 * Fires when the user resizes a column
44187 * @param {Number} columnIndex
44188 * @param {Number} newSize
44190 "columnresize" : true,
44192 * @event columnmove
44193 * Fires when the user moves a column
44194 * @param {Number} oldIndex
44195 * @param {Number} newIndex
44197 "columnmove" : true,
44200 * Fires when row(s) start being dragged
44201 * @param {Grid} this
44202 * @param {Roo.GridDD} dd The drag drop object
44203 * @param {event} e The raw browser event
44205 "startdrag" : true,
44208 * Fires when a drag operation is complete
44209 * @param {Grid} this
44210 * @param {Roo.GridDD} dd The drag drop object
44211 * @param {event} e The raw browser event
44216 * Fires when dragged row(s) are dropped on a valid DD target
44217 * @param {Grid} this
44218 * @param {Roo.GridDD} dd The drag drop object
44219 * @param {String} targetId The target drag drop object
44220 * @param {event} e The raw browser event
44225 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
44226 * @param {Grid} this
44227 * @param {Roo.GridDD} dd The drag drop object
44228 * @param {String} targetId The target drag drop object
44229 * @param {event} e The raw browser event
44234 * Fires when the dragged row(s) first cross another DD target while being dragged
44235 * @param {Grid} this
44236 * @param {Roo.GridDD} dd The drag drop object
44237 * @param {String} targetId The target drag drop object
44238 * @param {event} e The raw browser event
44240 "dragenter" : true,
44243 * Fires when the dragged row(s) leave another DD target while being dragged
44244 * @param {Grid} this
44245 * @param {Roo.GridDD} dd The drag drop object
44246 * @param {String} targetId The target drag drop object
44247 * @param {event} e The raw browser event
44252 * Fires when a row is rendered, so you can change add a style to it.
44253 * @param {GridView} gridview The grid view
44254 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
44260 * Fires when the grid is rendered
44261 * @param {Grid} grid
44266 * Fires when a date is selected
44267 * @param {DatePicker} this
44268 * @param {Date} date The selected date
44272 * @event monthchange
44273 * Fires when the displayed month changes
44274 * @param {DatePicker} this
44275 * @param {Date} date The selected month
44277 'monthchange': true,
44279 * @event evententer
44280 * Fires when mouse over an event
44281 * @param {Calendar} this
44282 * @param {event} Event
44284 'evententer': true,
44286 * @event eventleave
44287 * Fires when the mouse leaves an
44288 * @param {Calendar} this
44291 'eventleave': true,
44293 * @event eventclick
44294 * Fires when the mouse click an
44295 * @param {Calendar} this
44298 'eventclick': true,
44300 * @event eventrender
44301 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
44302 * @param {Calendar} this
44303 * @param {data} data to be modified
44305 'eventrender': true
44309 Roo.grid.Grid.superclass.constructor.call(this);
44310 this.on('render', function() {
44311 this.view.el.addClass('x-grid-cal');
44313 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
44317 if (!Roo.grid.Calendar.style) {
44318 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
44321 '.x-grid-cal .x-grid-col' : {
44322 height: 'auto !important',
44323 'vertical-align': 'top'
44325 '.x-grid-cal .fc-event-hori' : {
44336 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
44338 * @cfg {Store} eventStore The store that loads events.
44343 activeDate : false,
44346 monitorWindowResize : false,
44349 resizeColumns : function() {
44350 var col = (this.view.el.getWidth() / 7) - 3;
44351 // loop through cols, and setWidth
44352 for(var i =0 ; i < 7 ; i++){
44353 this.cm.setColumnWidth(i, col);
44356 setDate :function(date) {
44358 Roo.log('setDate?');
44360 this.resizeColumns();
44361 var vd = this.activeDate;
44362 this.activeDate = date;
44363 // if(vd && this.el){
44364 // var t = date.getTime();
44365 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
44366 // Roo.log('using add remove');
44368 // this.fireEvent('monthchange', this, date);
44370 // this.cells.removeClass("fc-state-highlight");
44371 // this.cells.each(function(c){
44372 // if(c.dateValue == t){
44373 // c.addClass("fc-state-highlight");
44374 // setTimeout(function(){
44375 // try{c.dom.firstChild.focus();}catch(e){}
44385 var days = date.getDaysInMonth();
44387 var firstOfMonth = date.getFirstDateOfMonth();
44388 var startingPos = firstOfMonth.getDay()-this.startDay;
44390 if(startingPos < this.startDay){
44394 var pm = date.add(Date.MONTH, -1);
44395 var prevStart = pm.getDaysInMonth()-startingPos;
44399 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
44401 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
44402 //this.cells.addClassOnOver('fc-state-hover');
44404 var cells = this.cells.elements;
44405 var textEls = this.textNodes;
44407 //Roo.each(cells, function(cell){
44408 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
44411 days += startingPos;
44413 // convert everything to numbers so it's fast
44414 var day = 86400000;
44415 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
44418 //Roo.log(prevStart);
44420 var today = new Date().clearTime().getTime();
44421 var sel = date.clearTime().getTime();
44422 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
44423 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
44424 var ddMatch = this.disabledDatesRE;
44425 var ddText = this.disabledDatesText;
44426 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
44427 var ddaysText = this.disabledDaysText;
44428 var format = this.format;
44430 var setCellClass = function(cal, cell){
44432 //Roo.log('set Cell Class');
44434 var t = d.getTime();
44439 cell.dateValue = t;
44441 cell.className += " fc-today";
44442 cell.className += " fc-state-highlight";
44443 cell.title = cal.todayText;
44446 // disable highlight in other month..
44447 cell.className += " fc-state-highlight";
44452 //cell.className = " fc-state-disabled";
44453 cell.title = cal.minText;
44457 //cell.className = " fc-state-disabled";
44458 cell.title = cal.maxText;
44462 if(ddays.indexOf(d.getDay()) != -1){
44463 // cell.title = ddaysText;
44464 // cell.className = " fc-state-disabled";
44467 if(ddMatch && format){
44468 var fvalue = d.dateFormat(format);
44469 if(ddMatch.test(fvalue)){
44470 cell.title = ddText.replace("%0", fvalue);
44471 cell.className = " fc-state-disabled";
44475 if (!cell.initialClassName) {
44476 cell.initialClassName = cell.dom.className;
44479 cell.dom.className = cell.initialClassName + ' ' + cell.className;
44484 for(; i < startingPos; i++) {
44485 cells[i].dayName = (++prevStart);
44486 Roo.log(textEls[i]);
44487 d.setDate(d.getDate()+1);
44489 //cells[i].className = "fc-past fc-other-month";
44490 setCellClass(this, cells[i]);
44495 for(; i < days; i++){
44496 intDay = i - startingPos + 1;
44497 cells[i].dayName = (intDay);
44498 d.setDate(d.getDate()+1);
44500 cells[i].className = ''; // "x-date-active";
44501 setCellClass(this, cells[i]);
44505 for(; i < 42; i++) {
44506 //textEls[i].innerHTML = (++extraDays);
44508 d.setDate(d.getDate()+1);
44509 cells[i].dayName = (++extraDays);
44510 cells[i].className = "fc-future fc-other-month";
44511 setCellClass(this, cells[i]);
44514 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
44516 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
44518 // this will cause all the cells to mis
44521 for (var r = 0;r < 6;r++) {
44522 for (var c =0;c < 7;c++) {
44523 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
44527 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
44528 for(i=0;i<cells.length;i++) {
44530 this.cells.elements[i].dayName = cells[i].dayName ;
44531 this.cells.elements[i].className = cells[i].className;
44532 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
44533 this.cells.elements[i].title = cells[i].title ;
44534 this.cells.elements[i].dateValue = cells[i].dateValue ;
44540 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
44541 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
44543 ////if(totalRows != 6){
44544 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
44545 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
44548 this.fireEvent('monthchange', this, date);
44553 * Returns the grid's SelectionModel.
44554 * @return {SelectionModel}
44556 getSelectionModel : function(){
44557 if(!this.selModel){
44558 this.selModel = new Roo.grid.CellSelectionModel();
44560 return this.selModel;
44564 this.eventStore.load()
44570 findCell : function(dt) {
44571 dt = dt.clearTime().getTime();
44573 this.cells.each(function(c){
44574 //Roo.log("check " +c.dateValue + '?=' + dt);
44575 if(c.dateValue == dt){
44585 findCells : function(rec) {
44586 var s = rec.data.start_dt.clone().clearTime().getTime();
44588 var e= rec.data.end_dt.clone().clearTime().getTime();
44591 this.cells.each(function(c){
44592 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
44594 if(c.dateValue > e){
44597 if(c.dateValue < s){
44606 findBestRow: function(cells)
44610 for (var i =0 ; i < cells.length;i++) {
44611 ret = Math.max(cells[i].rows || 0,ret);
44618 addItem : function(rec)
44620 // look for vertical location slot in
44621 var cells = this.findCells(rec);
44623 rec.row = this.findBestRow(cells);
44625 // work out the location.
44629 for(var i =0; i < cells.length; i++) {
44637 if (crow.start.getY() == cells[i].getY()) {
44639 crow.end = cells[i];
44655 for (var i = 0; i < cells.length;i++) {
44656 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
44663 clearEvents: function() {
44665 if (!this.eventStore.getCount()) {
44668 // reset number of rows in cells.
44669 Roo.each(this.cells.elements, function(c){
44673 this.eventStore.each(function(e) {
44674 this.clearEvent(e);
44679 clearEvent : function(ev)
44682 Roo.each(ev.els, function(el) {
44683 el.un('mouseenter' ,this.onEventEnter, this);
44684 el.un('mouseleave' ,this.onEventLeave, this);
44692 renderEvent : function(ev,ctr) {
44694 ctr = this.view.el.select('.fc-event-container',true).first();
44698 this.clearEvent(ev);
44704 var cells = ev.cells;
44705 var rows = ev.rows;
44706 this.fireEvent('eventrender', this, ev);
44708 for(var i =0; i < rows.length; i++) {
44712 cls += ' fc-event-start';
44714 if ((i+1) == rows.length) {
44715 cls += ' fc-event-end';
44718 //Roo.log(ev.data);
44719 // how many rows should it span..
44720 var cg = this.eventTmpl.append(ctr,Roo.apply({
44723 }, ev.data) , true);
44726 cg.on('mouseenter' ,this.onEventEnter, this, ev);
44727 cg.on('mouseleave' ,this.onEventLeave, this, ev);
44728 cg.on('click', this.onEventClick, this, ev);
44732 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
44733 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
44736 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
44737 cg.setWidth(ebox.right - sbox.x -2);
44741 renderEvents: function()
44743 // first make sure there is enough space..
44745 if (!this.eventTmpl) {
44746 this.eventTmpl = new Roo.Template(
44747 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
44748 '<div class="fc-event-inner">' +
44749 '<span class="fc-event-time">{time}</span>' +
44750 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
44752 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
44760 this.cells.each(function(c) {
44761 //Roo.log(c.select('.fc-day-content div',true).first());
44762 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
44765 var ctr = this.view.el.select('.fc-event-container',true).first();
44768 this.eventStore.each(function(ev){
44770 this.renderEvent(ev);
44774 this.view.layout();
44778 onEventEnter: function (e, el,event,d) {
44779 this.fireEvent('evententer', this, el, event);
44782 onEventLeave: function (e, el,event,d) {
44783 this.fireEvent('eventleave', this, el, event);
44786 onEventClick: function (e, el,event,d) {
44787 this.fireEvent('eventclick', this, el, event);
44790 onMonthChange: function () {
44794 onLoad: function () {
44796 //Roo.log('calendar onload');
44798 if(this.eventStore.getCount() > 0){
44802 this.eventStore.each(function(d){
44807 if (typeof(add.end_dt) == 'undefined') {
44808 Roo.log("Missing End time in calendar data: ");
44812 if (typeof(add.start_dt) == 'undefined') {
44813 Roo.log("Missing Start time in calendar data: ");
44817 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
44818 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
44819 add.id = add.id || d.id;
44820 add.title = add.title || '??';
44828 this.renderEvents();
44838 render : function ()
44842 if (!this.view.el.hasClass('course-timesheet')) {
44843 this.view.el.addClass('course-timesheet');
44845 if (this.tsStyle) {
44850 Roo.log(_this.grid.view.el.getWidth());
44853 this.tsStyle = Roo.util.CSS.createStyleSheet({
44854 '.course-timesheet .x-grid-row' : {
44857 '.x-grid-row td' : {
44858 'vertical-align' : 0
44860 '.course-edit-link' : {
44862 'text-overflow' : 'ellipsis',
44863 'overflow' : 'hidden',
44864 'white-space' : 'nowrap',
44865 'cursor' : 'pointer'
44870 '.de-act-sup-link' : {
44871 'color' : 'purple',
44872 'text-decoration' : 'line-through'
44876 'text-decoration' : 'line-through'
44878 '.course-timesheet .course-highlight' : {
44879 'border-top-style': 'dashed !important',
44880 'border-bottom-bottom': 'dashed !important'
44882 '.course-timesheet .course-item' : {
44883 'font-family' : 'tahoma, arial, helvetica',
44884 'font-size' : '11px',
44885 'overflow' : 'hidden',
44886 'padding-left' : '10px',
44887 'padding-right' : '10px',
44888 'padding-top' : '10px'
44896 monitorWindowResize : false,
44897 cellrenderer : function(v,x,r)
44902 xtype: 'CellSelectionModel',
44909 beforeload : function (_self, options)
44911 options.params = options.params || {};
44912 options.params._month = _this.monthField.getValue();
44913 options.params.limit = 9999;
44914 options.params['sort'] = 'when_dt';
44915 options.params['dir'] = 'ASC';
44916 this.proxy.loadResponse = this.loadResponse;
44918 //this.addColumns();
44920 load : function (_self, records, options)
44922 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
44923 // if you click on the translation.. you can edit it...
44924 var el = Roo.get(this);
44925 var id = el.dom.getAttribute('data-id');
44926 var d = el.dom.getAttribute('data-date');
44927 var t = el.dom.getAttribute('data-time');
44928 //var id = this.child('span').dom.textContent;
44931 Pman.Dialog.CourseCalendar.show({
44935 productitem_active : id ? 1 : 0
44937 _this.grid.ds.load({});
44942 _this.panel.fireEvent('resize', [ '', '' ]);
44945 loadResponse : function(o, success, response){
44946 // this is overridden on before load..
44948 Roo.log("our code?");
44949 //Roo.log(success);
44950 //Roo.log(response)
44951 delete this.activeRequest;
44953 this.fireEvent("loadexception", this, o, response);
44954 o.request.callback.call(o.request.scope, null, o.request.arg, false);
44959 result = o.reader.read(response);
44961 Roo.log("load exception?");
44962 this.fireEvent("loadexception", this, o, response, e);
44963 o.request.callback.call(o.request.scope, null, o.request.arg, false);
44966 Roo.log("ready...");
44967 // loop through result.records;
44968 // and set this.tdate[date] = [] << array of records..
44970 Roo.each(result.records, function(r){
44972 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
44973 _this.tdata[r.data.when_dt.format('j')] = [];
44975 _this.tdata[r.data.when_dt.format('j')].push(r.data);
44978 //Roo.log(_this.tdata);
44980 result.records = [];
44981 result.totalRecords = 6;
44983 // let's generate some duumy records for the rows.
44984 //var st = _this.dateField.getValue();
44986 // work out monday..
44987 //st = st.add(Date.DAY, -1 * st.format('w'));
44989 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
44991 var firstOfMonth = date.getFirstDayOfMonth();
44992 var days = date.getDaysInMonth();
44994 var firstAdded = false;
44995 for (var i = 0; i < result.totalRecords ; i++) {
44996 //var d= st.add(Date.DAY, i);
44999 for(var w = 0 ; w < 7 ; w++){
45000 if(!firstAdded && firstOfMonth != w){
45007 var dd = (d > 0 && d < 10) ? "0"+d : d;
45008 row['weekday'+w] = String.format(
45009 '<span style="font-size: 16px;"><b>{0}</b></span>'+
45010 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
45012 date.format('Y-m-')+dd
45015 if(typeof(_this.tdata[d]) != 'undefined'){
45016 Roo.each(_this.tdata[d], function(r){
45020 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
45021 if(r.parent_id*1>0){
45022 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
45025 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
45026 deactive = 'de-act-link';
45029 row['weekday'+w] += String.format(
45030 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
45032 r.product_id_name, //1
45033 r.when_dt.format('h:ia'), //2
45043 // only do this if something added..
45045 result.records.push(_this.grid.dataSource.reader.newRow(row));
45049 // push it twice. (second one with an hour..
45053 this.fireEvent("load", this, o, o.request.arg);
45054 o.request.callback.call(o.request.scope, result, o.request.arg, true);
45056 sortInfo : {field: 'when_dt', direction : 'ASC' },
45058 xtype: 'HttpProxy',
45061 url : baseURL + '/Roo/Shop_course.php'
45064 xtype: 'JsonReader',
45081 'name': 'parent_id',
45085 'name': 'product_id',
45089 'name': 'productitem_id',
45107 click : function (_self, e)
45109 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
45110 sd.setMonth(sd.getMonth()-1);
45111 _this.monthField.setValue(sd.format('Y-m-d'));
45112 _this.grid.ds.load({});
45118 xtype: 'Separator',
45122 xtype: 'MonthField',
45125 render : function (_self)
45127 _this.monthField = _self;
45128 // _this.monthField.set today
45130 select : function (combo, date)
45132 _this.grid.ds.load({});
45135 value : (function() { return new Date(); })()
45138 xtype: 'Separator',
45144 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
45154 click : function (_self, e)
45156 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
45157 sd.setMonth(sd.getMonth()+1);
45158 _this.monthField.setValue(sd.format('Y-m-d'));
45159 _this.grid.ds.load({});
45172 * Ext JS Library 1.1.1
45173 * Copyright(c) 2006-2007, Ext JS, LLC.
45175 * Originally Released Under LGPL - original licence link has changed is not relivant.
45178 * <script type="text/javascript">
45182 * @class Roo.LoadMask
45183 * A simple utility class for generically masking elements while loading data. If the element being masked has
45184 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
45185 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
45186 * element's UpdateManager load indicator and will be destroyed after the initial load.
45188 * Create a new LoadMask
45189 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
45190 * @param {Object} config The config object
45192 Roo.LoadMask = function(el, config){
45193 this.el = Roo.get(el);
45194 Roo.apply(this, config);
45196 this.store.on('beforeload', this.onBeforeLoad, this);
45197 this.store.on('load', this.onLoad, this);
45198 this.store.on('loadexception', this.onLoadException, this);
45199 this.removeMask = false;
45201 var um = this.el.getUpdateManager();
45202 um.showLoadIndicator = false; // disable the default indicator
45203 um.on('beforeupdate', this.onBeforeLoad, this);
45204 um.on('update', this.onLoad, this);
45205 um.on('failure', this.onLoad, this);
45206 this.removeMask = true;
45210 Roo.LoadMask.prototype = {
45212 * @cfg {Boolean} removeMask
45213 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
45214 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
45216 removeMask : false,
45218 * @cfg {String} msg
45219 * The text to display in a centered loading message box (defaults to 'Loading...')
45221 msg : 'Loading...',
45223 * @cfg {String} msgCls
45224 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
45226 msgCls : 'x-mask-loading',
45229 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
45235 * Disables the mask to prevent it from being displayed
45237 disable : function(){
45238 this.disabled = true;
45242 * Enables the mask so that it can be displayed
45244 enable : function(){
45245 this.disabled = false;
45248 onLoadException : function()
45250 Roo.log(arguments);
45252 if (typeof(arguments[3]) != 'undefined') {
45253 Roo.MessageBox.alert("Error loading",arguments[3]);
45257 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
45258 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
45265 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
45268 onLoad : function()
45270 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
45274 onBeforeLoad : function(){
45275 if(!this.disabled){
45276 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
45281 destroy : function(){
45283 this.store.un('beforeload', this.onBeforeLoad, this);
45284 this.store.un('load', this.onLoad, this);
45285 this.store.un('loadexception', this.onLoadException, this);
45287 var um = this.el.getUpdateManager();
45288 um.un('beforeupdate', this.onBeforeLoad, this);
45289 um.un('update', this.onLoad, this);
45290 um.un('failure', this.onLoad, this);
45295 * Ext JS Library 1.1.1
45296 * Copyright(c) 2006-2007, Ext JS, LLC.
45298 * Originally Released Under LGPL - original licence link has changed is not relivant.
45301 * <script type="text/javascript">
45306 * @class Roo.XTemplate
45307 * @extends Roo.Template
45308 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
45310 var t = new Roo.XTemplate(
45311 '<select name="{name}">',
45312 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
45316 // then append, applying the master template values
45319 * Supported features:
45324 {a_variable} - output encoded.
45325 {a_variable.format:("Y-m-d")} - call a method on the variable
45326 {a_variable:raw} - unencoded output
45327 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
45328 {a_variable:this.method_on_template(...)} - call a method on the template object.
45333 <tpl for="a_variable or condition.."></tpl>
45334 <tpl if="a_variable or condition"></tpl>
45335 <tpl exec="some javascript"></tpl>
45336 <tpl name="named_template"></tpl> (experimental)
45338 <tpl for="."></tpl> - just iterate the property..
45339 <tpl for=".."></tpl> - iterates with the parent (probably the template)
45343 Roo.XTemplate = function()
45345 Roo.XTemplate.superclass.constructor.apply(this, arguments);
45352 Roo.extend(Roo.XTemplate, Roo.Template, {
45355 * The various sub templates
45360 * basic tag replacing syntax
45363 * // you can fake an object call by doing this
45367 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
45370 * compile the template
45372 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
45375 compile: function()
45379 s = ['<tpl>', s, '</tpl>'].join('');
45381 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
45382 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
45383 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
45384 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
45385 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
45390 while(true == !!(m = s.match(re))){
45391 var forMatch = m[0].match(nameRe),
45392 ifMatch = m[0].match(ifRe),
45393 execMatch = m[0].match(execRe),
45394 namedMatch = m[0].match(namedRe),
45399 name = forMatch && forMatch[1] ? forMatch[1] : '';
45402 // if - puts fn into test..
45403 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
45405 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
45410 // exec - calls a function... returns empty if true is returned.
45411 exp = execMatch && execMatch[1] ? execMatch[1] : null;
45413 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
45421 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
45422 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
45423 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
45426 var uid = namedMatch ? namedMatch[1] : id;
45430 id: namedMatch ? namedMatch[1] : id,
45437 s = s.replace(m[0], '');
45439 s = s.replace(m[0], '{xtpl'+ id + '}');
45444 for(var i = tpls.length-1; i >= 0; --i){
45445 this.compileTpl(tpls[i]);
45446 this.tpls[tpls[i].id] = tpls[i];
45448 this.master = tpls[tpls.length-1];
45452 * same as applyTemplate, except it's done to one of the subTemplates
45453 * when using named templates, you can do:
45455 * var str = pl.applySubTemplate('your-name', values);
45458 * @param {Number} id of the template
45459 * @param {Object} values to apply to template
45460 * @param {Object} parent (normaly the instance of this object)
45462 applySubTemplate : function(id, values, parent)
45466 var t = this.tpls[id];
45470 if(t.test && !t.test.call(this, values, parent)){
45474 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
45475 Roo.log(e.toString());
45481 if(t.exec && t.exec.call(this, values, parent)){
45485 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
45486 Roo.log(e.toString());
45491 var vs = t.target ? t.target.call(this, values, parent) : values;
45492 parent = t.target ? values : parent;
45493 if(t.target && vs instanceof Array){
45495 for(var i = 0, len = vs.length; i < len; i++){
45496 buf[buf.length] = t.compiled.call(this, vs[i], parent);
45498 return buf.join('');
45500 return t.compiled.call(this, vs, parent);
45502 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
45503 Roo.log(e.toString());
45504 Roo.log(t.compiled);
45509 compileTpl : function(tpl)
45511 var fm = Roo.util.Format;
45512 var useF = this.disableFormats !== true;
45513 var sep = Roo.isGecko ? "+" : ",";
45514 var undef = function(str) {
45515 Roo.log("Property not found :" + str);
45519 var fn = function(m, name, format, args)
45521 //Roo.log(arguments);
45522 args = args ? args.replace(/\\'/g,"'") : args;
45523 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
45524 if (typeof(format) == 'undefined') {
45525 format= 'htmlEncode';
45527 if (format == 'raw' ) {
45531 if(name.substr(0, 4) == 'xtpl'){
45532 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
45535 // build an array of options to determine if value is undefined..
45537 // basically get 'xxxx.yyyy' then do
45538 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
45539 // (function () { Roo.log("Property not found"); return ''; })() :
45544 Roo.each(name.split('.'), function(st) {
45545 lookfor += (lookfor.length ? '.': '') + st;
45546 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
45549 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
45552 if(format && useF){
45554 args = args ? ',' + args : "";
45556 if(format.substr(0, 5) != "this."){
45557 format = "fm." + format + '(';
45559 format = 'this.call("'+ format.substr(5) + '", ';
45563 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
45567 // called with xxyx.yuu:(test,test)
45569 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
45571 // raw.. - :raw modifier..
45572 return "'"+ sep + udef_st + name + ")"+sep+"'";
45576 // branched to use + in gecko and [].join() in others
45578 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
45579 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
45582 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
45583 body.push(tpl.body.replace(/(\r\n|\n)/g,
45584 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
45585 body.push("'].join('');};};");
45586 body = body.join('');
45589 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
45591 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
45597 applyTemplate : function(values){
45598 return this.master.compiled.call(this, values, {});
45599 //var s = this.subs;
45602 apply : function(){
45603 return this.applyTemplate.apply(this, arguments);
45608 Roo.XTemplate.from = function(el){
45609 el = Roo.getDom(el);
45610 return new Roo.XTemplate(el.value || el.innerHTML);
45611 };// old names for panel elements
45612 Roo.GridPanel = Roo.panel.Grid;
45613 Roo.CalendarPanel = Roo.panel.Calendar;
45614 Roo.ContentPanel = Roo.panel.Content;
45615 Roo.NestedLayoutPanel = Roo.panel.NestedLayout;
45616 // Roo.TabPanel = Roo.panel.Tab;
45617 // Roo.TabPanelItem = Roo.panel.TabItem;
45618 Roo.TreePanel = Roo.panel.Tree;