1 /*global global: true, process: true, require: true, module: true */
  2 
  3 /**
  4  * Factory to generate the `GCN' object and expose it to the global context.
  5  * In browsers the global context will be the `window' object.  In nodejs, it
  6  * will be `global'.
  7  */
  8 GCN = (function (global) {
  9 
 10 	'use strict';
 11 
 12 	// Check whether we are in nodeJS context.
 13 	if (typeof process !== 'undefined' && process.versions &&
 14 			process.versions.node) {
 15 		global.isNode = true;
 16 
 17 		var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
 18 
 19 		// Expose jQuery into the global scope.
 20 		global.$ = global.jQuery = require('jquery');
 21 
 22 		// Overwrite jQuery's transport.
 23 		global.jQuery.ajaxSettings.xhr = function createNodeXHRForGCN() {
 24 			return new XMLHttpRequest();
 25 		};
 26 
 27 		// http://stackoverflow.com/a/6432602
 28 		global.jQuery.support.cors = true;
 29 	}
 30 
 31 	/**
 32 	 * @private
 33 	 * @type {boolean} A flag to indicate whether or not a handler has been
 34 	 *                 registerd through the `GCN.onRender()' function.
 35 	 */
 36 	var hasOnRenderHandler = false;
 37 
 38 	/**
 39 	 * @private
 40 	 * @type {boolean} A flag to indicate whether or not a handler has been
 41 	 *                 registerd through the `GCN.onError()' function.
 42 	 */
 43 	var hasOnErrorHandler = false;
 44 
 45 	/**
 46 	 * @type {boolean} An internal flag that stores whether an authentication
 47 	 *                 handler has been set.
 48 	 */
 49 	var hasAuthenticationHandler = false;
 50 
 51 	/**
 52 	 * GCN JS API error object.  This is the object passed to error handlers.
 53 	 *
 54 	 * @class GCNError
 55 	 * @param {string} code
 56 	 * @param {string} message
 57 	 * @param {object} data
 58 	 */
 59 	var GCNError = function (code, message, data) {
 60 		this.code    = code;
 61 		this.message = message;
 62 		this.data    = data;
 63 	};
 64 
 65 	/**
 66 	 * Returns a human-readable representation of this error object.
 67 	 *
 68 	 * @public
 69 	 * @return {string}
 70 	 */
 71 	GCNError.prototype.toString = function () {
 72 		return 'GCN ERROR (' + this.code + '): "' + (this.message || '') + '"';
 73 	};
 74 
 75 	/**
 76 	 * @name GCN
 77 	 * @class
 78 	 *
 79 	 * Base namespace for the Gentics Content.Node JavaScript API.
 80 	 */
 81 	var GCN = global.GCN || {};
 82 
 83 	jQuery.extend(GCN, {
 84 		/** @lends GCN */
 85 
 86 		/**
 87 		 * @type {object} Reference to the global context.
 88 		 */
 89 		global: global,
 90 
 91 		/**
 92 		 * @type {object<string, string>} Settings for the Gentics Content.Node JavaScript API.
 93 		 */
 94 		settings: {
 95 
 96 			/**
 97 			 * @name settings.lang
 98 			 * @memberOf GCN
 99 			 * @type {string} The language code with which to render tags.
100 			 */
101 			lang: 'en',
102 
103 			/**
104 			 * @const
105 			 * @name settings.BACKEND_PATH
106 			 * @memberOf GCN
107 			 * @type {string} Default GCN backend path. Do not add a trailing
108 			 *                slash here.
109 			 */
110 			BACKEND_PATH: '/CNPortletapp',
111 
112 			/**
113 			 * @const
114 			 * @name settings.MAGIC_LINK
115 			 * @memberOf GCN
116 			 * @type {string} The keyword for the construct that defines Aloha
117 			 *                Editor links.  In most Content.Node installations
118 			 *                this will be "gtxalohapagelink", but can be
119 			 *                otherwise defined.
120 			 */
121 			MAGIC_LINK: 'gtxalohapagelink',
122 
123 			/**
124 			 * @const
125 			 * @name settings.linksRenderMode
126 			 * @memberOf GCN
127 			 * @type {string} Determines whether links will be rendered as
128 			 *                back-end urls or front-end urls.  Can either be
129 			 *                set to "backend" or "frontend".
130 			 */
131 			linksRenderMode: 'backend',
132 
133 			/**
134 			 * @memberOf GCN
135 			 * @default false
136 			 * @type {bool|int|string} Set a channelid to work on for multichannelling
137 			 *							or false if no channel should be used
138 			 */
139 			channel: false
140 		},
141 
142 		/**
143 		 * Publish a message
144 		 *
145 		 * @param {string} message channel name
146 		 * @param {*=} params
147 		 */
148 		pub: function (channel, params) {
149 			if (!hasOnErrorHandler && channel === 'error-encountered') {
150 				// throw an error if there is no subscription to
151 				// error-encountered.
152 				throw params;
153 			}
154 
155 			jQuery(GCN).trigger(channel, params);
156 		},
157 
158 		/**
159 		 * Subscribe to a message channel
160 		 *
161 		 * @param {string} message channel name
162 		 * @param {function} handler function - message parameters will be passed
163 		 */
164 		sub: function (channel, handler) {
165 			// register default handlers
166 			switch (channel) {
167 			case 'error-encountered':
168 				hasOnErrorHandler = true;
169 				break;
170 			case 'content-rendered':
171 				hasOnRenderHandler = true;
172 				break;
173 			case 'authentication-required':
174 				hasAuthenticationHandler = true;
175 				break;
176 			}
177 
178 			jQuery(GCN).bind(channel, function (event, param1, param2, param3) {
179 				handler(param1, param2, param3);
180 			});
181 		},
182 
183 		/**
184 		 * Tigger an error message 'error-encountered'
185 		 *
186 		 * @param {string} error code
187 		 * @param {string} error message
188 		 * @param {object} additional error data
189 		 */
190 		error: function (code, message, data) {
191 			var error = new GCNError(code, message, data);
192 			this.pub('error-encountered', error);
193 		},
194 
195 		/**
196 		 * Returns an object containing the formal error fields.  The object
197 		 * contains a `toString' method to print any uncaught exceptions nicely.
198 		 *
199 		 * @param {string} code
200 		 * @param {string} message
201 		 * @param {object} data
202 		 * @return {GCNError}
203 		 */
204 		createError: function (code, message, data) {
205 			return new GCNError(code, message, data);
206 		},
207 
208 		/**
209 		 * Wraps the `jQuery.ajax()' method.
210 		 *
211 		 * @public
212 		 * @param {object} settings
213 		 * @throws HTTP_ERROR
214 		 */
215 		ajax: function (settings) {
216 			if (settings.json) {
217 				settings.data = JSON.stringify(settings.json);
218 				delete settings.json;
219 			}
220 			settings.dataType = 'json';
221 			settings.contentType = 'application/json; charset=utf-8';
222 			jQuery.ajax(settings);
223 		},
224 
225 		/**
226 		 * set links render mode if a parameter is given
227 		 * retrieve it if not
228 		 *
229 		 * @param {string} mode
230 		 * @return {string} mode
231 		 */
232 		linksRenderMode: function (mode) {
233 			if (mode) {
234 				GCN.settings.linksRenderMode = mode;
235 			}
236 			return GCN.settings.linksRenderMode;
237 		},
238 
239 		/**
240 		 * Set channel if a parameter is given retrieve it otherwise.
241 		 *
242 		 * If you don't want to work on a channel just set it to false, which
243 		 * is the default value.
244 		 *
245 		 * @param {string|boolean} channel The id of the channel to be set.
246 		 * @return {string} current channel id.
247 		 */
248 		channel: function (channel) {
249 			if (channel || false === channel) {
250 				GCN.settings.channel = channel;
251 			}
252 			return GCN.settings.channel;
253 		},
254 
255 		/**
256 		 * @param {string} html Rendered content
257 		 * @param {Chainback} contentObject The ContentObject which was rendered.
258 		 * @param {function(html)} callback Receives the processed html.
259 		 */
260 		_handleContentRendered: function (html, contentObject, callback) {
261 			if (hasOnRenderHandler) {
262 				GCN.pub('content-rendered', [html, contentObject, callback]);
263 			} else {
264 				callback(html);
265 			}
266 		},
267 
268 		/**
269 		 * Handles the ajax transport error.  It will invoke the custom error
270 		 * handler if one is provided, and propagate the error onto the global
271 		 * handler if the an error handler does not return `false'.
272 		 *
273 		 * @param {object} xhr
274 		 * @param {string} msg The error message
275 		 * @param {function} handler Custom error handler.
276 		 * @throws HTTP_ERROR
277 		 */
278 		handleHttpError: function (xhr, msg, handler) {
279 			var throwException = true;
280 
281 			if (handler) {
282 				throwException = handler(GCN.createError('HTTP_ERROR', msg,
283 					xhr));
284 			}
285 
286 			if (throwException !== 'false') {
287 				GCN.error('HTTP_ERROR', msg, xhr);
288 			}
289 		},
290 
291 		/**
292 		 * Handles error that occur when an ajax request succeeds but the
293 		 * backend responds with an error.
294 		 *
295 		 * @param {object} reponse The REST API response object.
296 		 * @param {function(GCNError):boolean} handler Custom error handler.
297 		 */
298 		handleResponseError: function (response, handler) {
299 			var info = response.responseInfo;
300 			var throwException = true;
301 
302 			if (handler) {
303 				throwException = handler(GCN.createError(
304 					info.responseCode,
305 					info.responseMessage,
306 					response
307 				));
308 			}
309 
310 			if (throwException !== false) {
311 				GCN.error(info.responseCode, info.responseMessage, response);
312 			}
313 		},
314 
315 		/**
316 		 * Tiggers the GCN error event.
317 		 *
318 		 * @param {GCNError} error
319 		 * @param {function(GCNError):boolean} handler Custom error handler.
320 		 * @return {boolean} Whether or not to the exception was thrown.
321 		 */
322 		handleError: function (error, handler) {
323 			var throwException = true;
324 
325 			if (handler) {
326 				throwException = handler(error);
327 			}
328 
329 			if (throwException !== false) {
330 				GCN.error(error.code, error.message, error.data);
331 			}
332 
333 			return throwException;
334 		},
335 
336 		/**
337 		 * Check if an authentication handler has been registered.
338 		 *
339 		 * @return {boolean} True if an handler for the
340 		 *                   'authentication-required' message has been
341 		 *                   registered.
342 		 */
343 		_hasAuthenticationHandler: function () {
344 			return hasAuthenticationHandler;
345 		}
346 
347 	});
348 
349 	// Expose the Gentics Content.Node JavaScript API to the global context.  This will be `window' in
350 	// most cases.
351 	return (global.GCN = GCN);
352 
353 }(typeof global !== 'undefined' ? global : window));
354