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