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