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 		 * @private
105 		 * @type {object<string, object} Constructs categories for this node.
106 		 *                               Cached here so that we only need to
107 		 *                               fetch this once.
108 		 */
109 		_constructCategories: null,
110 
111 		/**
112 		 * Retrieves a list of constructs and constructs categories that are
113 		 * assigned to this node and passes it as the only argument into the
114 		 * the `success()' callback.
115 		 *
116 		 * @param {function(Array.<object>)=} success Callback to receive an
117 		 *                                            array of constructs.
118 		 * @param {function(GCNError):boolean=} error Custom error handler.
119 		 * @return undefined
120 		 * @throws INVALID_ARGUMENTS
121 		 */
122 		constructs: function (success, error) {
123 			if (!success) {
124 				return;
125 			}
126 			var node = this;
127 			if (node._constructs) {
128 				node._invoke(success, [node._constructs]);
129 				return;
130 			}
131 			node._read(function () {
132 				node._authAjax({
133 					url: GCN.settings.BACKEND_PATH +
134 					     '/rest/construct/list.json?nodeId=' + node.id(),
135 					type: 'GET',
136 					error: function (xhr, status, msg) {
137 						GCN.handleHttpError(xhr, msg, error);
138 					},
139 					success: function (response) {
140 						if (GCN.getResponseCode(response) === 'OK') {
141 							node._constructs = GCN.mapConstructs(response.constructs);
142 							node._invoke(success, [node._constructs]);
143 						} else {
144 							GCN.handleResponseError(response, error);
145 						}
146 					}
147 				});
148 			}, error);
149 		},
150 
151 		/**
152 		 * Removes this node object.
153 		 *
154 		 * @ignore
155 		 * @param {function=} success Callback function to be invoked when
156 		 *                            this operation has completed
157 		 *                            successfully.
158 		 * @param {function(GCNError):boolean=} error Custom error handler.
159 		 */
160 		remove: function (success, error) {
161 			GCN.handleError(
162 				GCN.createError(
163 					'NOT_YET_IMPLEMENTED',
164 					'This method is not yet implemented',
165 					this
166 				),
167 				error
168 			);
169 		},
170 
171 		/**
172 		 * Saves the locally modified changes back to the system.
173 		 * This is currently not yet implemented.
174 		 * 
175 		 * @ignore
176 		 * @param {function=} success Callback function to be invoked when
177 		 *                            this operation has completed
178 		 *                            successfully.
179 		 * @param {function(GCNError):boolean=} error Custom error handler.
180 		 */
181 		save: function (success, error) {
182 			GCN.handleError(
183 				GCN.createError(
184 					'NOT_YET_IMPLEMENTED',
185 					'This method is not yet implemented',
186 					this
187 				),
188 				error
189 			);
190 		},
191 
192 		/**
193 		 * Retrieves the top-level folders of this node's root folder.
194 		 *
195 		 * @function
196 		 * @name folders
197 		 * @memberOf NodeAPI
198 		 * @param {function(FolderAPI)=} success
199 		 * @param {function(GCNError):boolean=} error Custom error handler.
200 		 */
201 		'!folders': function (success, error) {
202 			return this.folder(null, error).folders(success, error);
203 		},
204 
205 		/**
206 		 * Helper method that will load the constructs of this node.
207 		 * @ignore
208 		 * @private
209 		 * @this {NodeAPI}
210 		 * @param {function(Array.<object>)} success callback
211 		 * @param {function(GCNError):boolean=} error callback
212 		 */
213 		constructCategories: function (success, error) {
214 			if (!success) {
215 				return;
216 			}
217 			var node = this;
218 			if (node._constructCategories) {
219 				node._invoke(success, [node._constructCategories]);
220 			} else {
221 				node._read(function () {
222 					node._data.id = node._chain._data.nodeId;
223 					node.constructs(function (constructs) {
224 						node._constructCategories =
225 								mapConstructCategories(constructs);
226 						node._invoke(success, [node._constructCategories]);
227 					}, error);
228 				}, error);
229 			}
230 		}
231 	});
232 
233 	/**
234 	* Creates a new instance of NodeAPI. See the {@link NodeAPI} constructor for detailed information.
235 	* 
236 	* @function
237 	* @name node
238 	* @memberOf GCN
239 	* @see NodeAPI
240 	*/
241 	GCN.node = GCN.exposeAPI(NodeAPI);
242 	GCN.NodeAPI = NodeAPI;
243 
244 }(GCN));
245