1 /* 2 * Copyright (c) 2012-2013 LabKey Corporation 3 * 4 * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 5 */ 6 7 /** 8 * @name LABKEY.ext4.data.JsonReader 9 * @-class 10 */ 11 Ext4.define('LABKEY.ext4.data.JsonReader', { 12 /** 13 * @-lends LABKEY.ext4.data.JsonReader 14 */ 15 extend: 'Ext.data.reader.Json', 16 alias: 'reader.labkeyjson', 17 config: { 18 userFilters: null, 19 useSimpleAccessors: true 20 }, 21 mixins: { 22 observable: 'Ext.util.Observable' 23 }, 24 constructor: function(){ 25 this.callParent(arguments); 26 this.addEvents('dataload'); 27 }, 28 readRecords: function(data) { 29 if (data.metaData){ 30 // NOTE: normalize which field holds the PK. this is a little unfortunate b/c ext will automatically create this field if it doesnt exist, 31 // such as a query w/o a PK. therefore we fall back to a standard name, which we can ignore when drawing grids 32 this.idProperty = data.metaData.id || this.idProperty || '_internalId'; 33 this.totalProperty = data.metaData.totalProperty; //NOTE: normalize which field holds total rows. 34 if (this.model){ 35 this.model.prototype.idProperty = this.idProperty; 36 this.model.prototype.totalProperty = this.totalProperty; 37 } 38 39 //NOTE: it would be interesting to convert this JSON into a more functional object here 40 //for example, columns w/ lookups could actually reference their target 41 //we could add methods like getDisplayString(), which accept the ext record and return the appropriate display string 42 Ext4.each(data.metaData.fields, function(meta){ 43 if (meta.jsonType == 'int' || meta.jsonType=='float' || meta.jsonType=='boolean') 44 meta.useNull = true; //prevents Ext from assigning 0's to field when record created 45 46 if (meta.jsonType == 'string') 47 meta.sortType = 'asUCString'; 48 49 //convert string into function 50 if (meta.extFormatFn){ 51 try { 52 meta.extFormatFn = eval(meta.extFormatFn); 53 } 54 catch (ex) 55 { 56 //this is potentially the sort of thing we'd want to log to mothership?? 57 } 58 } 59 60 if (meta.jsonType) { 61 meta.extType = LABKEY.ext4.Util.EXT_TYPE_MAP[meta.jsonType]; 62 } 63 }); 64 } 65 66 return this.callParent([data]); 67 }, 68 69 //added event to allow store to modify metadata before it is applied 70 onMetaChange : function(meta) { 71 this.fireEvent('datachange', meta); 72 73 this.callParent(arguments); 74 75 //NOTE: Ext.data.Model.onFieldAddReplace() would normally reset the idField; however, we usually end up changing the idProperty since it was not known at time of store creation 76 if (this.model){ 77 this.model.prototype.idField = this.model.prototype.fields.get(this.model.prototype.idProperty); 78 } 79 }, 80 81 /* 82 because our 9.1 API format returns results as objects, we transform them here. In addition to extracting the values, Ext creates an accessor for the record's ID 83 this must also be modified to support the 9.1 API. Because I believe getId() can be called both on initial load (prior to 84 when we transform the data) and after, I modified the method to test whether the field's value is an object instead of 85 looking for '.value' exclusively. 86 */ 87 createFieldAccessExpression: (function() { 88 var re = /[\[\.]/; 89 90 return function(field, fieldVarName, dataName) { 91 var me = this, 92 hasMap = (field.mapping !== null), 93 map = hasMap ? field.mapping : field.name, 94 result, 95 operatorSearch; 96 97 if (typeof map === 'function') { 98 result = fieldVarName + '.mapping(' + dataName + ', this)'; 99 } else if (this.useSimpleAccessors === true || ((operatorSearch = String(map).search(re)) < 0)) { 100 if (!hasMap || isNaN(map)) { 101 // If we don't provide a mapping, we may have a field name that is numeric 102 map = '"' + map + '"'; 103 } 104 //TODO: account for field.notFromServer here... 105 //also: we should investigate how convert() works and probably use this instead 106 result = dataName + "[" + map + "] !== undefined ? " + dataName + "[" + map + "].value : ''"; 107 } else { 108 result = dataName + (operatorSearch > 0 ? '.' : '') + map; 109 } 110 return result; 111 }; 112 }()), 113 114 //see note for createFieldAccessExpression() 115 buildExtractors: function(force) { 116 this.callParent(arguments); 117 118 var me = this, 119 idProp = me.getIdProperty(), 120 accessor, 121 idField, 122 map; 123 124 if (idProp) { 125 idField = me.model.prototype.fields.get(idProp); 126 if (idField) { 127 map = idField.mapping; 128 idProp = (map !== undefined && map !== null) ? map : idProp; 129 } 130 accessor = me.createAccessor('["' + idProp + '"].value'); 131 132 me.getId = function(record) { 133 var id = accessor.call(me, record); 134 return (id === undefined || id === '') ? null : id; 135 }; 136 } 137 } 138 }); 139