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 	 * Maps constructs, that were fetched via the Rest API, using their keyword
 69 	 * as the keys.
 70 	 *
 71 	 * @param {object<string, object>} constructs Consturcts mapped against
 72 	 *                                            their id.
 73 	 * @return {object<string, object>} Constructs mapped against their keys.
 74 	 */
 75 	function mapConstructs(constructs) {
 76 		if (!constructs) {
 77 			return {};
 78 		}
 79 		var map = {};
 80 		var constructId;
 81 		for (constructId in constructs) {
 82 			if (constructs.hasOwnProperty(constructId)) {
 83 				map[constructs[constructId].keyword] = constructs[constructId];
 84 			}
 85 		}
 86 		return map;
 87 	}
 88 
 89 	/**
 90 	 * Represents a Node
 91 	 *
 92 	 * @name NodeAPI
 93 	 * @class
 94 	 * @augments Chainback
 95 	 * 
 96 	 * @param {number|string}
 97 	 *            id of the file to be loaded
 98 	 * @param {function(ContentObjectAPI))=}
 99 	 *            success Optional success callback that will receive this
100 	 *            object as its only argument.
101 	 * @param {function(GCNError):boolean=}
102 	 *            error Optional custom error handler.
103 	 * @param {object}
104 	 *            settings currently there are no additional settings to be used
105 	 */
106 	var NodeAPI = GCN.defineChainback({
107 		/** @lends NodeAPI */
108 
109 		__chainbacktype__: 'NodeAPI',
110 		_extends: GCN.ContentObjectAPI,
111 		_type: 'node',
112 
113 		_data: {
114 			folderId: null
115 		},
116 
117 		/**
118 		 * @private
119 		 * @type {object<string, number} Constructs for this node are cached
120 		 *                               here so that we only need to fetch
121 		 *                               this once.
122 		 */
123 		_constructs: null,
124 
125 		/**
126 		 * @private
127 		 * @type {object<string, object} Constructs categories for this node.
128 		 *                               Cached here so that we only need to
129 		 *                               fetch this once.
130 		 */
131 		_constructCategories: null,
132 
133 		/**
134 		 * Retrieves a list of constructs and constructs categories that are
135 		 * assigned to this node and passes it as the only argument into the
136 		 * the `success()' callback.
137 		 *
138 		 * @param {function(Array.<object>)=} success Callback to receive an
139 		 *                                            array of constructs.
140 		 * @param {function(GCNError):boolean=} error Custom error handler.
141 		 * @return undefined
142 		 * @throws INVALID_ARGUMENTS
143 		 */
144 		constructs: function (success, error) {
145 			if (!success) {
146 				return;
147 			}
148 			var node = this;
149 			if (node._constructs) {
150 				node._invoke(success, [node._constructs]);
151 				return;
152 			}
153 			node._read(function () {
154 				node._authAjax({
155 					url: GCN.settings.BACKEND_PATH +
156 					     '/rest/construct/list.json?nodeId=' + node.id(),
157 					type: 'GET',
158 					error: function (xhr, status, msg) {
159 						GCN.handleHttpError(xhr, msg, error);
160 					},
161 					success: function (response) {
162 						if (GCN.getResponseCode(response) === 'OK') {
163 							node._constructs = mapConstructs(response.constructs);
164 							node._invoke(success, [node._constructs]);
165 						} else {
166 							GCN.handleResponseError(response, error);
167 						}
168 					}
169 				});
170 			}, error);
171 		},
172 
173 		/**
174 		 * Removes this node object.
175 		 *
176 		 * @ignore
177 		 * @param {function=} success Callback function to be invoked when
178 		 *                            this operation has completed
179 		 *                            successfully.
180 		 * @param {function(GCNError):boolean=} error Custom error handler.
181 		 */
182 		remove: function (success, error) {
183 			GCN.handleError(
184 				GCN.error('NOT_YET_IMPLEMENTED',
185 						  'This method is not yet implemented', this),
186 				error
187 			);
188 		},
189 
190 		/**
191 		 * Saves the locally modified changes back to the system.
192 		 * This is currently not yet implemented.
193 		 * 
194 		 * @ignore
195 		 * @param {function=} success Callback function to be invoked when
196 		 *                            this operation has completed
197 		 *                            successfully.
198 		 * @param {function(GCNError):boolean=} error Custom error handler.
199 		 */
200 		save: function (success, error) {
201 			GCN.handleError(
202 				GCN.error('NOT_YET_IMPLEMENTED',
203 						  'This method is not yet implemented', this),
204 				error
205 			);
206 		},
207 
208 		/**
209 		 * Retrieves the top-level folders of this node's root folder.
210 		 *
211 		 * @function
212 		 * @name folders
213 		 * @memberOf NodeAPI
214 		 * @param {function(FolderAPI)=} success
215 		 * @param {function(GCNError):boolean=} error Custom error handler.
216 		 */
217 		'!folders': function (success, error) {
218 			return this.folder(null, error).folders(success, error);
219 		},
220 
221 		/**
222 		 * Helper method that will load the constructs of this node.
223 		 * @ignore
224 		 * @private
225 		 * @this {NodeAPI}
226 		 * @param {function(Array.<object>)} success callback
227 		 * @param {function(GCNError):boolean=} error callback
228 		 */
229 		constructCategories: function (success, error) {
230 			if (!success) {
231 				return;
232 			}
233 			var node = this;
234 			if (node._constructCategories) {
235 				node._invoke(success, [node._constructCategories]);
236 			} else {
237 				node._read(function () {
238 					node._data.id = node._chain._data.nodeId;
239 					node.constructs(function (constructs) {
240 						node._constructCategories =
241 								mapConstructCategories(constructs);
242 						node._invoke(success, [node._constructCategories]);
243 					}, error);
244 				}, error);
245 			}
246 		}
247 	});
248 
249 	/**
250 	* Creates a new instance of NodeAPI. See the {@link NodeAPI} constructor for detailed information.
251 	* 
252 	* @function
253 	* @name node
254 	* @memberOf GCN
255 	* @see NodeAPI
256 	*/
257 	GCN.node = GCN.exposeAPI(NodeAPI);
258 	GCN.NodeAPI = NodeAPI;
259 
260 }(GCN));
261