1 /** 2 * @fileOverview Manages background loading of data for jMatrixBrowse. 3 * 4 * When initiated, it loads the data for the complete matrix in the DOM. 5 * Other components should make requests to this to obtain data to be loaded 6 * in the matrix rather than directly speaking with the API as this module 7 * will manage the data already cached form the API and make additional requests 8 * if necessary. 9 * 10 * @version 0.1 11 * @author Pulkit Goyal <pulkit110@gmail.com> 12 */ 13 14 var jMatrixBrowseNs = jMatrixBrowseNs || {}; 15 16 (function (jQuery, jMatrixBrowseNs) { 17 18 /** 19 * Manages backgorund loading for jMatrixBrowse. 20 * 21 * @param {jQuery Object} elem - element that initiated jMatrixBrowse. 22 * @param {Object} api - api manager for making requests to api. 23 * @class BackgroundDataManager 24 * @memberOf jMatrixBrowseNs 25 */ 26 jMatrixBrowseNs.BackgroundDataManager = function(elem, api, config) { 27 var that = this; 28 29 var _backgroundDataContainer; // Container for background data. 30 31 var _windowLoaded = { 32 row1: 0, 33 col1: 0, 34 row2: 0, 35 col2: 0 36 }; 37 38 var backgroundLoadingWindowSize = {height: 20, width: 20}; // TODO: Move to config 39 40 var _elem = elem; // Element that triggered jMatrixBrowse. 41 var _api = api; // api manager 42 var _config = config; // jMatrixBrowse configuration. 43 44 beginLoadingData(); 45 46 // Public methods 47 this.getCellsForRequest = function(request, callback) { 48 // Create a request dynamically to load only the cells which have not already been loaded. 49 var requests = getRequiredRequests(request); 50 51 if (requests.length > 0) { 52 // Make requests for required data and merge the responses in one matrix of cells 53 var numberOfResponsesToReceive = requests.length; 54 var responses = new Array(numberOfResponsesToReceive); 55 for (var i = requests.length - 1; i >= 0; i--) { 56 (function(i) { 57 _api.getResponseDataAsync(requests[i], function(data) { 58 responses[i] = data; 59 -- numberOfResponsesToReceive; 60 if (numberOfResponsesToReceive == 0) { 61 // All responses have been loaded. Combine responses by taking the data from responses and background cells 62 var cells = combineResponses(requests, responses, request); 63 callback.call(this, cells); 64 } 65 }); 66 })(i); 67 }; 68 } else { 69 // No need to make requests to the api. 70 var cells = []; 71 for (var i = request.row1; i <= request.row2; ++i) { 72 cells.push([]); 73 for (var j = request.col1; j <= request.col2; ++j) { 74 var cellSelector = '.jMatrixBrowse-background-cell[data-row=' + i + '][data-col=' + j + ']'; 75 cells[i-request.row1].push(_elem.find(cellSelector)); 76 } 77 } 78 callback.call(this, cells); 79 } 80 }; 81 82 /** 83 * Gets the curernt window that has been loaded by the background data manager. 84 * 85 * @returns {Object} windowLoaded - window (row1, col1, row2, col2) of cells that have been loaded. 86 */ 87 this.getWindowLoaded = function() { 88 return _windowLoaded; 89 }; 90 91 // Private methods 92 /** 93 * Begins loading all the data into the dom. 94 * This creates a new container to hold the data inside elem. 95 */ 96 function beginLoadingData() { 97 98 // Create container for keeping background data. 99 _backgroundDataContainer = jQuery('<div/>', { 100 className: 'jMatrixBrowse-background-data-container' 101 }).appendTo(_elem); 102 103 (function loadData(request){ 104 setTimeout(function(){ 105 // Load Data 106 var response = _api.getResponseDataAsync(request, function(data) { 107 108 // Load data in DOM 109 for (var i = 0; i < data.length; ++i) { 110 for (var j = 0; j < data[i].length; ++j) { 111 var backgroundCell = jQuery('<div/>', { 112 className: 'jMatrixBrowse-background-cell', 113 'data-row': i + request.row1, 114 'data-col': j + request.col1, 115 html: data[i][j] 116 }); 117 if (_config.getDataReloadStrategy === jMatrixBrowseNs.Constants.RELOAD_CELL_POSITION) { 118 _elem.find('.jMatrixBrowse-content').append(backgroundCell); 119 backgroundCell.hide(); 120 } else { 121 _backgroundDataContainer.append(backgroundCell); 122 } 123 } 124 } 125 126 // Update the coordinates of window 127 _windowLoaded.row2 = request.row2; 128 _windowLoaded.col2 = request.col2; 129 130 var matrixSize = _api.getMatrixSize(); 131 132 if (request.row2 < matrixSize.height - 1) { 133 // Load more data in the same group of cols. 134 request.row1 = Math.min(request.row1 + backgroundLoadingWindowSize.height + 1, matrixSize.height-1); 135 request.row2 = Math.min(request.row2 + backgroundLoadingWindowSize.height + 1, matrixSize.height-1); 136 } else { 137 // Begin loading data in the next grouop of cols. 138 request.row1 = 0; 139 request.row2 = backgroundLoadingWindowSize.height; 140 if (request.col2 < matrixSize.width - 1) { 141 // There are more columns to load 142 request.col1 = Math.min(request.col1 + backgroundLoadingWindowSize.width + 1, matrixSize.width-1); 143 request.col2 = Math.min(request.col2 + backgroundLoadingWindowSize.width + 1, matrixSize.width-1); 144 } else { 145 // All columns loaded 146 // Don't need to request more data now. 147 _elem.trigger({ 148 type: 'jMatrixBrowseLoadComplete' 149 }); 150 return; 151 } 152 } 153 154 loadData(request); 155 }); 156 157 }, jMatrixBrowseNs.Constants.BACKGROUND_DATA_RELOAD_DELAY); 158 })({ 159 row1: 0, 160 row2: backgroundLoadingWindowSize.height, 161 col1: 0, 162 col2: backgroundLoadingWindowSize.width 163 }); 164 165 } 166 167 /** 168 * Forms the requests that should be made to the api to get the remaining cells. 169 * 170 * @param {Object} request The requested window. 171 * @returns {Array} Array of requests to be made to api to get cells not already in background. 172 */ 173 function getRequiredRequests(request) { 174 var requests = []; 175 var notAllCellsExist = false; 176 for (var j = request.col1; j <= request.col2; ++j) { 177 for (var i = request.row1; i <= request.row2; ++i) { 178 var cellSelector = '.jMatrixBrowse-background-cell[data-row=' + i + '][data-col=' + j + ']'; 179 if (_elem.find(cellSelector).length == 0) { 180 // This row doesn't exist 181 // Since we were loading columnwise, we know that all elements after i,j don't exist. 182 requests.push({ 183 row1: i, 184 col1: j, 185 row2: request.row2, 186 col2: request.col2 187 }); 188 notAllCellsExist = true; 189 break; 190 } 191 } 192 if (notAllCellsExist) 193 break; 194 } 195 if (notAllCellsExist) { 196 if (i > request.row1 && i < request.row2 && j > request.col1 && j < request.col2) { 197 requests.push({ 198 row1: request.row1, 199 col1: j+1, 200 row2: i-1, 201 col2: request.col2 202 }); 203 } 204 } 205 return requests; 206 } 207 208 /** 209 * Combines the responses from various api requests and backgrounds to form one matrix of cells. 210 * @param {Array} requests Array of requests that were made to the api. 211 * @param {Array} responses Responses received from the api. 212 * @param {Object} request One big request for which we want the data. 213 * @return {ArrayOfArray} Array of array of cells in the requested window. 214 */ 215 function combineResponses(requests, responses, request) { 216 // Make an array of array of cells 217 var cells = new Array(request.row2 - request.row1 + 1); 218 for (var i = cells.length - 1; i >= 0; i--) { 219 cells[i] = new Array(request.col2 - request.col1 + 1); 220 }; 221 222 // Merge the responses in one matrix 223 for (var i = responses.length - 1; i >= 0; --i) { 224 var currentResponse = responses[i]; 225 var currentRequest = requests[i]; 226 for (var j = currentResponse.length - 1; j >= 0; --j) { 227 for (var k = currentResponse[j].length - 1; k >= 0; --k) { 228 var cell = jQuery('<div/>', { 229 className: 'jMatrixBrowse-background-cell', 230 'data-row': j + currentRequest.row1, 231 'data-col': k + currentRequest.col1, 232 html: currentResponse[j][k] 233 }); 234 if (_config.getDataReloadStrategy === jMatrixBrowseNs.Constants.RELOAD_CELL_POSITION) { 235 _elem.find('.jMatrixBrowse-content').append(cell); 236 cell.hide(); 237 } 238 cells[j + currentRequest.row1 - request.row1][k + currentRequest.col1 - request.col1] = cell; 239 }; 240 }; 241 }; 242 243 // Add the already existing background cells to the matrix. 244 for (var j = request.col1; j <= requests[0].col1; ++j) { 245 // If we are at requests[0].col1, we should load only upto request[0].row1 rows from background 246 // Otherwise, we load until request.row2 rows 247 for (var i = request.row1; i <= request.row2; ++i) { 248 if (j == requests[0].col1 && i >= requests[0].row1) 249 break; 250 var cellSelector = '.jMatrixBrowse-background-cell[data-row=' + i + '][data-col=' + j + ']'; 251 cells[i - request.row1][j - request.col1] = _elem.find(cellSelector); 252 }; 253 }; 254 return cells; 255 } 256 257 return that; 258 }; 259 260 })(jQuery, jMatrixBrowseNs); 261