UNPKG

21.7 kBJavaScriptView Raw
1/*------------------------------------*\
2 ROUTES
3\*------------------------------------*/
4/* jslint node: true */
5
6/**
7 * Add route to express.
8 * @private
9 * @function loadRoutes
10 * @memberOf NA~
11 * @param {NA} NA NodeAtlas instance.
12 */
13function loadRoutes(NA) {
14 /* For each `webconfig.routes`. */
15 NA.forEach(NA.webconfig.routes, function (currentUrl) {
16 NA.request(currentUrl, NA.webconfig.routes);
17 });
18}
19
20/**
21 * Crawl all routes and execute each file with response requested by client after passed into `NA#controllers[].setRoutes` hook.
22 * @private
23 * @function initRoutes
24 * @memberOf NA#
25 * @this NA
26 */
27exports.initRoutes = function () {
28 var NA = this,
29 runIndexPage = function () {
30 /* Only if server was started and `index` is set to « true ». */
31 if (
32
33 /**
34 * Allow NodeAtlas to create a root page with a link for each routes if set to true.
35 * @public
36 * @alias index
37 * @type {boolean}
38 * @memberOf NA#webconfig
39 * @default false
40 */
41 NA.webconfig.index) {
42 NA.indexPage();
43 }
44 };
45
46 if (typeof NA.controllers[NA.webconfig.controller] !== 'undefined' &&
47 typeof NA.controllers[NA.webconfig.controller].setRoutes !== 'undefined') {
48
49 /**
50 * Set all routes before the index was started.
51 * @function setRoutes
52 * @memberOf NA#controllers[]
53 * @this NA
54 * @param {NA~callback} next Next steps after routes are setted.
55 */
56 NA.controllers[NA.webconfig.controller].setRoutes.call(NA, function () {
57 runIndexPage();
58 loadRoutes(NA);
59 NA.pageNotFound();
60 });
61
62 /* ...else, just continue. */
63 } else {
64 runIndexPage();
65 loadRoutes(NA);
66 NA.pageNotFound();
67 }
68};
69
70/**
71 * Create a « Overview » page to « / » url with all of page accessible via links.
72 * @private
73 * @function indexPage
74 * @memberOf NA#
75 * @this NA
76 */
77exports.indexPage = function () {
78 var NA = this,
79 url = NA.modules.url,
80 path = NA.modules.path,
81 opn = NA.modules.opn,
82 urlOutput = url.resolve(NA.webconfig.urlRoot, path.join(NA.webconfig.urlRelativeSubPath, ((typeof NA.configuration.browse === 'string') ? NA.configuration.browse : "")));
83
84 /* Create a new path to « / ». Erase the route to « / » defined into `routes`. */
85 NA.express.get(url.format(path.join("/", NA.webconfig.urlRelativeSubPath, "/")), function (request, response) {
86 var data = {},
87 matches = function (regex, matches) { return data[matches]; };
88
89 data.render = "";
90
91 /* List all routes... */
92 NA.forEach(NA.webconfig.routes, function (page) {
93 var routeParameters;
94
95 if (typeof page !== "string") {
96 routeParameters = page;
97 } else {
98 routeParameters = NA.webconfig.routes[page];
99 }
100
101 data.page = page;
102 if (routeParameters.url) {
103 data.page = routeParameters.url;
104 }
105
106 if (routeParameters.output !== false) {
107 data.page = decodeURIComponent(data.page);
108 data.render += NA.cliLabels.indexPage.line.replace(/%([\-a-zA-Z0-9_]+)%/g, matches);
109 }
110 });
111
112 /* ...and provide a page. */
113 response.writeHead(200, NA.cliLabels.indexPage.charsetAndType);
114 response.end(NA.cliLabels.indexPage.data.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
115 });
116
117 /* Display index after all routes are setted. */
118 if (NA.configuration.browse) {
119 opn(urlOutput);
120 }
121};
122
123/**
124 * Set the public directory for asset like CSS/JS and media.
125 * @private
126 * @function initStatics
127 * @memberOf NA#
128 * @this NA
129 */
130exports.initStatics = function () {
131 var NA = this,
132 express = NA.modules.express,
133 path = NA.modules.path,
134 url = NA.modules.url,
135 staticOptions = { maxAge: 86400000 * 30 };
136
137 /* No Cache */
138 if (!NA.webconfig.cache) {
139 staticOptions.etag = false;
140 staticOptions.maxAge = 0;
141 staticOptions.lastModified = false;
142 }
143
144 /**
145 * Set information for static file from `assetsRelativePath`.
146 * @public
147 * @see http://expressjs.com/api.html#express.static for options.
148 * @alias staticOptions
149 * @type {string}
150 * @memberOf NA#webconfig
151 */
152 staticOptions = NA.extend(staticOptions, NA.webconfig.staticOptions);
153
154 NA.express.use(NA.webconfig.urlRelativeSubPath, express.static(path.join(NA.serverPath, NA.webconfig.assetsRelativePath), staticOptions));
155
156 NA.forEach(NA.webconfig.statics, function (directory, directories) {
157 var virtual = directory,
158 real = directories[directory],
159 options;
160
161 if (NA.webconfig.statics instanceof Array) {
162 virtual = directory.virtual;
163 real = directory;
164 }
165
166 if (typeof real === "object") {
167 options = real.staticOptions || staticOptions;
168 real = real.path;
169 }
170
171 NA.express.use(url.format(path.join(NA.webconfig.urlRelativeSubPath, virtual)), express.static(path.join(NA.serverPath, real), options));
172 });
173};
174
175/**
176 * Affect support of GET, POST, UPDATE, DELETE and OPTIONS to a route.
177 * @private
178 * @function setSupport
179 * @memberOf NA~
180 * @param {boolean} support Type of support GET, POST, PUT, DELETE or OPTIONS.
181 * @param {boolean} path Instruction support for all page.
182 * @param {boolean} options Instruction support for current page.
183 * @return {boolean} if support is effective;
184 */
185function setSupport(support, common, specific) {
186
187 /* Manage GET / POST / PUT / DELETE / OPTIONS support for an url. */
188 if (common === false || common === "false") {
189 support = false;
190 }
191
192 if (common === true || common === "true") {
193 support = true;
194 }
195
196 if (specific === false || specific === "false") {
197 support = false;
198 }
199
200 if (specific === true || specific === "true") {
201 support = true;
202 }
203
204 return support;
205}
206
207/**
208 * Set parameters authorization.
209 * @private
210 * @function supportGet
211 * @memberOf NA~
212 * @param {NA} NA NodeAtlas instance.
213 * @param {Object} routeParameters Parameters set into `routes[<currentRoute>]`.
214 * @param {boolean} getSupport Represent initial state of support.
215 * @return {boolean} if support is effective;
216 */
217function supportGet(NA, routeParameters, getSupport) {
218 return setSupport(getSupport,
219
220 /**
221 * Allow you to avoid or authorize GET response for all page.
222 * @public
223 * @alias get
224 * @type {boolean}
225 * @memberOf NA#webconfig
226 * @default true
227 */
228 NA.webconfig.get,
229
230 /**
231 * Allow you to avoid or authorize GET response for current page.
232 * @public
233 * @alias get
234 * @type {boolean}
235 * @memberOf NA#locals.routeParameters
236 * @default true
237 */
238 routeParameters.get
239 );
240}
241
242/**
243 * Set parameters authorization.
244 * @private
245 * @function supportPost
246 * @memberOf NA~
247 * @param {NA} NA NodeAtlas instance.
248 * @param {Object} routeParameters Parameters set into `routes[<currentRoute>]`.
249 * @param {boolean} postSupport Represent initial state of support.
250 * @return {boolean} if support is effective;
251 */
252function supportPost(NA, routeParameters, postSupport) {
253 return setSupport(postSupport,
254
255 /**
256 * Allow you to avoid or authorize POST response for all page.
257 * @public
258 * @alias post
259 * @type {boolean}
260 * @memberOf NA#webconfig
261 * @default true
262 */
263 NA.webconfig.post,
264
265 /**
266 * Allow you to avoid or authorize POST response for current page.
267 * @public
268 * @alias post
269 * @type {boolean}
270 * @memberOf NA#locals.routeParameters
271 * @default true
272 */
273 routeParameters.post
274 );
275}
276
277/**
278 * Set parameters authorization.
279 * @private
280 * @function supportPut
281 * @memberOf NA~
282 * @param {NA} NA NodeAtlas instance.
283 * @param {Object} routeParameters Parameters set into `routes[<currentRoute>]`.
284 * @param {boolean} putSupport Represent initial state of support.
285 * @return {boolean} if support is effective;
286 */
287function supportPut(NA, routeParameters, putSupport) {
288 return setSupport(putSupport,
289
290 /**
291 * Allow you to avoid or authorize PUT response for all page.
292 * @public
293 * @alias put
294 * @type {boolean}
295 * @memberOf NA#webconfig
296 * @default false
297 */
298 NA.webconfig.put,
299
300 /**
301 * Allow you to avoid or authorize PUT response for current page.
302 * @public
303 * @alias put
304 * @type {boolean}
305 * @memberOf NA#locals.routeParameters
306 * @default false
307 */
308 routeParameters.put
309 );
310}
311
312/**
313 * Set parameters authorization.
314 * @private
315 * @function supportDelete
316 * @memberOf NA~
317 * @param {NA} NA NodeAtlas instance.
318 * @param {Object} routeParameters Parameters set into `routes[<currentRoute>]`.
319 * @param {boolean} deleteSupport Represent initial state of support.
320 * @return {boolean} if support is effective;
321 */
322function supportDelete(NA, routeParameters, deleteSupport) {
323 return setSupport(deleteSupport,
324
325 /**
326 * Allow you to avoid or authorize DELETE response for all page.
327 * @public
328 * @alias delete
329 * @type {boolean}
330 * @memberOf NA#webconfig
331 * @default false
332 */
333 NA.webconfig.delete,
334
335 /**
336 * Allow you to avoid or authorize DELETE response for current page.
337 * @public
338 * @alias delete
339 * @type {boolean}
340 * @memberOf NA#locals.routeParameters
341 * @default false
342 */
343 routeParameters.delete
344 );
345}
346
347/**
348 * Set parameters authorization.
349 * @private
350 * @function supportOptions
351 * @memberOf NA~
352 * @param {NA} NA NodeAtlas instance.
353 * @param {Object} routeParameters Parameters set into `routes[<currentRoute>]`.
354 * @param {boolean} optionsSupport Represent initial state of support.
355 * @return {boolean} if support is effective;
356 */
357function supportOptions(NA, routeParameters, optionsSupport) {
358 return setSupport(optionsSupport,
359
360 /**
361 * Allow you to avoid or authorize OPTIONS response for all page.
362 * @public
363 * @alias options
364 * @type {boolean}
365 * @memberOf NA#webconfig
366 * @default false
367 */
368 NA.webconfig.options,
369
370 /**
371 * Allow you to avoid or authorize OPTIONS response for current page.
372 * @public
373 * @alias options
374 * @type {boolean}
375 * @memberOf NA#locals.routeParameters
376 * @default false
377 */
378 routeParameters.options
379 );
380}
381
382/**
383 * Listen a specific request.
384 * @private
385 * @function request
386 * @memberOf NA#
387 * @this NA
388 * @param {string} path The url listening.
389 * @param {Object} options Options associate to this url.
390 */
391exports.request = function (path, options) {
392 var NA = this,
393 routeParameters,
394 getSupport = true,
395 postSupport = true,
396 putSupport = false,
397 deleteSupport = false,
398 optionsSupport = false,
399 objectPath;
400
401 /* Case of `path` is an object because `NA.webconfig.routes` is an array and not an object. */
402 if (typeof path === "object") {
403 routeParameters = path;
404 } else {
405 routeParameters = options[path];
406 }
407
408 /* Adding of subfolder before url listening. */
409 objectPath = NA.webconfig.urlRelativeSubPath + ((
410
411 /**
412 * If setted, replace « The url listening ». « The url listening. » become a « key » value.
413 * @public
414 * @alias url
415 * @type {string}
416 * @memberOf NA#locals.routeParameters
417 */
418 routeParameters.url) ? routeParameters.url : path);
419
420 /* Manage GET / POST / PUT / DELETE / OPTIONS support for an url. */
421 getSupport = supportGet(NA, routeParameters, getSupport);
422 postSupport = supportPost(NA, routeParameters, postSupport);
423 putSupport = supportPut(NA, routeParameters, putSupport);
424 deleteSupport = supportDelete(NA, routeParameters, deleteSupport);
425 optionsSupport = supportOptions(NA, routeParameters, optionsSupport);
426
427 /* Ask for a page in GET, POST, PUT or DELETE. */
428 NA.requestRegex({
429 getSupport: getSupport,
430 postSupport: postSupport,
431 putSupport: putSupport,
432 deleteSupport: deleteSupport,
433 optionsSupport: optionsSupport
434 }, objectPath, options, path, routeParameters);
435};
436
437/**
438 * Listen a specific request (Regex Part).
439 * @private
440 * @function requestRegex
441 * @memberOf NA#
442 * @param {Object} support Contain all GET, POST, PUT, DELETE or OPTIONS capability.
443 * @param {boolean} support.getSupport This page can be requested by GET ?
444 * @param {boolean} support.postSupport This page can be requested by POST ?
445 * @param {boolean} support.putSupport This page can be requested by PUT ?
446 * @param {boolean} support.deleteSupport This page can be requested by DELETE ?
447 * @param {boolean} support.optionsSupport This page can be requested by OPTIONS ?
448 * @param {string} objectPath The list of Url match for obtain response.
449 * @param {Object} options Option associate to this url.
450 * @param {string} path The Url in routes' webconfig.
451 * @param {Object} routeParameters Parameters for this route.
452 */
453exports.requestRegex = function (support, objectPath, options, path, routeParameters) {
454 var NA = this;
455
456 /* Allow you to use regex into your url route... */
457 if (
458 /**
459 * Use RegExp expression as selector for route url If setted to true.
460 * Same if is a string but string represent option like "g".
461 * @public
462 * @alias regExp
463 * @type {string|boolean}
464 * @default false
465 * @memberOf NA#locals.routeParameters
466 */
467 routeParameters.regExp
468 ) {
469
470 /* ...with options... */
471 if (typeof routeParameters.regExp === 'string') {
472 objectPath = new RegExp(objectPath, routeParameters.regExp);
473 /* ...or not... */
474 } else {
475 objectPath = new RegExp(objectPath);
476 }
477 }
478
479 /* Ask for a page in GET / POST / PUT / DELETE / OPTIONS. */
480 NA.executeRequest(support, objectPath, options, path);
481};
482
483/**
484 * Prepare locals and headers.
485 * @private
486 * @function prepare
487 * @memberOf NA~
488 * @param {NA} NA NodeAtlas instance.
489 * @param {Object} path Key/Path for routes.
490 * @param {boolean} options Value(s) for routes.
491 * @return {function} Preparation middleware.
492 */
493function prepare(NA, path, options) {
494 return function (request, response, next) {
495 /* ...else execute render... */
496 NA.prepareResponse(path, options, request, response, function () {
497 next();
498 });
499 };
500}
501
502/**
503 * If page must be redirected.
504 * @private
505 * @function redirect
506 * @memberOf NA~
507 * @param {NA} NA NodeAtlas instance.
508 * @param {Object} routeParameters Parameters for route.
509 * @return {function} Preparation redirect.
510 */
511function redirect(NA, routeParameters) {
512 return function (request, response, next) {
513 /* Verify if route is a redirection... */
514 if (routeParameters.redirect && routeParameters.statusCode) {
515 /* ...and if is it, redirect... */
516 return NA.redirect(routeParameters, request, response);
517 }
518 next();
519 };
520}
521
522/**
523 * If page must be redirected.
524 * @private
525 * @function redirect
526 * @memberOf NA~
527 * @param {NA} NA NodeAtlas instance.
528 * @return {function} Preparation middleware.
529 */
530function response(NA) {
531 return function (request, response, next) {
532 /* ...else execute render... */
533 NA.changeVariationsCommon(response.locals, request, response, function () {
534 next();
535 });
536 };
537}
538
539/**
540 * Create Middleware function.
541 * @private
542 * @function getMiddlewares
543 * @memberOf NA~
544 * @param {NA} NA NodeAtlas instance.
545 * @param {Object} routeParameters Parameters for route.
546 * @return {function} Preparation middleware.
547 */
548function getMiddlewares(NA, routeParameters) {
549 var path = NA.modules.path,
550 middlewares = [];
551
552 function hasMiddlewaresFile(callback) {
553 if (
554
555 /**
556 * Allow you to set Express middleware for a specific route.
557 * @public
558 * @alias middlewares
559 * @type {string}
560 * @memberOf NA#locals.routeParameters
561 */
562 routeParameters.middlewares) {
563 middlewares = [];
564 if (routeParameters.middlewares instanceof Array) {
565 routeParameters.middlewares.forEach(function (middleware) {
566 callback(require(path.join(NA.serverPath, NA.webconfig.middlewaresRelativePath, middleware)).bind(NA));
567 });
568 } else {
569 callback(require(path.join(NA.serverPath, NA.webconfig.middlewaresRelativePath, routeParameters.middlewares)).bind(NA));
570 }
571 }
572 }
573
574 hasMiddlewaresFile(function (file) {
575 var content;
576 try {
577 content = file();
578 } catch (e) {
579 content = "";
580 }
581
582 if (content instanceof Array) {
583 middlewares = middlewares.concat(content);
584 } else {
585 middlewares.push(file);
586 }
587 });
588
589 return function() { return middlewares; };
590}
591
592/**
593 * Ask for a page in GET, POST, UPDATE, DELETE or OPTIONS.
594 * @private
595 * @function executeRequest
596 * @memberOf NA#
597 * @param {Object} support Contain all GET, POST, PUT and DELETE capability.
598 * @param {boolean} support.getSupport This page can be requested by GET ?
599 * @param {boolean} support.postSupport This page can be requested by POST ?
600 * @param {boolean} support.putSupport This page can be requested by PUT ?
601 * @param {boolean} support.deleteSupport This page can be requested by DELETE ?
602 * @param {boolean} support.optionsSupport This page can be requested by OPTIONS ?
603 * @param {string} objectPath The list of Url match for obtain response.
604 * @param {Object} options Option associate to this url.
605 * @param {string} path The Url in routes' webconfig.
606 */
607exports.executeRequest = function (support, objectPath, options, path) {
608 var NA = this,
609 routeParameters,
610 middlewares;
611
612 /* Case of `path` is an object because `NA.webconfig.routes` is an array and not an object. */
613 if (typeof path === "object") {
614 routeParameters = path;
615 } else {
616 routeParameters = options[path];
617 }
618
619 middlewares = getMiddlewares(NA, routeParameters)();
620
621 /** Execute Get Request */
622 if (support.getSupport) {
623 NA.express.get(objectPath, prepare(NA, path, options), middlewares, redirect(NA, routeParameters), response(NA));
624 }
625
626 /** Execute Post Request */
627 if (support.postSupport) {
628 NA.express.post(objectPath, prepare(NA, path, options), middlewares, redirect(NA, routeParameters), response(NA));
629 }
630
631 /** Execute Put Request */
632 if (support.putSupport) {
633 NA.express.put(objectPath, prepare(NA, path, options), middlewares, redirect(NA, routeParameters), response(NA));
634 }
635
636 /** Execute Delete Request */
637 if (support.deleteSupport) {
638 NA.express.delete(objectPath, prepare(NA, path, options), middlewares, redirect(NA, routeParameters), response(NA));
639 }
640
641 /** Execute Options Request */
642 if (support.optionsSupport) {
643 NA.express.options(objectPath, prepare(NA, path, options), middlewares, redirect(NA, routeParameters), function (request, response) {
644 response.sendStatus(200);
645 });
646 }
647};
648
649/**
650 * Send HTML result to the client.
651 * @private
652 * @function sendResponse
653 * @memberOf NA#
654 * @param {Object} request Initial request.
655 * @param {Object} response Initial response.
656 * @param {string} data HTML DOM ready for sending.
657 * @param {NA~callback} next Next step after.
658 */
659exports.sendResponse = function (request, response, data) {
660
661 /* Set/Send body */
662 response.write(data);
663 response.end();
664};
665
666/**
667 * Redirect a page to an other page if option page is set for that.
668 * @private
669 * @function redirect
670 * @memberOf NA#
671 * @param {Object} routeParameters All information associate with the redirection.
672 * @param {Object} request Initial request.
673 * @param {Object} response Initial response.
674 */
675exports.redirect = function (routeParameters, request, response) {
676 var NA = this,
677 location,
678 path = NA.modules.path;
679
680 /* Re-inject param into redirected url if is replaced by regex. */
681 if (routeParameters.regExp) {
682
683 /**
684 * Represent route to redirect if current route matched.
685 * @public
686 * @alias redirect
687 * @type {string}
688 * @memberOf NA#locals.routeParameters
689 */
690 location = routeParameters.redirect.replace(/\$([0-9]+)/g, function (regex, matches) { return request.params[matches]; });
691 /* Or by standard selector. */
692 } else {
693 location = routeParameters.redirect.replace(/\:([a-z0-9]+)/g, function (regex, matches) { return request.params[matches]; });
694 }
695
696 /* Set status and new location. */
697 response.writeHead(routeParameters.statusCode, {
698 Location: path.join(NA.webconfig.urlRelativeSubPath, location)
699 });
700
701 /* No more data. */
702 response.end();
703};
704
705/**
706 * Define a page to display when no url match in route or in directories provided by `NA#initStatics`.
707 * @private
708 * @function pageNotFound
709 * @memberOf NA#
710 * @this NA
711 */
712exports.pageNotFound = function () {
713 var NA = this,
714 pageNotFound,
715 key,
716 i,
717
718 /**
719 * Represent route to use if no route match in all route.
720 * @public
721 * @alias pageNotFound
722 * @type {string}
723 * @memberOf NA#webconfig
724 */
725 pageNotFoundUrl = NA.webconfig.pageNotFound,
726 middlewares;
727
728 if (NA.webconfig.routes instanceof Array) {
729 for (i = 0; i < NA.webconfig.routes.length; i++) {
730 key = NA.webconfig.routes[i].key || NA.webconfig.routes[i].url;
731 if (NA.webconfig.pageNotFound && key === NA.webconfig.pageNotFound) {
732 pageNotFound = NA.webconfig.routes[i];
733 pageNotFoundUrl = NA.webconfig.routes[i];
734 }
735 }
736 } else if (NA.webconfig.pageNotFound && NA.webconfig.routes[NA.webconfig.pageNotFound]) {
737 pageNotFound = NA.webconfig.routes[NA.webconfig.pageNotFound];
738 }
739
740 if (pageNotFound) {
741 middlewares = getMiddlewares(NA, pageNotFound)();
742
743 /* Match all Get Request */
744 NA.express.all("*", prepare(NA, pageNotFoundUrl, NA.webconfig.routes), middlewares, redirect(NA, pageNotFound), response(NA));
745 }
746};
\No newline at end of file