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.error('NOT_YET_IMPLEMENTED',
163 						  'This method is not yet implemented', this),
164 				error
165 			);
166 		},
167 
168 		/**
169 		 * Saves the locally modified changes back to the system.
170 		 * This is currently not yet implemented.
171 		 * 
172 		 * @ignore
173 		 * @param {function=} success Callback function to be invoked when
174 		 *                            this operation has completed
175 		 *                            successfully.
176 		 * @param {function(GCNError):boolean=} error Custom error handler.
177 		 */
178 		save: function (success, error) {
179 			GCN.handleError(
180 				GCN.error('NOT_YET_IMPLEMENTED',
181 						  'This method is not yet implemented', this),
182 				error
183 			);
184 		},
185 
186 		/**
187 		 * Retrieves the top-level folders of this node's root folder.
188 		 *
189 		 * @function
190 		 * @name folders
191 		 * @memberOf NodeAPI
192 		 * @param {function(FolderAPI)=} success
193 		 * @param {function(GCNError):boolean=} error Custom error handler.
194 		 */
195 		'!folders': function (success, error) {
196 			return this.folder(null, error).folders(success, error);
197 		},
198 
199 		/**
200 		 * Helper method that will load the constructs of this node.
201 		 * @ignore
202 		 * @private
203 		 * @this {NodeAPI}
204 		 * @param {function(Array.<object>)} success callback
205 		 * @param {function(GCNError):boolean=} error callback
206 		 */
207 		constructCategories: function (success, error) {
208 			if (!success) {
209 				return;
210 			}
211 			var node = this;
212 			if (node._constructCategories) {
213 				node._invoke(success, [node._constructCategories]);
214 			} else {
215 				node._read(function () {
216 					node._data.id = node._chain._data.nodeId;
217 					node.constructs(function (constructs) {
218 						node._constructCategories =
219 								mapConstructCategories(constructs);
220 						node._invoke(success, [node._constructCategories]);
221 					}, error);
222 				}, error);
223 			}
224 		}
225 	});
226 
227 	/**
228 	* Creates a new instance of NodeAPI. See the {@link NodeAPI} constructor for detailed information.
229 	* 
230 	* @function
231 	* @name node
232 	* @memberOf GCN
233 	* @see NodeAPI
234 	*/
235 	GCN.node = GCN.exposeAPI(NodeAPI);
236 	GCN.NodeAPI = NodeAPI;
237 
238 }(GCN));
239