1 /*global GCN: true */
  2 
  3 (function (GCN) {
  4 
  5 	'use strict';
  6 
  7 	/**
  8 	 * Maps constructcategories that were fetched via the Rest API
  9 	 * into a sorted nested array of constructs.
 10 	 *
 11 	 * @param {object<string, object>} constructs
 12 	 * @return {object<string, object>}
 13 	 */
 14 	function mapConstructCategories(constructs) {
 15 		var constructKeyword;
 16 		var categoryMap = { categories: {}, categorySortorder: [] };
 17 		var constructCategoryArray = [];
 18 
 19 		for (constructKeyword in constructs) {
 20 			if (constructs.hasOwnProperty(constructKeyword)) {
 21 				var construct = constructs[constructKeyword];
 22 
 23 				var constructCategoryName = construct.category;
 24 				var categorySortorder = construct.categorySortorder;
 25 
 26 				// Use a custom name for constructs that have not been assigned 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].constructs[constructKeyword] = construct;
 44 			}
 45 		}
 46 
 47 		// Sort the categories by the sortorder
 48 		constructCategoryArray.sort(function (a, b) {
 49 			return a.sortorder - b.sortorder;
 50 		});
 51 
 52 		// Add the sorted category names to the sortorder field
 53 		var k;
 54 		for (k in constructCategoryArray) {
 55 			if (constructCategoryArray.hasOwnProperty(k)) {
 56 				var category = constructCategoryArray[k];
 57 				if (typeof category.sortorder !== 'undefined' && category.sortorder !== -1) {
 58 					categoryMap.categorySortorder.push(category.name);
 59 				}
 60 			}
 61 		}
 62 
 63 		return categoryMap;
 64 	}
 65 
 66 	/**
 67 	 * Maps constructs, that were fetched via the Rest API, using their keyword
 68 	 * as the keys.
 69 	 *
 70 	 * @param {object<string, object>} constructs Consturcts mapped against
 71 	 *                                            their id.
 72 	 * @return {object<string, object>} Constructs mapped against their keys.
 73 	 */
 74 	function mapConstructs(constructs) {
 75 		if (!constructs) {
 76 			return {};
 77 		}
 78 		var map = {};
 79 		var constructId;
 80 		for (constructId in constructs) {
 81 			if (constructs.hasOwnProperty(constructId)) {
 82 				map[constructs[constructId].keyword] = constructs[constructId];
 83 			}
 84 		}
 85 		return map;
 86 	}
 87 
 88 	/**
 89 	 * Node object.
 90 	 *
 91 	 * @name NodeAPI
 92 	 * @class
 93 	 * @augments Chainback
 94 	 */
 95 	var NodeAPI = GCN.defineChainback({
 96 		/** @lends NodeAPI */
 97 
 98 		__chainbacktype__: 'NodeAPI',
 99 		_extends: GCN.ContentObjectAPI,
100 		_type: 'node',
101 
102 		_data: {
103 			folderId: null
104 		},
105 
106 		/**
107 		 * @private
108 		 * @type {object<string, number} Constructs for this node are cached
109 		 *                               here so that we only need to fetch
110 		 *                               this once.
111 		 */
112 		_constructs: null,
113 
114 		/**
115 		 * @private
116 		 * @type {object<string, object} Constructs categories for this node.
117 		 *                               Cached here so that we only need to
118 		 *                               fetch this once.
119 		 */
120 		_constructCategories: null,
121 
122 		/**
123 		 * Retrieves a list of constructs and constructs categories that are
124 		 * assigned to this node and passes it as the only argument into the
125 		 * the `success()' callback.
126 		 *
127 		 * @param {function(Array.<object>)=} success Callback to receive an
128 		 *                                            array of constructs.
129 		 * @param {function(GCNError):boolean=} error Custom error handler.
130 		 * @return undefined
131 		 * @throws INVALID_ARGUMENTS
132 		 */
133 		constructs: function (success, error) {
134 			var that = this;
135 			if (!success) {
136 				GCN.error('INVALID_ARGUMENTS', 'the `constructs()\' method ' +
137 					'requires at least a success callback to be given');
138 			}
139 			if (this._constructs) {
140 				success(this._constructs);
141 			} else {
142 				var fork = this._fork();
143 				var fetchConstructs = function () {
144 					fork._authAjax({
145 						url: GCN.settings.BACKEND_PATH + '/rest/construct/list.json?nodeId=' + that.id(),
146 						type: 'GET',
147 						error: function (xhr, status, msg) {
148 							GCN.handleHttpError(xhr, msg, error);
149 						},
150 						success: function (response) {
151 							if (GCN.getResponseCode(response) === 'OK') {
152 								fork._constructs = mapConstructs(response.constructs);
153 								success(fork._constructs);
154 							} else {
155 								GCN.handleResponseError(response, error);
156 								fork._merge();
157 							}
158 						},
159 						complete: function () {
160 							fork._merge();
161 						}
162 					});
163 					return false;
164 				};
165 
166 				fork._read(fetchConstructs, error);
167 			}
168 		},
169 
170 		/**
171 		 * Removes this node object.
172 		 *
173 		 * @param {function=} success Callback.
174 		 * @param {function(GCNError):boolean=} error Custom error handler.
175 		 * @param {function} success callback
176 		 */
177 		remove: function (success, error) {
178 
179 		},
180 
181 		/**
182 		 * @FIXME: Is it really possible to save changes to a node?  If not,
183 		 *         then we should not surface this method.
184 		 */
185 		save: function () {},
186 
187 		/**
188 		 * Retreives the top-level folders of this node's root folder.
189 		 *
190 		 * @param {function(FolderAPI)=} success
191 		 * @param {function(GCNError):boolean=} error Custom error handler.
192 		 */
193 		'!folders': function (success, error) {
194 			return this.folder(null, error).folders(success, error);
195 		},
196 
197 		/**
198 		 * Helper method that will load the constructs of this node.
199 		 *
200 		 * @private
201 		 * @this {NodeAPI}
202 		 * @param {function(Array.<object>)} success callback
203 		 * @param {function(GCNError):boolean=} error callback
204 		 */
205 		constructCategories: function (success, error) {
206 			if (this._constructCategories) {
207 				success(this._constructCategories);
208 			} else {
209 				var that = this;
210 				this._continueWith(function (child) {
211 					that._data.id = child._data.nodeId;
212 
213 					that.constructs(function (constructs) {
214 						that._constructCategories = mapConstructCategories(constructs);
215 						success(that._constructCategories);
216 					}, error);
217 				}, error);
218 			}
219 		},
220 
221 		/**
222 		 * Internal method, to fetch this object's data from the server.
223 		 *
224 		 * @private
225 		 * @override
226 		 * @param {function(ContentObjectAPI)=} success Optional callback that
227 		 *                                              receives this object as
228 		 *                                              its only argument.
229 		 * @param {function(GCNError):boolean=} error Optional customer error
230 		 *                                            handler.
231 		 */
232 		'!_read': function (success, error) {
233 			if (this._fetched) {
234 				if (success) {
235 					success(this);
236 				}
237 
238 				return;
239 			}
240 
241 			var that = this;
242 			var id = this.id();
243 
244 			if (null === id || undefined === id) {
245 				this._getIdFromParent(function () {
246 					that._read(success, error);
247 				}, error);
248 
249 				return;
250 			}
251 
252 			// Note: we don't fetch data from the server,
253 			// because currently, there is no resource for nodes
254 			// As soon as there is a resource for nodes, we simply
255 			// can remove the whole overwritten function _read
256 			success(that);
257 		}
258 
259 	});
260 
261 	GCN.node = GCN.exposeAPI(NodeAPI);
262 	GCN.NodeAPI = NodeAPI;
263 
264 }(GCN));
265