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 				this._invoke(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 								fork._invoke(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 			GCN.error('NOT_YET_IMPLEMENTED', "This method is not yet implemented");
179 		},
180 
181 		/**
182 		 * Saves the locally modified changes back to the system. 
183 		 * This is currently not yet implemented.
184 		 * 
185 		 * @public
186 		 * @override
187 		 */
188 		save: function () {
189 			GCN.error('NOT_YET_IMPLEMENTED', "This method is not yet implemented");
190 		},
191 
192 		/**
193 		 * Retrieves the top-level folders of this node's root folder.
194 		 *
195 		 * @param {function(FolderAPI)=} success
196 		 * @param {function(GCNError):boolean=} error Custom error handler.
197 		 */
198 		'!folders': function (success, error) {
199 			return this.folder(null, error).folders(success, error);
200 		},
201 
202 		/**
203 		 * Helper method that will load the constructs of this node.
204 		 *
205 		 * @private
206 		 * @this {NodeAPI}
207 		 * @param {function(Array.<object>)} success callback
208 		 * @param {function(GCNError):boolean=} error callback
209 		 */
210 		constructCategories: function (success, error) {
211 			if (this._constructCategories) {
212 				this._invoke(success, [this._constructCategories]);
213 			} else {
214 				var that = this;
215 				this._read(function () {
216 					that._data.id = that._data.nodeId; //child._data.nodeId
217 					that.constructs(function (constructs) {
218 						that._constructCategories
219 							= mapConstructCategories(constructs);
220 						that._invoke(success, [that._constructCategories]);
221 					}, error);
222 				}, error);
223 			}
224 		}
225 
226 	});
227 
228 	GCN.node = GCN.exposeAPI(NodeAPI);
229 	GCN.NodeAPI = NodeAPI;
230 
231 }(GCN));
232