1 /*global GCN: true */ 2 (function (GCN) { 3 'use strict'; 4 5 /** 6 * Maps constructcategories that were fetched via the Rest API into a 7 * sorted nested array of constructs. 8 * 9 * @param {object<string, object>} constructs 10 * @return {object<string, object>} 11 */ 12 function mapConstructCategories(constructs) { 13 var constructKeyword; 14 var categoryMap = { 15 categories: {}, 16 categorySortorder: [] 17 }; 18 var constructCategoryArray = []; 19 for (constructKeyword in constructs) { 20 if (constructs.hasOwnProperty(constructKeyword)) { 21 var construct = constructs[constructKeyword]; 22 var constructCategoryName = construct.category; 23 var categorySortorder = construct.categorySortorder; 24 25 // Use a custom name for constructs that have not been assigned 26 // to a category. 27 if (!constructCategoryName) { 28 constructCategoryName = 'GCN_UNCATEGORIZED'; 29 categorySortorder = -1; 30 } 31 32 // Initialize the inner array of constructs. 33 if (!categoryMap.categories[constructCategoryName]) { 34 var newCategory = {}; 35 newCategory.constructs = {}; 36 newCategory.sortorder = categorySortorder; 37 newCategory.name = constructCategoryName; 38 categoryMap.categories[constructCategoryName] = newCategory; 39 constructCategoryArray.push(newCategory); 40 } 41 42 // Add the construct to the category. 43 categoryMap.categories[constructCategoryName] 44 .constructs[constructKeyword] = construct; 45 } 46 } 47 48 // Sort the categories by the sortorder. 49 constructCategoryArray.sort(function (a, b) { 50 return a.sortorder - b.sortorder; 51 }); 52 53 // Add the sorted category names to the sortorder field. 54 var k; 55 for (k in constructCategoryArray) { 56 if (constructCategoryArray.hasOwnProperty(k)) { 57 var category = constructCategoryArray[k]; 58 if (typeof category.sortorder !== 'undefined' && category.sortorder !== -1) { 59 categoryMap.categorySortorder.push(category.name); 60 } 61 } 62 } 63 64 return categoryMap; 65 } 66 67 /** 68 * Represents a Node 69 * 70 * @name NodeAPI 71 * @class 72 * @augments Chainback 73 * 74 * @param {number|string} 75 * id of the file to be loaded 76 * @param {function(ContentObjectAPI))=} 77 * success Optional success callback that will receive this 78 * object as its only argument. 79 * @param {function(GCNError):boolean=} 80 * error Optional custom error handler. 81 * @param {object} 82 * settings currently there are no additional settings to be used 83 */ 84 var NodeAPI = GCN.defineChainback({ 85 /** @lends NodeAPI */ 86 87 __chainbacktype__: 'NodeAPI', 88 _extends: GCN.ContentObjectAPI, 89 _type: 'node', 90 91 _data: { 92 folderId: null 93 }, 94 95 /** 96 * @private 97 * @type {object<string, number} Constructs for this node are cached 98 * here so that we only need to fetch 99 * this once. 100 */ 101 _constructs: null, 102 103 /** 104 * List of success and error callbacks that need to be called 105 * once the constructs are loaded 106 * @private 107 * @type {array.<object>} 108 */ 109 _constructLoadHandlers: null, 110 111 /** 112 * @private 113 * @type {object<string, object} Constructs categories for this node. 114 * Cached here so that we only need to 115 * fetch this once. 116 */ 117 _constructCategories: null, 118 119 /** 120 * Retrieves a list of constructs and constructs categories that are 121 * assigned to this node and passes it as the only argument into the 122 * the `success()' callback. 123 * 124 * @param {function(Array.<object>)=} success Callback to receive an 125 * array of constructs. 126 * @param {function(GCNError):boolean=} error Custom error handler. 127 * @return undefined 128 * @throws INVALID_ARGUMENTS 129 */ 130 constructs: function (success, error) { 131 if (!success) { 132 return; 133 } 134 var node = this; 135 if (node._constructs) { 136 node._invoke(success, [node._constructs]); 137 return; 138 } 139 140 // if someone else is already loading the constructs, just add the callbacks 141 node._constructLoadHandlers = node._constructLoadHandlers || []; 142 if (node._constructLoadHandlers.length > 0) { 143 node._constructLoadHandlers.push({success: success, error: error}); 144 return; 145 } 146 147 // we are the first to load the constructs, register the callbacks and 148 // trigger the ajax call 149 node._constructLoadHandlers.push({success: success, error: error}); 150 node._read(function () { 151 node._authAjax({ 152 url: GCN.settings.BACKEND_PATH + 153 '/rest/construct/list.json?nodeId=' + node.id(), 154 type: 'GET', 155 error: function (xhr, status, msg) { 156 var i; 157 for (i = 0; i < node._constructLoadHandlers.length; i++) { 158 GCN.handleHttpError(xhr, msg, node._constructLoadHandlers[i].error); 159 } 160 }, 161 success: function (response) { 162 var i; 163 if (GCN.getResponseCode(response) === 'OK') { 164 node._constructs = GCN.mapConstructs(response.constructs); 165 for (i = 0; i < node._constructLoadHandlers.length; i++) { 166 node._invoke(node._constructLoadHandlers[i].success, [node._constructs]); 167 } 168 } else { 169 for (i = 0; i < node._constructLoadHandlers.length; i++) { 170 GCN.handleResponseError(response, node._constructLoadHandlers[i].error); 171 } 172 } 173 }, 174 175 complete: function () { 176 node._constructLoadHandlers = []; 177 } 178 }); 179 }, error); 180 }, 181 182 /** 183 * Removes this node object. 184 * 185 * @ignore 186 * @param {function=} success Callback function to be invoked when 187 * this operation has completed 188 * successfully. 189 * @param {function(GCNError):boolean=} error Custom error handler. 190 */ 191 remove: function (success, error) { 192 GCN.handleError( 193 GCN.createError( 194 'NOT_YET_IMPLEMENTED', 195 'This method is not yet implemented', 196 this 197 ), 198 error 199 ); 200 }, 201 202 /** 203 * Saves the locally modified changes back to the system. 204 * This is currently not yet implemented. 205 * 206 * @ignore 207 * @param {function=} success Callback function to be invoked when 208 * this operation has completed 209 * successfully. 210 * @param {function(GCNError):boolean=} error Custom error handler. 211 */ 212 save: function (success, error) { 213 GCN.handleError( 214 GCN.createError( 215 'NOT_YET_IMPLEMENTED', 216 'This method is not yet implemented', 217 this 218 ), 219 error 220 ); 221 }, 222 223 /** 224 * Retrieves the top-level folders of this node's root folder. 225 * 226 * @function 227 * @name folders 228 * @memberOf NodeAPI 229 * @param {function(FolderAPI)=} success 230 * @param {function(GCNError):boolean=} error Custom error handler. 231 */ 232 '!folders': function (success, error) { 233 return this.folder(null, error).folders(success, error); 234 }, 235 236 /** 237 * Helper method that will load the constructs of this node. 238 * @ignore 239 * @private 240 * @this {NodeAPI} 241 * @param {function(Array.<object>)} success callback 242 * @param {function(GCNError):boolean=} error callback 243 */ 244 constructCategories: function (success, error) { 245 if (!success) { 246 return; 247 } 248 var node = this; 249 if (node._constructCategories) { 250 node._invoke(success, [node._constructCategories]); 251 } else { 252 node._read(function () { 253 node._data.id = node._chain._data.nodeId; 254 node.constructs(function (constructs) { 255 node._constructCategories = 256 mapConstructCategories(constructs); 257 node._invoke(success, [node._constructCategories]); 258 }, error); 259 }, error); 260 } 261 } 262 }); 263 264 /** 265 * Creates a new instance of NodeAPI. See the {@link NodeAPI} constructor for detailed information. 266 * 267 * @function 268 * @name node 269 * @memberOf GCN 270 * @see NodeAPI 271 */ 272 GCN.node = GCN.exposeAPI(NodeAPI); 273 GCN.NodeAPI = NodeAPI; 274 275 }(GCN)); 276