gina 0.0.9-p91b → 0.1.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. package/INSTALL.md +46 -0
  2. package/{core/asset/html/static.html → Icon/r} +0 -0
  3. package/LICENSE +1 -1
  4. package/README-4Contributors.md +96 -0
  5. package/README.md +296 -104
  6. package/bin/cli +287 -0
  7. package/bin/cli-debug +60 -0
  8. package/bin/cmd +184 -0
  9. package/bin/gina +180 -0
  10. package/config/logger.json +17 -0
  11. package/doc/framework/cli/doc.json +9 -0
  12. package/doc/framework/index.md +60 -0
  13. package/framework/v0.1.1-alpha.2/AUTHORS +7 -0
  14. package/{core/utils/lib/inherits → framework/v0.1.1-alpha.2}/LICENSE +1 -1
  15. package/framework/v0.1.1-alpha.2/VERSION +1 -0
  16. package/{core/locales/dist/language/en.json → framework/v0.1.1-alpha.2/core/asset/html/nolayout.html} +0 -0
  17. package/{core/locales/dist/language/fr.json → framework/v0.1.1-alpha.2/core/asset/html/static.html} +0 -0
  18. package/{core → framework/v0.1.1-alpha.2/core}/asset/img/android-chrome-192x192.png +0 -0
  19. package/{core → framework/v0.1.1-alpha.2/core}/asset/img/android-chrome-512x512.png +0 -0
  20. package/{core → framework/v0.1.1-alpha.2/core}/asset/img/apple-touch-icon.png +0 -0
  21. package/{core → framework/v0.1.1-alpha.2/core}/asset/img/favicon-16x16.png +0 -0
  22. package/{core → framework/v0.1.1-alpha.2/core}/asset/img/favicon-32x32.png +0 -0
  23. package/{core → framework/v0.1.1-alpha.2/core}/asset/img/favicon.ico +0 -0
  24. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.js +20904 -0
  25. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.js.map +56 -0
  26. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.min.css +1 -0
  27. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.min.css.map +1 -0
  28. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.min.js +736 -0
  29. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.min.js.map +56 -0
  30. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.onload.min.js +5 -0
  31. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/dist/gina.onload.min.js.map +8 -0
  32. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/readme.md +192 -0
  33. package/framework/v0.1.1-alpha.2/core/asset/js/plugin/uuid.json +23 -0
  34. package/framework/v0.1.1-alpha.2/core/config.js +2308 -0
  35. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/index.js +757 -0
  36. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/lib/connector.js +20 -0
  37. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/lib/connector.v2.js +429 -0
  38. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/lib/connector.v3.js +432 -0
  39. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/lib/n1ql.js +14 -0
  40. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/lib/session-store.js +21 -0
  41. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/lib/session-store.v2.js +258 -0
  42. package/framework/v0.1.1-alpha.2/core/connectors/couchbase/lib/session-store.v3.js +341 -0
  43. package/{core → framework/v0.1.1-alpha.2/core}/controller/controller.framework.js +3 -2
  44. package/framework/v0.1.1-alpha.2/core/controller/controller.js +3990 -0
  45. package/{core → framework/v0.1.1-alpha.2/core}/controller/index.js +5 -5
  46. package/framework/v0.1.1-alpha.2/core/deps/busboy/.travis.yml +17 -0
  47. package/framework/v0.1.1-alpha.2/core/deps/busboy/LICENSE +19 -0
  48. package/framework/v0.1.1-alpha.2/core/deps/busboy/README.md +225 -0
  49. package/framework/v0.1.1-alpha.2/core/deps/busboy/deps/encoding/encoding-indexes.js +73 -0
  50. package/framework/v0.1.1-alpha.2/core/deps/busboy/deps/encoding/encoding.js +2391 -0
  51. package/framework/v0.1.1-alpha.2/core/deps/busboy/lib/main.js +89 -0
  52. package/framework/v0.1.1-alpha.2/core/deps/busboy/lib/types/multipart.js +328 -0
  53. package/framework/v0.1.1-alpha.2/core/deps/busboy/lib/types/urlencoded.js +214 -0
  54. package/framework/v0.1.1-alpha.2/core/deps/busboy/lib/utils.js +191 -0
  55. package/framework/v0.1.1-alpha.2/core/deps/busboy/package.json +69 -0
  56. package/framework/v0.1.1-alpha.2/core/deps/swig-client/swig-2.0.0.min.js +5 -0
  57. package/{core → framework/v0.1.1-alpha.2/core}/dev/index.js +5 -5
  58. package/{core → framework/v0.1.1-alpha.2/core}/dev/lib/class.js +1 -1
  59. package/{core → framework/v0.1.1-alpha.2/core}/dev/lib/factory.js +1 -1
  60. package/{core → framework/v0.1.1-alpha.2/core}/dev/lib/tools.js +0 -0
  61. package/framework/v0.1.1-alpha.2/core/gna.js +1070 -0
  62. package/{core → framework/v0.1.1-alpha.2/core}/locales/README.md +41 -2
  63. package/{core → framework/v0.1.1-alpha.2/core}/locales/currency.json +0 -0
  64. package/{core/plugins/README.md → framework/v0.1.1-alpha.2/core/locales/dist/language/en.json} +0 -0
  65. package/{core/plugins/lib/intl/README.md → framework/v0.1.1-alpha.2/core/locales/dist/language/fr.json} +0 -0
  66. package/{core → framework/v0.1.1-alpha.2/core}/locales/dist/region/en.json +0 -0
  67. package/framework/v0.1.1-alpha.2/core/locales/dist/region/fr.json +9492 -0
  68. package/{core → framework/v0.1.1-alpha.2/core}/locales/index.js +5 -4
  69. package/{core → framework/v0.1.1-alpha.2/core}/locales/src/make.js +15 -12
  70. package/{core → framework/v0.1.1-alpha.2/core}/locales/src/resources/currency.csv +0 -0
  71. package/{core → framework/v0.1.1-alpha.2/core}/locales/src/resources/region.csv +0 -0
  72. package/{core → framework/v0.1.1-alpha.2/core}/locales/src/resources/region.mapping.json +0 -0
  73. package/{core → framework/v0.1.1-alpha.2/core}/mime.types +0 -0
  74. package/{core → framework/v0.1.1-alpha.2/core}/model/entity.js +156 -196
  75. package/{core → framework/v0.1.1-alpha.2/core}/model/index.js +67 -48
  76. package/{core → framework/v0.1.1-alpha.2/core}/model/template/entityFactory.js +2 -2
  77. package/{core → framework/v0.1.1-alpha.2/core}/model/template/index.js +8 -10
  78. package/{core/plugins/lib/storage → framework/v0.1.1-alpha.2/core/plugins}/README.md +0 -0
  79. package/{core → framework/v0.1.1-alpha.2/core}/plugins/index.js +3 -3
  80. package/{core/plugins/lib/validator → framework/v0.1.1-alpha.2/core/plugins/lib/file}/README.md +0 -0
  81. package/{core/plugins/lib/intl → framework/v0.1.1-alpha.2/core/plugins/lib/file}/build.json +0 -0
  82. package/framework/v0.1.1-alpha.2/core/plugins/lib/file/package.json +25 -0
  83. package/{core/utils/lib/collection → framework/v0.1.1-alpha.2/core/plugins/lib/intl}/README.md +0 -0
  84. package/{core/plugins/lib/storage → framework/v0.1.1-alpha.2/core/plugins/lib/intl}/build.json +0 -0
  85. package/{core → framework/v0.1.1-alpha.2/core}/plugins/lib/intl/package.json +3 -3
  86. package/{core → framework/v0.1.1-alpha.2/core}/plugins/lib/intl/src/main.js +17 -1
  87. package/{core/utils/lib/routing → framework/v0.1.1-alpha.2/core/plugins/lib/storage}/README.md +0 -0
  88. package/{core/plugins/lib/validator → framework/v0.1.1-alpha.2/core/plugins/lib/storage}/build.json +0 -0
  89. package/{core → framework/v0.1.1-alpha.2/core}/plugins/lib/storage/package.json +2 -2
  90. package/{core → framework/v0.1.1-alpha.2/core}/plugins/lib/storage/src/main.js +91 -85
  91. package/{core/utils/lib/url → framework/v0.1.1-alpha.2/core/plugins/lib/validator}/README.md +0 -0
  92. package/{core/utils/lib/collection → framework/v0.1.1-alpha.2/core/plugins/lib/validator}/build.json +0 -0
  93. package/{core → framework/v0.1.1-alpha.2/core}/plugins/lib/validator/package.json +3 -3
  94. package/framework/v0.1.1-alpha.2/core/plugins/lib/validator/src/form-validator.js +1762 -0
  95. package/framework/v0.1.1-alpha.2/core/plugins/lib/validator/src/main.js +6917 -0
  96. package/framework/v0.1.1-alpha.2/core/router.js +664 -0
  97. package/framework/v0.1.1-alpha.2/core/server.express.js +213 -0
  98. package/framework/v0.1.1-alpha.2/core/server.isaac.js +386 -0
  99. package/framework/v0.1.1-alpha.2/core/server.js +3010 -0
  100. package/{core → framework/v0.1.1-alpha.2/core}/status.codes +8 -0
  101. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/config/app.json +6 -0
  102. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/config/routing.json +11 -0
  103. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/config/settings.json +9 -0
  104. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/config/settings.server.json +30 -0
  105. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/config/templates.json +42 -0
  106. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/controllers/controller.content.js +39 -0
  107. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/controllers/controller.js +30 -0
  108. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle/controllers/setup.js +111 -0
  109. package/{core/template/samples → framework/v0.1.1-alpha.2/core/template/boilerplate}/bundle/index.js +0 -0
  110. package/{core/template/samples/bundle → framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_namespace}/controllers/controller.js +9 -7
  111. package/{core/template/views → framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_public}/css/default.css +0 -0
  112. package/{core/template/views → framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_public}/css/vendor/readme.md +0 -0
  113. package/{core/template/views → framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_public}/favicon.ico +0 -0
  114. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_public/js/vendor/readme.md +1 -0
  115. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_public/readme.md +1 -0
  116. package/framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_templates/handlers/main.js +24 -0
  117. package/{core/template/views/html/default.html → framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_templates/html/homepage.html} +0 -0
  118. package/{core/template/views/html/layout.html → framework/v0.1.1-alpha.2/core/template/boilerplate/bundle_templates/html/layout/main.html} +2 -2
  119. package/{core → framework/v0.1.1-alpha.2/core}/template/command/gina.bat.tpl +0 -0
  120. package/{core → framework/v0.1.1-alpha.2/core}/template/command/gina.tpl +1 -1
  121. package/framework/v0.1.1-alpha.2/core/template/conf/env.json +76 -0
  122. package/{core/template/conf/project.json → framework/v0.1.1-alpha.2/core/template/conf/manifest.json} +1 -0
  123. package/{core → framework/v0.1.1-alpha.2/core}/template/conf/package.json +2 -2
  124. package/framework/v0.1.1-alpha.2/core/template/conf/settings.json +92 -0
  125. package/framework/v0.1.1-alpha.2/core/template/conf/statics.json +10 -0
  126. package/framework/v0.1.1-alpha.2/core/template/conf/templates.json +37 -0
  127. package/{core → framework/v0.1.1-alpha.2/core}/template/error/client/json/401.json +0 -0
  128. package/{core → framework/v0.1.1-alpha.2/core}/template/error/client/json/403.json +0 -0
  129. package/{core → framework/v0.1.1-alpha.2/core}/template/error/client/json/404.json +0 -0
  130. package/{core → framework/v0.1.1-alpha.2/core}/template/error/server/html/50x.html +0 -0
  131. package/{core → framework/v0.1.1-alpha.2/core}/template/error/server/json/500.json +0 -0
  132. package/{core → framework/v0.1.1-alpha.2/core}/template/error/server/json/503.json +0 -0
  133. package/{core/utils/lib/routing/build.json → framework/v0.1.1-alpha.2/core/template/extensions/logger/config.json} +0 -0
  134. package/{core/utils → framework/v0.1.1-alpha.2}/helpers/console.js +3 -3
  135. package/{core/utils → framework/v0.1.1-alpha.2}/helpers/context.js +240 -49
  136. package/framework/v0.1.1-alpha.2/helpers/dateFormat.js +528 -0
  137. package/framework/v0.1.1-alpha.2/helpers/index.js +79 -0
  138. package/framework/v0.1.1-alpha.2/helpers/json/README.md +0 -0
  139. package/framework/v0.1.1-alpha.2/helpers/json/package.json +20 -0
  140. package/framework/v0.1.1-alpha.2/helpers/json/src/main.js +97 -0
  141. package/{core/utils → framework/v0.1.1-alpha.2}/helpers/path.js +342 -140
  142. package/framework/v0.1.1-alpha.2/helpers/plugins/README.md +4 -0
  143. package/framework/v0.1.1-alpha.2/helpers/plugins/package.json +20 -0
  144. package/framework/v0.1.1-alpha.2/helpers/plugins/src/api-error.js +160 -0
  145. package/framework/v0.1.1-alpha.2/helpers/plugins/src/main.js +32 -0
  146. package/framework/v0.1.1-alpha.2/helpers/prototypes.js +218 -0
  147. package/{core/utils → framework/v0.1.1-alpha.2}/helpers/task.js +49 -29
  148. package/{core/utils → framework/v0.1.1-alpha.2}/helpers/text.js +7 -7
  149. package/framework/v0.1.1-alpha.2/lib/archiver/README.md +0 -0
  150. package/framework/v0.1.1-alpha.2/lib/archiver/build.json +0 -0
  151. package/framework/v0.1.1-alpha.2/lib/archiver/package.json +20 -0
  152. package/framework/v0.1.1-alpha.2/lib/archiver/src/dep/jszip.min.js +15 -0
  153. package/framework/v0.1.1-alpha.2/lib/archiver/src/main.js +499 -0
  154. package/framework/v0.1.1-alpha.2/lib/cmd/aliases.json +13 -0
  155. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/add.js +507 -0
  156. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/arguments.json +4 -0
  157. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/copy.js +15 -0
  158. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/cp.js +2 -0
  159. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/help.js +28 -0
  160. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/help.txt +67 -0
  161. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/list.js +129 -0
  162. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/remove.js +229 -0
  163. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/rename.js +4 -0
  164. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/restart.js +235 -0
  165. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/rm.js +2 -0
  166. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/start.js +394 -0
  167. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/status.js +3 -0
  168. package/framework/v0.1.1-alpha.2/lib/cmd/bundle/stop.js +232 -0
  169. package/framework/v0.1.1-alpha.2/lib/cmd/env/add.js +436 -0
  170. package/framework/v0.1.1-alpha.2/lib/cmd/env/get.js +62 -0
  171. package/framework/v0.1.1-alpha.2/lib/cmd/env/help.js +28 -0
  172. package/framework/v0.1.1-alpha.2/lib/cmd/env/help.txt +33 -0
  173. package/framework/v0.1.1-alpha.2/lib/cmd/env/link-dev.js +80 -0
  174. package/framework/v0.1.1-alpha.2/lib/cmd/env/list.js +111 -0
  175. package/framework/v0.1.1-alpha.2/lib/cmd/env/remove.js +150 -0
  176. package/framework/v0.1.1-alpha.2/lib/cmd/env/rm.js +2 -0
  177. package/framework/v0.1.1-alpha.2/lib/cmd/env/set.js +57 -0
  178. package/framework/v0.1.1-alpha.2/lib/cmd/env/unset.js +44 -0
  179. package/framework/v0.1.1-alpha.2/lib/cmd/env/use.js +79 -0
  180. package/framework/v0.1.1-alpha.2/lib/cmd/framework/dot.js +70 -0
  181. package/framework/v0.1.1-alpha.2/lib/cmd/framework/get.js +0 -0
  182. package/framework/v0.1.1-alpha.2/lib/cmd/framework/help.js +39 -0
  183. package/framework/v0.1.1-alpha.2/lib/cmd/framework/help.txt +31 -0
  184. package/framework/v0.1.1-alpha.2/lib/cmd/framework/init.js +514 -0
  185. package/{core/utils/lib/cmd → framework/v0.1.1-alpha.2/lib/cmd/framework}/msg.json +3 -3
  186. package/framework/v0.1.1-alpha.2/lib/cmd/framework/open.js +50 -0
  187. package/framework/v0.1.1-alpha.2/lib/cmd/framework/restart.js +124 -0
  188. package/framework/v0.1.1-alpha.2/lib/cmd/framework/set.js +161 -0
  189. package/framework/v0.1.1-alpha.2/lib/cmd/framework/start.js +96 -0
  190. package/framework/v0.1.1-alpha.2/lib/cmd/framework/status.js +72 -0
  191. package/framework/v0.1.1-alpha.2/lib/cmd/framework/stop.js +159 -0
  192. package/framework/v0.1.1-alpha.2/lib/cmd/framework/tail.js +183 -0
  193. package/framework/v0.1.1-alpha.2/lib/cmd/framework/update.js +0 -0
  194. package/framework/v0.1.1-alpha.2/lib/cmd/framework/version.js +44 -0
  195. package/framework/v0.1.1-alpha.2/lib/cmd/gina-dev.1.md +66 -0
  196. package/framework/v0.1.1-alpha.2/lib/cmd/gina-framework.1.md +100 -0
  197. package/framework/v0.1.1-alpha.2/lib/cmd/gina.1.md +79 -0
  198. package/framework/v0.1.1-alpha.2/lib/cmd/helper.js +1147 -0
  199. package/framework/v0.1.1-alpha.2/lib/cmd/index.js +170 -0
  200. package/framework/v0.1.1-alpha.2/lib/cmd/msg.json +20 -0
  201. package/framework/v0.1.1-alpha.2/lib/cmd/port/help.js +28 -0
  202. package/framework/v0.1.1-alpha.2/lib/cmd/port/help.txt +31 -0
  203. package/framework/v0.1.1-alpha.2/lib/cmd/port/inc/scan.js +108 -0
  204. package/framework/v0.1.1-alpha.2/lib/cmd/port/list.js +176 -0
  205. package/framework/v0.1.1-alpha.2/lib/cmd/port/set.js +0 -0
  206. package/framework/v0.1.1-alpha.2/lib/cmd/project/add.js +528 -0
  207. package/framework/v0.1.1-alpha.2/lib/cmd/project/arguments.json +9 -0
  208. package/framework/v0.1.1-alpha.2/lib/cmd/project/build.js +115 -0
  209. package/framework/v0.1.1-alpha.2/lib/cmd/project/help.js +28 -0
  210. package/framework/v0.1.1-alpha.2/lib/cmd/project/help.txt +76 -0
  211. package/framework/v0.1.1-alpha.2/lib/cmd/project/import.js +2 -0
  212. package/framework/v0.1.1-alpha.2/lib/cmd/project/list.js +55 -0
  213. package/framework/v0.1.1-alpha.2/lib/cmd/project/move.js +0 -0
  214. package/framework/v0.1.1-alpha.2/lib/cmd/project/remove.js +144 -0
  215. package/framework/v0.1.1-alpha.2/lib/cmd/project/rename.js +162 -0
  216. package/framework/v0.1.1-alpha.2/lib/cmd/project/restart.js +0 -0
  217. package/framework/v0.1.1-alpha.2/lib/cmd/project/rm.js +2 -0
  218. package/framework/v0.1.1-alpha.2/lib/cmd/project/start.js +0 -0
  219. package/framework/v0.1.1-alpha.2/lib/cmd/project/status.js +3 -0
  220. package/framework/v0.1.1-alpha.2/lib/cmd/project/stop.js +0 -0
  221. package/framework/v0.1.1-alpha.2/lib/cmd/protocol/help.js +27 -0
  222. package/framework/v0.1.1-alpha.2/lib/cmd/protocol/help.txt +57 -0
  223. package/framework/v0.1.1-alpha.2/lib/cmd/protocol/list.js +239 -0
  224. package/framework/v0.1.1-alpha.2/lib/cmd/protocol/set.js +631 -0
  225. package/framework/v0.1.1-alpha.2/lib/cmd/scope/help.js +28 -0
  226. package/framework/v0.1.1-alpha.2/lib/cmd/scope/help.txt +33 -0
  227. package/framework/v0.1.1-alpha.2/lib/cmd/scope/link-local.js +80 -0
  228. package/framework/v0.1.1-alpha.2/lib/cmd/scope/list.js +116 -0
  229. package/framework/v0.1.1-alpha.2/lib/cmd/scope/remove.js +150 -0
  230. package/framework/v0.1.1-alpha.2/lib/cmd/scope/rm.js +2 -0
  231. package/framework/v0.1.1-alpha.2/lib/cmd/scope/set.js +57 -0
  232. package/framework/v0.1.1-alpha.2/lib/cmd/scope/unset.js +44 -0
  233. package/framework/v0.1.1-alpha.2/lib/cmd/scope/use.js +79 -0
  234. package/framework/v0.1.1-alpha.2/lib/cmd/view/add.js +311 -0
  235. package/framework/v0.1.1-alpha.2/lib/collection/README.md +5 -0
  236. package/framework/v0.1.1-alpha.2/lib/collection/build.json +0 -0
  237. package/{core/utils → framework/v0.1.1-alpha.2}/lib/collection/package.json +2 -2
  238. package/framework/v0.1.1-alpha.2/lib/collection/src/main.js +1459 -0
  239. package/{core/utils → framework/v0.1.1-alpha.2}/lib/config.js +49 -34
  240. package/framework/v0.1.1-alpha.2/lib/cron/README.md +7 -0
  241. package/framework/v0.1.1-alpha.2/lib/cron/package.json +20 -0
  242. package/framework/v0.1.1-alpha.2/lib/cron/src/main.js +176 -0
  243. package/{core/utils → framework/v0.1.1-alpha.2}/lib/generator/index.js +8 -4
  244. package/framework/v0.1.1-alpha.2/lib/index.js +116 -0
  245. package/framework/v0.1.1-alpha.2/lib/inherits/LICENSE +19 -0
  246. package/{core/utils → framework/v0.1.1-alpha.2}/lib/inherits/README.md +0 -0
  247. package/{core/utils → framework/v0.1.1-alpha.2}/lib/inherits/example/inheriting_eventemitter.js +0 -0
  248. package/{core/utils → framework/v0.1.1-alpha.2}/lib/inherits/example/protected_inheritance.js +0 -0
  249. package/{core/utils → framework/v0.1.1-alpha.2}/lib/inherits/example/simple_inheritance.js +0 -0
  250. package/{core/utils → framework/v0.1.1-alpha.2}/lib/inherits/example/super_attribute_overridden_by_child_on_init.js +0 -0
  251. package/{core/utils → framework/v0.1.1-alpha.2}/lib/inherits/package.json +2 -2
  252. package/{core/utils → framework/v0.1.1-alpha.2}/lib/inherits/src/main.js +18 -19
  253. package/framework/v0.1.1-alpha.2/lib/logger/README.md +7 -0
  254. package/framework/v0.1.1-alpha.2/lib/logger/package.json +20 -0
  255. package/framework/v0.1.1-alpha.2/lib/logger/src/containers/default/index.js +55 -0
  256. package/framework/v0.1.1-alpha.2/lib/logger/src/containers/file/index.js +251 -0
  257. package/framework/v0.1.1-alpha.2/lib/logger/src/containers/file/lib/logrotator/README.md +100 -0
  258. package/framework/v0.1.1-alpha.2/lib/logger/src/containers/file/lib/logrotator/index.js +274 -0
  259. package/framework/v0.1.1-alpha.2/lib/logger/src/containers/mq/index.js +52 -0
  260. package/framework/v0.1.1-alpha.2/lib/logger/src/containers/mq/listener.js +302 -0
  261. package/framework/v0.1.1-alpha.2/lib/logger/src/containers/mq/speaker.js +118 -0
  262. package/framework/v0.1.1-alpha.2/lib/logger/src/helper.js +131 -0
  263. package/framework/v0.1.1-alpha.2/lib/logger/src/main.js +734 -0
  264. package/{core/utils → framework/v0.1.1-alpha.2}/lib/math/index.js +58 -35
  265. package/{core/utils → framework/v0.1.1-alpha.2}/lib/merge/README.md +5 -0
  266. package/{core/utils → framework/v0.1.1-alpha.2}/lib/merge/example/merge.js +1 -1
  267. package/{core/utils → framework/v0.1.1-alpha.2}/lib/merge/example/merge_2_literal objects.js +0 -0
  268. package/{core/utils → framework/v0.1.1-alpha.2}/lib/merge/example/merge_and_preserve_first.js +2 -0
  269. package/{core/utils → framework/v0.1.1-alpha.2}/lib/merge/package.json +2 -2
  270. package/framework/v0.1.1-alpha.2/lib/merge/src/main.js +531 -0
  271. package/{core/utils → framework/v0.1.1-alpha.2}/lib/model.js +42 -19
  272. package/framework/v0.1.1-alpha.2/lib/proc.js +518 -0
  273. package/framework/v0.1.1-alpha.2/lib/routing/README.md +0 -0
  274. package/framework/v0.1.1-alpha.2/lib/routing/build.json +0 -0
  275. package/{core/utils → framework/v0.1.1-alpha.2}/lib/routing/package.json +2 -2
  276. package/framework/v0.1.1-alpha.2/lib/routing/src/main.js +1492 -0
  277. package/framework/v0.1.1-alpha.2/lib/session-store.js +33 -0
  278. package/{core/utils → framework/v0.1.1-alpha.2}/lib/shell.js +43 -30
  279. package/framework/v0.1.1-alpha.2/lib/swig-filters/README.md +0 -0
  280. package/framework/v0.1.1-alpha.2/lib/swig-filters/package.json +20 -0
  281. package/framework/v0.1.1-alpha.2/lib/swig-filters/src/main.js +322 -0
  282. package/framework/v0.1.1-alpha.2/lib/url/README.md +0 -0
  283. package/{core/utils → framework/v0.1.1-alpha.2}/lib/url/index.js +2 -1
  284. package/{core/utils → framework/v0.1.1-alpha.2}/lib/url/mocks.json +0 -0
  285. package/{core/utils → framework/v0.1.1-alpha.2}/lib/url/routing.json +9 -9
  286. package/{core/utils → framework/v0.1.1-alpha.2}/lib/url/test.js +0 -0
  287. package/{core/utils → framework/v0.1.1-alpha.2}/lib/validator.js +7 -7
  288. package/framework/v0.1.1-alpha.2/package.json +14 -0
  289. package/package.json +35 -33
  290. package/resources/etc/init.d/debian/gina +117 -0
  291. package/resources/home/framework/env.json +34 -0
  292. package/resources/home/framework/locals.json +14 -0
  293. package/resources/home/framework/project.json +48 -0
  294. package/resources/home/framework/projects.json +6 -0
  295. package/resources/home/main.json +47 -0
  296. package/resources/home/settings.json +22 -0
  297. package/resources/home/user/extensions/logger/default/config.json +77 -0
  298. package/resources/home/user/extensions/logger/file/config.json +11 -0
  299. package/resources/img/android-chrome-192x192.png +0 -0
  300. package/resources/img/android-chrome-512x512.png +0 -0
  301. package/resources/img/apple-touch-icon.png +0 -0
  302. package/resources/img/favicon-16x16.png +0 -0
  303. package/resources/img/favicon-32x32.png +0 -0
  304. package/resources/img/favicon.ico +0 -0
  305. package/resources/package.json.template +50 -0
  306. package/script/lib.js +24 -0
  307. package/script/post_install.js +449 -157
  308. package/script/pre_install.js +277 -36
  309. package/script/prepare_version.js +425 -0
  310. package/utils/helper.js +438 -0
  311. package/utils/prototypes.js +239 -0
  312. package/utils/prototypes.json_clone.js +175 -0
  313. package/.npmignore +0 -6
  314. package/MIDDLEWARE +0 -1
  315. package/SUCCESS +0 -1
  316. package/VERSION +0 -1
  317. package/core/asset/html/nolayout.html +0 -1
  318. package/core/asset/js/plugin/build.dev.json +0 -28
  319. package/core/asset/js/plugin/build.json +0 -31
  320. package/core/asset/js/plugin/dist/gina.min.css +0 -1
  321. package/core/asset/js/plugin/dist/gina.min.css.map +0 -1
  322. package/core/asset/js/plugin/dist/gina.min.js +0 -13731
  323. package/core/asset/js/plugin/dist/gina.min.js.map +0 -42
  324. package/core/asset/js/plugin/dist/gina.onload.min.js +0 -3
  325. package/core/asset/js/plugin/dist/gina.onload.min.js.map +0 -8
  326. package/core/asset/js/plugin/readme.md +0 -152
  327. package/core/asset/js/plugin/src/gina/main.js +0 -132
  328. package/core/asset/js/plugin/src/gina/popin/css/design.css +0 -23
  329. package/core/asset/js/plugin/src/gina/popin/css/main.css +0 -1112
  330. package/core/asset/js/plugin/src/gina/popin/css/popin.css +0 -3
  331. package/core/asset/js/plugin/src/gina/popin/css/popin.css.map +0 -1
  332. package/core/asset/js/plugin/src/gina/popin/doc/TOC.md +0 -29
  333. package/core/asset/js/plugin/src/gina/popin/doc/css.md +0 -162
  334. package/core/asset/js/plugin/src/gina/popin/doc/extend.md +0 -663
  335. package/core/asset/js/plugin/src/gina/popin/doc/faq.md +0 -46
  336. package/core/asset/js/plugin/src/gina/popin/doc/html.md +0 -227
  337. package/core/asset/js/plugin/src/gina/popin/doc/js.md +0 -37
  338. package/core/asset/js/plugin/src/gina/popin/doc/misc.md +0 -178
  339. package/core/asset/js/plugin/src/gina/popin/doc/usage.md +0 -130
  340. package/core/asset/js/plugin/src/gina/popin/main.js +0 -783
  341. package/core/asset/js/plugin/src/gina/popin/sass/config.sass +0 -37
  342. package/core/asset/js/plugin/src/gina/popin/sass/gina-design.sass +0 -622
  343. package/core/asset/js/plugin/src/gina/popin/sass/gina-popin.sass +0 -54
  344. package/core/asset/js/plugin/src/gina/popin/sass/helper.scss +0 -73
  345. package/core/asset/js/plugin/src/gina/toolbar/.npmignore +0 -19
  346. package/core/asset/js/plugin/src/gina/toolbar/css/toolbar.css +0 -433
  347. package/core/asset/js/plugin/src/gina/toolbar/css/toolbar.css.map +0 -1
  348. package/core/asset/js/plugin/src/gina/toolbar/index.html +0 -285
  349. package/core/asset/js/plugin/src/gina/toolbar/index.kit +0 -120
  350. package/core/asset/js/plugin/src/gina/toolbar/jquery-3.1.0.min.js +0 -4
  351. package/core/asset/js/plugin/src/gina/toolbar/main.js +0 -693
  352. package/core/asset/js/plugin/src/gina/toolbar/mock.gina.json +0 -1
  353. package/core/asset/js/plugin/src/gina/toolbar/mock.user.json +0 -1
  354. package/core/asset/js/plugin/src/gina/toolbar/readme.md +0 -7
  355. package/core/asset/js/plugin/src/gina/toolbar/sass/toolbar.sass +0 -563
  356. package/core/asset/js/plugin/src/gina/toolbar/svg-src/Info-color.svg +0 -1
  357. package/core/asset/js/plugin/src/gina/toolbar/svg-src/Info-grey.svg +0 -1
  358. package/core/asset/js/plugin/src/gina/toolbar/svg-src/error-color.svg +0 -1
  359. package/core/asset/js/plugin/src/gina/toolbar/svg-src/error-grey.svg +0 -1
  360. package/core/asset/js/plugin/src/gina/toolbar/svg-src/info-optim.svg +0 -13
  361. package/core/asset/js/plugin/src/gina/toolbar/svg-src/logo-gina.svg +0 -1
  362. package/core/asset/js/plugin/src/gina/toolbar/svg-src/settings-big.svg +0 -1
  363. package/core/asset/js/plugin/src/gina/toolbar/svg-src/settings.afdesign +0 -0
  364. package/core/asset/js/plugin/src/gina/toolbar/svg-src/settings.svg +0 -1
  365. package/core/asset/js/plugin/src/gina/toolbar/svg-src/stripe-calendar-optim.svg +0 -1
  366. package/core/asset/js/plugin/src/gina/toolbar/svg-src/stripe-calendar.svg +0 -1
  367. package/core/asset/js/plugin/src/gina/toolbar/svg-src/stripe-card-optim.svg +0 -1
  368. package/core/asset/js/plugin/src/gina/toolbar/svg-src/stripe-card.svg +0 -1
  369. package/core/asset/js/plugin/src/gina/toolbar/svg-src/stripe-lock-optim.svg +0 -1
  370. package/core/asset/js/plugin/src/gina/toolbar/svg-src/stripe-lock.svg +0 -1
  371. package/core/asset/js/plugin/src/gina/toolbar/svg-src/warning-color.svg +0 -1
  372. package/core/asset/js/plugin/src/gina/toolbar/svg-src/warning-grey.svg +0 -1
  373. package/core/asset/js/plugin/src/gina/toolbar/test.jpg +0 -0
  374. package/core/asset/js/plugin/src/gina/toolbar/toolbar.html +0 -104
  375. package/core/asset/js/plugin/src/gina/utils/dom.js +0 -24
  376. package/core/asset/js/plugin/src/gina/utils/events.js +0 -201
  377. package/core/asset/js/plugin/src/gina/utils/loader.js +0 -57
  378. package/core/asset/js/plugin/src/main.js +0 -235
  379. package/core/asset/js/plugin/src/vendor/jquery/1.11.1-css-event_alias.min.js +0 -5
  380. package/core/asset/js/plugin/src/vendor/jquery/1.12.4.min.js +0 -5
  381. package/core/asset/js/plugin/src/vendor/jquery/2.1.1-css-event_alias-sizzle.min.js +0 -4
  382. package/core/asset/js/plugin/src/vendor/jquery/README.md +0 -14
  383. package/core/asset/js/plugin/src/vendor/jquery/slim-3.1.1.min.js +0 -4
  384. package/core/config.js +0 -1225
  385. package/core/controller/controller.js +0 -1860
  386. package/core/gna.js +0 -908
  387. package/core/plugins/lib/validator/src/form-validator.js +0 -788
  388. package/core/plugins/lib/validator/src/main.js +0 -2056
  389. package/core/router.js +0 -717
  390. package/core/server.js +0 -1363
  391. package/core/template/conf/env.json +0 -70
  392. package/core/template/conf/settings.json +0 -48
  393. package/core/template/conf/statics.json +0 -6
  394. package/core/template/conf/views.json +0 -16
  395. package/core/template/samples/bundle/config/app.json +0 -4
  396. package/core/template/samples/bundle/config/routing.json +0 -9
  397. package/core/template/samples/bundle/controllers/setup.js +0 -33
  398. package/core/template/views/js/vendor/readme.md +0 -1
  399. package/core/template/views/readme.md +0 -1
  400. package/core/utils/helpers/dateFormat.js +0 -264
  401. package/core/utils/helpers/index.js +0 -202
  402. package/core/utils/index.js +0 -75
  403. package/core/utils/lib/cmd/app.js +0 -686
  404. package/core/utils/lib/cmd/basic.help.txt +0 -40
  405. package/core/utils/lib/cmd/basic.js +0 -141
  406. package/core/utils/lib/cmd/gina-add-bundle.js +0 -328
  407. package/core/utils/lib/cmd/gina-add-views.js +0 -99
  408. package/core/utils/lib/cmd/gina-build.js +0 -218
  409. package/core/utils/lib/cmd/gina-clean.js +0 -26
  410. package/core/utils/lib/cmd/gina-connect.js +0 -176
  411. package/core/utils/lib/cmd/gina-delete-bundle.js +0 -176
  412. package/core/utils/lib/cmd/gina-deploy.js +0 -452
  413. package/core/utils/lib/cmd/gina-init-project.js +0 -83
  414. package/core/utils/lib/cmd/gina-start-bundle.js +0 -3
  415. package/core/utils/lib/cmd/gina-start.js +0 -3
  416. package/core/utils/lib/cmd/index.js +0 -157
  417. package/core/utils/lib/cmd/project.js +0 -14
  418. package/core/utils/lib/collection/src/main.js +0 -650
  419. package/core/utils/lib/logger/containers/file.js +0 -11
  420. package/core/utils/lib/logger/index.js +0 -260
  421. package/core/utils/lib/merge/src/main.js +0 -344
  422. package/core/utils/lib/proc.js +0 -416
  423. package/core/utils/lib/routing/src/main.js +0 -165
  424. package/documentation/css/default.css +0 -3
  425. package/documentation/html/home.html +0 -6
  426. package/documentation/html/inc/_footer.html +0 -5
  427. package/documentation/html/layout.html +0 -21
  428. package/documentation/img/debug-conf1.png +0 -0
  429. package/documentation/img/debug-conf2.png +0 -0
  430. package/documentation/img/debug-conf3.png +0 -0
  431. package/documentation/img/debug-conf4.png +0 -0
  432. package/documentation/img/debug-conf5.png +0 -0
  433. package/documentation/img/debug-conf6.png +0 -0
  434. package/documentation/img/debug-conf7.png +0 -0
  435. package/documentation/img/debug-new1.png +0 -0
  436. package/documentation/img/debug-new2.png +0 -0
  437. package/documentation/img/debug-start.png +0 -0
  438. package/documentation/md/api/controller/main.md +0 -74
  439. package/migration_note.md +0 -7
  440. package/package-lock.json +0 -611
  441. package/script/pre_publish.js +0 -207
  442. package/tutorial/Commands/README.md +0 -56
  443. package/tutorial/Commands/add-bundle-result.png +0 -0
  444. package/tutorial/Commands/add-bundle.png +0 -0
  445. package/tutorial/Commands/delete-bundle.png +0 -0
  446. package/tutorial/Commands/help.png +0 -0
  447. package/tutorial/Commands/init-project.png +0 -0
  448. package/tutorial/Commands/start-bundle-result.png +0 -0
  449. package/tutorial/Commands/start-bundle-stop.png +0 -0
  450. package/tutorial/Commands/start-bundle.png +0 -0
  451. package/tutorial/Commands/version.png +0 -0
  452. package/tutorial/ETC/README.md +0 -74
  453. package/tutorial/ETC/add-bundle-result.png +0 -0
  454. package/tutorial/ETC/add-bundle.png +0 -0
  455. package/tutorial/ETC/init-project.png +0 -0
  456. package/tutorial/Install/README.md +0 -54
  457. package/tutorial/Install/git-get.png +0 -0
  458. package/tutorial/Install/git-install-end.png +0 -0
  459. package/tutorial/Install/git-install-result.png +0 -0
  460. package/tutorial/Install/git-install-start.png +0 -0
  461. package/tutorial/Install/install-end.png +0 -0
  462. package/tutorial/Install/install-result.png +0 -0
  463. package/tutorial/Install/install-start.png +0 -0
  464. package/tutorial/WebStorm/README.md +0 -30
  465. package/tutorial/WebStorm/closure-compiler.png +0 -0
  466. package/tutorial/WebStorm/sass.png +0 -0
@@ -0,0 +1,3990 @@
1
+ "use strict";
2
+ /*
3
+ * This file is part of the gina package.
4
+ * Copyright (c) 2009-2022 Rhinostone <contact@gina.io>
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+
10
+ //Imports.
11
+ var fs = require('fs');
12
+ const {promises: {readFile}} = require("fs");
13
+ var util = require('util');
14
+ var promisify = util.promisify;
15
+ var EventEmitter = require('events').EventEmitter;
16
+ var zlib = require('zlib');
17
+
18
+ //var dns = require('dns');
19
+ // var tls = require('tls');
20
+ // var crypto = require('crypto');
21
+
22
+ var lib = require('./../../lib') || require.cache[require.resolve('./../../lib')];
23
+ var merge = lib.merge;
24
+ var inherits = lib.inherits;
25
+ var console = lib.logger;
26
+ var Collection = lib.Collection;
27
+ var routingUtils = lib.routing;
28
+ var swig = require('swig');
29
+ var SwigFilters = lib.SwigFilters;
30
+ var statusCodes = requireJSON( _( getPath('gina').core + '/status.codes') );
31
+
32
+
33
+ /**
34
+ * @class SuperController
35
+ *
36
+ *
37
+ * @package Gina
38
+ * @namespace
39
+ * @author Rhinostone <contact@gina.io>
40
+ *
41
+ * @api Public
42
+ */
43
+ function SuperController(options) {
44
+
45
+ //public
46
+ this.name = 'SuperController';
47
+ this.engine = {};
48
+
49
+
50
+ var self = this;
51
+ //private
52
+ var local = {
53
+ req : null,
54
+ res : null,
55
+ next : null,
56
+ options : options || null,
57
+ query : {},
58
+ _data : {},
59
+ view : {}
60
+ };
61
+
62
+ /**
63
+ * SuperController Constructor
64
+ * @constructor
65
+ * */
66
+ var init = function() {
67
+
68
+ if ( typeof(SuperController.initialized) != 'undefined' ) {
69
+ return getInstance()
70
+ } else {
71
+
72
+ SuperController.instance = self;
73
+
74
+
75
+ if (local.options) {
76
+ SuperController.instance._options = local.options;
77
+ }
78
+
79
+ SuperController.initialized = true;
80
+
81
+ }
82
+ }
83
+
84
+ var getInstance = function() {
85
+ local.options = SuperController.instance._options = options;
86
+ // 2022-03-07 Fix for none-developpement environnements (without cache)
87
+ self._options = local.options;
88
+
89
+ return SuperController.instance;
90
+ }
91
+
92
+ var hasViews = function() {
93
+ return ( typeof(local.options.template) != 'undefined' ) ? true : false;
94
+ }
95
+
96
+ /**
97
+ * isHttp2
98
+ * Returns `true` if server configured for HTTP/2
99
+ *
100
+ * @returns {boolean} isHttp2
101
+ */
102
+ var isHttp2 = function() {
103
+ var options = local.options;
104
+ var protocolVersion = ~~options.conf.server.protocol.match(/\/(.*)$/)[1].replace(/\.\d+/, '');
105
+ var httpLib = options.conf.server.protocol.match(/^(.*)\//)[1] + ( (protocolVersion >= 2) ? protocolVersion : '' );
106
+
107
+
108
+ return /http2/.test(httpLib)
109
+ }
110
+ /**
111
+ * isSecured
112
+ * Returns `true` if server configured to handle a HTTPS exchanges
113
+ *
114
+ * @returns {boolean} isSecured
115
+ */
116
+ var isSecured = function() {
117
+ return /https/.test(local.options.conf.server.scheme)
118
+ }
119
+
120
+ this.getRequestObject = function() {
121
+ return local.req;
122
+ }
123
+
124
+ this.getResponseObject = function() {
125
+ return local.res;
126
+ }
127
+
128
+ this.getNextCallback = function() {
129
+ return local.next;
130
+ }
131
+
132
+ /**
133
+ * Check if env is running cacheless
134
+ * */
135
+ this.isCacheless = function() {
136
+ return (/^true$/i.test(process.env.NODE_ENV_IS_DEV)) ? true : false;
137
+ }
138
+ /**
139
+ * Check if the project scope is set for local
140
+ * */
141
+ this.isLocalScope = function() {
142
+ return (/^true$/i.test(process.env.NODE_SCOPE_IS_LOCAL)) ? true : false;
143
+ }
144
+
145
+ this.setOptions = function(req, res, next, options) {
146
+ local.options = SuperController.instance._options = options;
147
+ local.options.renderingStack = (local.options.renderingStack) ? local.options.renderingStack : [];
148
+ local.options.isRenderingCustomError = (local.options.isRenderingCustomError) ? local.options.isRenderingCustomError : false;
149
+
150
+
151
+ // N.B.: Avoid setting `page` properties as much as possible from the routing.json
152
+ // It will be easier for the framework if set from the controller.
153
+ //
154
+ // Here is a sample if you choose to set `page.view.title` from the rule
155
+ // ------rouging rule sample -----
156
+ // {
157
+ // "default": {
158
+ // "url": ["", "/"],
159
+ // "param": {
160
+ // "control": "home",
161
+ // "title": "My Title"
162
+ // }
163
+ // }
164
+ //
165
+ // ------controller action sample -----
166
+ // Here is a sample if you decide to set `page.view.title` from your controller
167
+ //
168
+ // this.home = function(req, res, next) {
169
+ // var data = { page: { view: { title: "My Title"}}};
170
+ // self.render(data)
171
+ // }
172
+
173
+ if ( typeof(options.conf.content.routing[options.rule].param) != 'undefined' ) {
174
+ var str = 'page.'
175
+ , p = options.conf.content.routing[options.rule].param
176
+ ;
177
+
178
+ for (var key in p) {
179
+ if ( p.hasOwnProperty(key) && !/^(control)$/.test(key) ) {
180
+ str += key + '.';
181
+ var obj = p[key], value = '';
182
+ for (var prop in obj) {
183
+ if (obj.hasOwnProperty(prop)) {
184
+ value += obj[prop]
185
+ } else {
186
+
187
+ if ( /^:/.test(value) ) {
188
+ str = 'page.view.params.'+ key + '.';
189
+ set(str.substr(0, str.length-1), req.params[value.substr(1)]);
190
+ } else if (/^(file|title)$/.test(key)) {
191
+ str = 'page.view.'+ key + '.';
192
+ set(str.substr(0, str.length-1), value);
193
+ } else {
194
+ set(str.substr(0, str.length-1), value)
195
+ }
196
+
197
+ str = 'page.'
198
+ }
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ local.req = req;
205
+ local.res = res;
206
+ local.next = next;
207
+
208
+ getParams(req);
209
+ if ( typeof(local.options.template) != 'undefined' && typeof(local.options.control) != 'undefined' ) {
210
+
211
+
212
+ var action = local.options.control
213
+ , rule = local.options.rule
214
+ , ext = 'html' // by default
215
+ , isWithoutLayout = false // by default
216
+ , namespace = local.options.namespace || '';
217
+
218
+
219
+ if ( typeof(local.options.template) != 'undefined' && local.options.template ) {
220
+ if (
221
+ typeof(local.options.template.ext) != 'undefined'
222
+ && local.options.template.ext
223
+ && local.options.template.ext != ''
224
+ ) {
225
+ ext = local.options.template.ext
226
+ }
227
+
228
+ if ( !/\./.test(ext) ) {
229
+ ext = '.' + ext;
230
+ local.options.template.ext = ext
231
+ }
232
+
233
+ if (
234
+ typeof(local.options.template.layout) == 'undefined'
235
+ || /^false$/.test(local.options.template.layout)
236
+ || local.options.template.layout == ''
237
+ ) {
238
+ isWithoutLayout = true;
239
+ }
240
+ }
241
+
242
+
243
+ if ( hasViews() ) {
244
+
245
+ if ( typeof(local.options.file) == 'undefined') {
246
+ local.options.file = 'index'
247
+ }
248
+
249
+ if ( typeof(local.options.isWithoutLayout) == 'undefined' || !isWithoutLayout ) {
250
+ local.options.isWithoutLayout = false;
251
+ }
252
+
253
+ rule = local.options.rule;
254
+ namespace = local.options.namespace || 'default';
255
+
256
+
257
+ set('page.view.file', local.options.file);
258
+ set('page.view.title', rule.replace(new RegExp('@' + options.conf.bundle), ''));
259
+ set('page.view.namespace', namespace);
260
+ }
261
+
262
+
263
+ var ctx = getContext('gina');
264
+ // new declaration && overrides
265
+ var version = {
266
+ "number" : ctx.version,
267
+ "platform" : process.platform,
268
+ "arch" : process.arch,
269
+ "nodejs" : process.versions.node,
270
+ "middleware" : ctx.middleware
271
+ };
272
+
273
+ set('page.environment.gina', version.number);
274
+ set('page.environment.gina pid', GINA_PID);
275
+ set('page.environment.nodejs', version.nodejs +' '+ version.platform +' '+ version.arch);
276
+ set('page.environment.engine', options.conf.server.engine);//version.middleware
277
+ set('page.environment.env', process.env.NODE_ENV);
278
+ set('page.environment.envIsDev', self.isCacheless());
279
+ set('page.environment.date.now', new Date().format("isoDateTime"));
280
+
281
+
282
+ var routing = local.options.conf.routing = ctx.config.envConf.routing; // all routes
283
+ set('page.environment.routing', escape(JSON.stringify(routing))); // export for GFF
284
+ //reverseRouting
285
+ var reverseRouting = local.options.conf.reverseRouting = ctx.config.envConf.reverseRouting; // all routes
286
+ set('page.environment.reverseRouting', escape(JSON.stringify(reverseRouting))); // export for GFF
287
+
288
+ var forms = local.options.conf.forms = options.conf.content.forms // all forms
289
+ set('page.environment.forms', escape(JSON.stringify(forms))); // export for GFF
290
+ set('page.forms', options.conf.content.forms);
291
+
292
+ set('page.environment.hostname', ctx.config.envConf[options.conf.bundle][process.env.NODE_ENV].hostname);
293
+ set('page.environment.webroot', options.conf.server.webroot);
294
+ set('page.environment.bundle', options.conf.bundle);
295
+ set('page.environment.project', options.conf.projectName);
296
+ set('page.environment.protocol', options.conf.server.protocol);
297
+ set('page.environment.scheme', options.conf.server.scheme);
298
+ set('page.environment.port', options.conf.server.port);
299
+ set('page.environment.debugPort', options.conf.server.debugPort);
300
+ set('page.environment.pid', process.pid);
301
+
302
+
303
+ set('page.view.ext', ext);
304
+ set('page.view.control', action);
305
+ set('page.view.controller', local.options.controller.replace(options.conf.bundlesPath, ''), true);
306
+ if (typeof (local.options.controlRequired) != 'undefined' ) {
307
+ set('page.view.controlRequired', local.options.controlRequired);
308
+ }
309
+ set('page.view.method', local.options.method);
310
+ set('page.view.namespace', namespace); // by default
311
+ set('page.view.url', req.url);
312
+ if ( local.options.template ) {
313
+ set('page.view.layout', local.options.template.layout.replace(new RegExp(local.options.template.templates+'/'), ''));
314
+ set('page.view.html.properties.mode.javascriptsDeferEnabled', local.options.template.javascriptsDeferEnabled);
315
+ set('page.view.html.properties.mode.routeNameAsFilenameEnabled', local.options.template.routeNameAsFilenameEnabled);
316
+ }
317
+
318
+
319
+ var parameters = JSON.clone(req.getParams());
320
+ parameters = merge(parameters, options.conf.content.routing[rule].param);
321
+ // excluding default page properties
322
+ delete parameters[0];
323
+ delete parameters.file;
324
+ delete parameters.control;
325
+ delete parameters.title;
326
+
327
+ if (parameters.count() > 0)
328
+ set('page.view.params', parameters); // view parameters passed through URI or route params
329
+
330
+ set('page.view.route', rule);
331
+
332
+
333
+ var acceptLanguage = GINA_CULTURE; // by default : language-COUNTRY
334
+ if ( typeof(req.headers['accept-language']) != 'undefined' ) {
335
+ acceptLanguage = req.headers['accept-language']
336
+ } else if ( typeof(local.options.conf.server.response.header['accept-language']) != 'undefined' ) {
337
+ acceptLanguage = local.options.conf.server.response.header['accept-language']
338
+ }
339
+
340
+ // set user locale: region & culture
341
+ var userCulture = acceptLanguage.split(',')[0];
342
+ var userCultureCode = userCulture.split(/\-/);
343
+ var userLangCode = userCultureCode[0];
344
+ var userCountryCode = userCultureCode[1];
345
+
346
+ var locales = new Collection( getContext('gina').locales );
347
+ var userLocales = null;
348
+
349
+ try {
350
+ userLocales = locales.findOne({ lang: userLangCode }).content;
351
+ } catch (err) {
352
+ //var defaultRegion = (local.options.conf.content.settings.region) ? local.options.conf.content.settings.region.shortCode
353
+ console.warn('language code `'+ userLangCode +'` not handled by current locales setup: replacing by default: `'+ local.options.conf.content.settings.region.shortCode +'`');
354
+ userLocales = locales.findOne({ lang: local.options.conf.content.settings.region.shortCode }).content // by default
355
+ }
356
+
357
+ // user locales list
358
+ local.options.conf.locales = userLocales;
359
+
360
+ // user locale
361
+ options.conf.locale = new Collection(userLocales).findOne({ short: userCountryCode }) || {};
362
+
363
+ //set('page.date.now', new Date().format("isoDateTime"));
364
+ if ( typeof(options.conf.locale) == 'undefined' || !options.conf.locale ) {
365
+ options.conf.locale = {}
366
+ }
367
+ options.conf.locale.date = {
368
+ now: new Date().format("isoDateTime")
369
+ }
370
+ set('page.view.locale', options.conf.locale);
371
+ set('page.view.lang', userCulture);
372
+ }
373
+
374
+ if ( !getContext('isProxyHost') ) {
375
+ var isProxyHost = ( typeof(req.headers.host) != 'undefined' && local.options.conf.server.scheme +'://'+ req.headers.host != local.options.conf.hostname || typeof(req.headers[':authority']) != 'undefined' && local.options.conf.server.scheme +'://'+ req.headers[':authority'] != local.options.conf.hostname ) ? true : false;
376
+ setContext('isProxyHost', isProxyHost);
377
+ }
378
+
379
+ //TODO - detect when to use swig
380
+ var dir = null;
381
+ if (local.options.template || self.templates) {
382
+ dir = local.options.template.templates || self.templates;
383
+
384
+ var swigOptions = {
385
+ autoescape : ( typeof(local.options.autoescape) != 'undefined') ? local.options.autoescape : false,
386
+ // `memory` is no working yet ... advanced rendering setup required
387
+ //cache : (local.options.cacheless) ? false : 'memory'
388
+ cache : false
389
+ };
390
+ if (dir) {
391
+ swigOptions.loader = swig.loaders.fs(dir);
392
+ }
393
+ if ( typeof(local._swigOptions) == 'undefined' ) {
394
+ local._swigOptions = JSON.clone(swigOptions);
395
+ }
396
+ swig.setDefaults(swigOptions);
397
+ // used for self.engine.compile(tpl, swigOptions)(data)
398
+ swig.getOptions = function() {
399
+ return local._swigOptions;
400
+ }
401
+ // preserve the same timezone as the system
402
+ var defaultTZOffset = new Date().getTimezoneOffset();
403
+ swig.setDefaultTZOffset(defaultTZOffset);
404
+
405
+ self.engine = swig;
406
+ }
407
+ }
408
+
409
+ this.renderWithoutLayout = function (data, displayToolbar) {
410
+
411
+ // preventing multiple call of self.renderWithoutLayout() when controller is rendering from another required controller
412
+ if (local.options.renderingStack.length > 1) {
413
+ return false;
414
+ }
415
+
416
+ local.options.isWithoutLayout = true;
417
+
418
+ self.render(data, displayToolbar);
419
+ }
420
+
421
+ /**
422
+ * Render HTML templates : Swig is the default template engine
423
+ *
424
+ * Extend default filters
425
+ * - length
426
+ *
427
+ * Avilable filters:
428
+ * - getWebroot()
429
+ * - getUrl()
430
+ *
431
+ * N.B.: Filters can be extended through your `<project>/src/<bundle>/controllers/setup.js`
432
+ *
433
+ *
434
+ * @param {object} userData
435
+ * @param {boolean} [displayToolbar]
436
+ * @param {object} [errOptions]
437
+ * @returns {void}
438
+ * */
439
+ this.render = async function(userData, displayToolbar, errOptions) {
440
+ var err = null;
441
+ var isRenderingCustomError = (
442
+ typeof(userData.isRenderingCustomError) != 'undefined'
443
+ && /^true$/i.test(userData.isRenderingCustomError)
444
+ ) ? true : false;
445
+ if (isRenderingCustomError)
446
+ delete userData.isRenderingCustomError;
447
+
448
+ var localOptions = (errOptions) ? errOptions : local.options;
449
+ localOptions.renderingStack.push( self.name );
450
+ // preventing multiple call of self.render() when controller is rendering from another required controller
451
+ if ( localOptions.renderingStack.length > 1 && !isRenderingCustomError ) {
452
+ return false
453
+ }
454
+
455
+
456
+ var data = null
457
+ , layout = null
458
+ , template = null
459
+ , file = null
460
+ , path = null
461
+ , plugin = null
462
+ , isWithoutLayout = (localOptions.isWithoutLayout) ? true : false
463
+ ;
464
+
465
+ localOptions.debugMode = ( typeof(displayToolbar) == 'undefined' ) ? undefined : ( (/true/i.test(displayToolbar)) ? true : false ); // only active for dev env
466
+
467
+ // specific override
468
+ if (
469
+ self.isCacheless()
470
+ && typeof(local.req[ local.req.method.toLowerCase() ]) != 'undefined'
471
+ && typeof(local.req[ local.req.method.toLowerCase() ].debug) != 'undefined'
472
+ ) {
473
+ localOptions.debugMode = ( /true/i.test(local.req[ local.req.method.toLowerCase() ].debug) ) ? true : false;
474
+ } else if (
475
+ self.isCacheless()
476
+ && hasViews()
477
+ && !isWithoutLayout
478
+ && localOptions.debugMode == undefined
479
+ ) {
480
+ localOptions.debugMode = true;
481
+ } else if ( localOptions.debugMode == undefined ) {
482
+ localOptions.debugMode = self.isCacheless()
483
+ }
484
+
485
+ try {
486
+ data = getData();
487
+
488
+ if (!userData) {
489
+ userData = { page: { view: {}}}
490
+ } else if ( userData && !userData['page']) {
491
+
492
+ if ( typeof(data['page']['data']) == 'undefined' )
493
+ data['page']['data'] = userData;
494
+ else
495
+ data['page']['data'] = (isRenderingCustomError) ? userData : merge( userData, data['page']['data'] );
496
+ } else {
497
+ data = (isRenderingCustomError) ? userData : merge(userData, data)
498
+ }
499
+
500
+ template = localOptions.rule.replace('\@'+ localOptions.bundle, '');
501
+ var localTemplateConf = localOptions.template;
502
+ if ( isWithoutLayout ) {
503
+ localTemplateConf = JSON.clone(localOptions.template);
504
+ localTemplateConf.javascripts = new Collection(localTemplateConf.javascripts).find({ isCommon: false}, { isCommon: true, name: 'gina' });
505
+ localTemplateConf.stylesheets = new Collection(localTemplateConf.stylesheets).find({ isCommon: false}, { isCommon: true, name: 'gina' });
506
+ }
507
+ setResources(localTemplateConf);
508
+ // Allowing file & ext override
509
+ if (
510
+ typeof(local.req.routing.param.file) != 'undefined'
511
+ && data.page.view.file !== local.req.routing.param.file
512
+ ) {
513
+ data.page.view.file = localOptions.file = local.req.routing.param.file
514
+ }
515
+ if (
516
+ typeof(local.req.routing.param.ext) != 'undefined'
517
+ && data.page.view.ext !== local.req.routing.param.ext
518
+ ) {
519
+ data.page.view.ext = localOptions.template.ext = local.req.routing.param.ext
520
+ }
521
+
522
+ file = (isRenderingCustomError) ? localOptions.file : data.page.view.file;
523
+
524
+ // pre-compiling variables
525
+ data = merge(data, getData()); // needed !!
526
+
527
+ if (typeof(data.page.data) == 'undefined' ) {
528
+ data.page.data = {}
529
+ }
530
+
531
+
532
+ if (
533
+ !local.options.isRenderingCustomError
534
+ && typeof(data.page.data.status) != 'undefined'
535
+ && !/^2/.test(data.page.data.status)
536
+ && typeof(data.page.data.error) != 'undefined'
537
+ ) {
538
+ var statusCode = localOptions.conf.server.coreConfiguration.statusCodes;
539
+ var errorObject = {
540
+ status: data.page.data.status,
541
+ //errors: msg.error || msg.errors || msg,
542
+ error: statusCodes[data.page.data.status] || msg.error || msg,
543
+ message: data.page.data.message || data.page.data.error,
544
+ stack: data.page.data.stack
545
+ };
546
+ if ( typeof(data.page.data.session) != 'undefined' ) {
547
+ errorObject.session = data.page.data.session;
548
+ }
549
+ self.throwError(errorObject);
550
+ return;
551
+ }
552
+
553
+ // making path thru [namespace &] file
554
+ if ( typeof(localOptions.namespace) != 'undefined' && localOptions.namespace ) {
555
+ // excepted for custom paths
556
+ if ( !/^(\.|\/|\\)/.test(file) )
557
+ file = ''+ file.replace(localOptions.namespace+'-', '');
558
+
559
+ // means that rule name === namespace -> pointing to root namespace dir
560
+ if (!file || file === localOptions.namespace) {
561
+ file = 'index'
562
+ }
563
+ path = (isRenderingCustomError) ? _(file) : _(localOptions.template.html +'/'+ localOptions.namespace + '/' + file)
564
+ } else {
565
+ if ( localOptions.path && !/(\?|\#)/.test(localOptions.path) ) {
566
+ path = _(localOptions.path);
567
+ var re = new RegExp( data.page.view.ext+'$');
568
+ if ( data.page.view.ext && re.test(data.page.view.file) ) {
569
+ data.page.view.path = path.replace('/'+ data.page.view.file, '');
570
+
571
+ path = path.replace(re, '');
572
+ data.page.view.file = data.page.view.file.replace(re, '');
573
+
574
+ } else {
575
+ data.page.view.path = path.replace('/'+ data.page.view.file, '');
576
+ }
577
+
578
+ } else {
579
+ path = (!isRenderingCustomError && !/^(\.|\/|\\)/.test(file))
580
+ ? _(localOptions.template.html +'/'+ file)
581
+ : file
582
+ }
583
+ }
584
+
585
+ if (data.page.view.ext && !new RegExp(data.page.view.ext+ '$').test(file) /** && hasViews() && fs.existsSync(_(path + data.page.view.ext, true))*/ ) {
586
+ path += data.page.view.ext
587
+ }
588
+
589
+ data.page.view.path = path;
590
+
591
+ var dic = {}, msg = '';
592
+ for (var d in data.page) {
593
+ dic['page.'+d] = data.page[d]
594
+ }
595
+
596
+
597
+
598
+ // please, do not start with a slashe when including...
599
+ // ex.:
600
+ // /html/inc/_partial.html (BAD)
601
+ // html/inc/_partial.html (GOOD)
602
+ // ./html/namespace/page.html (GOOD)
603
+
604
+ if ( !fs.existsSync(path) ) {
605
+ msg = 'could not open "'+ path +'"' +
606
+ '\n1) The requested file does not exists in your templates/html (check your template directory). Can you find: '+path +
607
+ '\n2) Check the following rule in your `'+localOptions.conf.bundlePath+'/config/routing.json` and look around `param` to make sure that nothing is wrong with your file declaration: '+
608
+ '\n' + options.rule +':'+ JSON.stringify(options.conf.content.routing[options.rule], null, 4) +
609
+ '\n3) At this point, if you still have problems trying to run this portion of code, you can contact us telling us how to reproduce the bug.'
610
+ //'\n\r[ stack trace ] '
611
+ ;
612
+ err = new ApiError(msg, 500);
613
+ console.error(err.stack);
614
+ self.throwError(err);
615
+ return;
616
+ }
617
+
618
+ var isProxyHost = (
619
+ typeof(local.req.headers.host) != 'undefined'
620
+ && localOptions.conf.server.scheme +'://'+ local.req.headers.host != localOptions.conf.hostname
621
+ || typeof(local.req.headers[':authority']) != 'undefined'
622
+ && localOptions.conf.server.scheme +'://'+ local.req.headers[':authority'] != localOptions.conf.hostname
623
+ ) ? true : false;
624
+ // setup swig default filters
625
+ var filters = SwigFilters({
626
+ options : JSON.clone(localOptions),
627
+ isProxyHost : isProxyHost,
628
+ throwError : self.throwError,
629
+ req : local.req,
630
+ res : local.res
631
+ });
632
+
633
+ try {
634
+
635
+ // Extends default `length` filter
636
+ swig.setFilter('length', filters.length);
637
+
638
+
639
+
640
+ // Allows you to get a bundle web root
641
+ swig.setFilter('getWebroot', filters.getWebroot);
642
+
643
+ swig.setFilter('getUrl', filters.getUrl);
644
+
645
+ } catch (err) {
646
+ // [ martin ]
647
+ // i sent an email to [ paul@paularmstrongdesigns.com ] on 2014/08 to see if there is:
648
+ // a way of retrieving swig compilation stack traces
649
+ //var stack = __stack.splice(1).toString().split(',').join('\n');
650
+ // -> no response...
651
+ self.throwError(local.res, 500, new Error('template compilation exception encoutered: [ '+path+' ]\n'+(err.stack||err.message)));
652
+ return;
653
+ }
654
+
655
+
656
+
657
+ var layoutPath = null
658
+ , assets = null
659
+ , mapping = null
660
+ , XHRData = null
661
+ , XHRView = null
662
+ , isDeferModeEnabled = null
663
+ , viewInfos = null
664
+ , filename = null
665
+ , isWithSwigLayout = null
666
+ , isUsingGinaLayout = (!isWithoutLayout && typeof(localOptions.template.layout) != 'undefined' && fs.existsSync(local.options.template.layout)) ? true : false
667
+ ;
668
+
669
+ if ( isWithoutLayout || isUsingGinaLayout ) {
670
+ layoutPath = (isWithoutLayout) ? localOptions.template.noLayout : localOptions.template.layout;
671
+ // user layout override
672
+ if ( isUsingGinaLayout && !isWithoutLayout ) {
673
+ layoutPath = localOptions.template.layout;
674
+ }
675
+ if (isWithoutLayout) {
676
+ data.page.view.layout = layoutPath;
677
+ }
678
+ } else { // without layout case
679
+
680
+ // by default
681
+ layoutPath = localOptions.template.layout;
682
+ if ( !/^\//.test(layoutPath)) {
683
+ layoutPath = localOptions.template.templates +'/'+ layoutPath;
684
+ }
685
+ // default layout
686
+ if (
687
+ !isWithoutLayout && !fs.existsSync(layoutPath) && layoutPath == localOptions.template.templates +'/index.html'
688
+ ) {
689
+ console.warn('Layout '+ local.options.template.layout +' not found, replacing with `nolayout`: '+ localOptions.template.noLayout);
690
+ layoutPath = localOptions.template.noLayout
691
+ isWithoutLayout = true;
692
+ data.page.view.layout = layoutPath;
693
+ }
694
+ // user defiend layout
695
+ else if ( !isWithoutLayout && !fs.existsSync(layoutPath) ) {
696
+ isWithSwigLayout = true;
697
+ layoutPath = localOptions.template.noLayout;
698
+ data.page.view.layout = layoutPath;
699
+ }
700
+ // layout defiendd but not found
701
+ else if (!fs.existsSync(layoutPath) ) {
702
+ err = new ApiError(options.bundle +' SuperController exception while trying to load your layout `'+ layoutPath +'`.\nIt seems like you have defined a layout, but gina could not locate the file.\nFor more informations, check your `config/templates.json` declaration around `'+ local.options.rule.replace(/\@(.*)/g, '') +'`', 500);
703
+ self.throwError(err);
704
+ return;
705
+ }
706
+
707
+ }
708
+
709
+
710
+ var isLoadingPartial = false;
711
+ try {
712
+ assets = {assets:"${assets}"};
713
+
714
+ /**
715
+ * retrieve template & layout
716
+ * */
717
+ var tpl = null;
718
+ // tpl = fs.readFileSync(path).toString();
719
+ // layout = fs.readFileSync(layoutPath).toString();
720
+
721
+ await Promise.all([
722
+ readFile(layoutPath),
723
+ readFile(path)
724
+ ])
725
+ .then(([_layout, _tpl]) => {
726
+ layout = _layout.toString();
727
+ tpl = _tpl.toString();
728
+ })
729
+ .catch(error => {
730
+ console.error(error.message);
731
+ return;
732
+ });
733
+
734
+
735
+ // mappin conf
736
+ mapping = { filename: path };
737
+ if (isRenderingCustomError) {
738
+ // TODO - Test if there is a block call `gina-error` in the layout & replace block name from tpl
739
+
740
+ if ( !/\{\%(\s+extends|extends)/.test(tpl) ) {
741
+ tpl = "\n{% extends '"+ layoutPath +"' %}\n" + tpl;
742
+ }
743
+ if (!/\{\% block content/.test(tpl)) {
744
+ // TODO - test if lyout has <body>
745
+ tpl = '{% block content %}<p>If you view this message you didn’t define a content block in your template.</p>{% endblock %}' + tpl;
746
+ }
747
+
748
+ tpl = tpl.replace(/\{\{ page\.content \}\}/g, '');
749
+ }
750
+
751
+ if ( isWithoutLayout || isWithSwigLayout) {
752
+ layout = tpl;
753
+ } else if (isUsingGinaLayout) {
754
+ mapping = { filename: path };
755
+ if ( /(\{\{|\{\{\s+)page\.content/.test(layout) ) {
756
+
757
+ if ( /\{\%(\s+extends|extends)/.test(tpl) ) {
758
+ err = new Error('You cannot use at the same time `page.content` in your layout `'+ layoutPath +'` while calling `extends` from your page or content `'+ path +'`. You have to choose one or the other');
759
+ self.throwError(local.res, 500, err);
760
+ return
761
+ }
762
+ layout = layout.replace('{{ page.content }}', tpl);
763
+ } else {
764
+ layout = layout.replace(/\<\/body\>/i, '\t'+tpl+'\n</body>');
765
+ }
766
+
767
+ } else {
768
+ tpl = tpl.replace('{{ page.view.layout }}', data.page.view.layout);
769
+ if (/\<\/body\>/i.test(layout)) {
770
+ layout = layout.replace(/\<\/body\>/i, '\t'+tpl+'\n</body>');
771
+ }
772
+ else {
773
+ layout += tpl;
774
+ }
775
+ }
776
+
777
+ // precompilation needed in case of `extends` or in order to display the toolbar
778
+ if ( hasViews() && self.isCacheless() || /\{\%(\s+extends|extends)/.test(layout) ) {
779
+ layout = swig.compile(layout, mapping)(data);
780
+ }
781
+ //dic['page.content'] = layout;
782
+
783
+ } catch(err) {
784
+ err.stack = 'Exception, bad syntax or undefined data found: start investigating in '+ mapping.filename +'\n' + err.stack;
785
+ self.throwError(local.res, 500, err);
786
+ return
787
+ }
788
+
789
+ isLoadingPartial = (
790
+ !/\<html/i.test(layout)
791
+ || !/\<head/i.test(layout)
792
+ || !/\<body/i.test(layout)
793
+ ) ? true : false;
794
+
795
+ // if (isLoadingPartial) {
796
+ // console.warn('----------------> loading partial `'+ path);
797
+ // }
798
+
799
+ isDeferModeEnabled = localOptions.template.javascriptsDeferEnabled || localOptions.conf.content.templates._common.javascriptsDeferEnabled || false;
800
+
801
+ // iframe case - without HTML TAG
802
+ if (!self.isXMLRequest() && !/\<html/.test(layout) ) {
803
+ layout = '<html>\n\t<head></head>\n\t<body class="gina-iframe-body">\n\t\t'+ layout +'\n\t</body>\n</html>';
804
+ }
805
+
806
+ // adding stylesheets
807
+ if (!isWithoutLayout && data.page.view.stylesheets && !/\{\{\s+(page\.view\.stylesheets)\s+\}\}/.test(layout) ) {
808
+ layout = layout.replace(/\<\/head\>/i, '\n\t{{ page.view.stylesheets }}\n</head>')
809
+ }
810
+
811
+ if (hasViews() && isWithoutLayout) {
812
+ // $.getScript(...)
813
+ //var isProxyHost = ( typeof(local.req.headers.host) != 'undefined' && localOptions.conf.server.scheme +'://'+ local.req.headers.host != localOptions.conf.hostname || typeof(local.req.headers[':authority']) != 'undefined' && localOptions.conf.server.scheme +'://'+ local.req.headers[':authority'] != localOptions.conf.hostname ) ? true : false;
814
+ //var hostname = (isProxyHost) ? localOptions.conf.hostname.replace(/\:\d+$/, '') : localOptions.conf.hostname;
815
+
816
+
817
+
818
+ var scripts = data.page.view.scripts;
819
+ scripts = scripts.replace(/\s+\<script/g, '\n<script');
820
+
821
+ if (!isProxyHost) {
822
+ var webroot = data.page.environment.webroot;
823
+ scripts = scripts.replace(/src\=\"\/(.*)\"/g, 'src="'+ webroot +'$1"');
824
+ //stylesheets = stylesheets.replace(/href\=\"\/(.*)\"/g, 'href="'+ webroot +'$1"')
825
+ }
826
+
827
+ // iframe case - without HTML TAG
828
+ if (self.isXMLRequest() || !/\<html/.test(layout) ) {
829
+ layout += scripts;
830
+ //layout += stylesheets;
831
+ }
832
+
833
+ }
834
+
835
+ // adding plugins
836
+
837
+
838
+ if (
839
+ hasViews() && self.isCacheless() && !isWithoutLayout
840
+ && localOptions.debugMode
841
+ ||
842
+ hasViews() && self.isCacheless() && !isWithoutLayout
843
+ && typeof(localOptions.debugMode) == 'undefined'
844
+ ||
845
+ hasViews() && localOptions.debugMode
846
+ ) {
847
+
848
+ layout = ''
849
+ + '{%- set ginaDataInspector = JSON.clone(page) -%}'
850
+ + '{%- set ginaDataInspector.view.assets = {} -%}'
851
+ + '{%- set ginaDataInspector.view.scripts = "ignored-by-toolbar" -%}'
852
+ + '{%- set ginaDataInspector.view.stylesheets = "ignored-by-toolbar" -%}'
853
+ + layout
854
+ ;
855
+
856
+ plugin = '\t'
857
+ + '{# Gina Toolbar #}'
858
+ + '{%- set userDataInspector = JSON.clone(page) -%}'
859
+ + '{%- set userDataInspector.view.scripts = "ignored-by-toolbar" -%}'
860
+ + '{%- set userDataInspector.view.stylesheets = "ignored-by-toolbar" -%}'
861
+ + '{%- set userDataInspector.view.assets = '+ JSON.stringify(assets) +' -%}'
862
+ + '{%- include "'+ getPath('gina').core +'/asset/js/plugin/src/gina/toolbar/toolbar.html" with { gina: ginaDataInspector, user: userDataInspector } -%}'
863
+ + '{# END Gina Toolbar #}'
864
+ ;
865
+
866
+
867
+ if (isWithoutLayout && localOptions.debugMode || localOptions.debugMode ) {
868
+
869
+ XHRData = '\t<input type="hidden" id="gina-without-layout-xhr-data" value="'+ encodeURIComponent(JSON.stringify(data.page.data)) +'">\n\r';
870
+
871
+ layout = layout.replace(/<\/body>/i, XHRData + '\n\t</body>');
872
+ }
873
+
874
+ if (self.isCacheless() || localOptions.debugMode ) {
875
+ layout = layout.replace(/<\/body>/i, plugin + '\n\t</body>');
876
+ }
877
+
878
+ // adding javascripts
879
+ layout.replace('{{ page.view.scripts }}', '');
880
+ // placed in the HEAD excepted when rendering a partial or when `isDeferModeEnabled` == true
881
+ if (isLoadingPartial) {
882
+ layout += '\t{{ page.view.scripts }}';
883
+ } else {
884
+ if ( isDeferModeEnabled ) {
885
+ layout = layout.replace(/\<\/head\>/i, '\t{{ page.view.scripts }}\n\t</head>');
886
+ } else { // placed in the BODY
887
+ layout = layout.replace(/\<\/body\>/i, '\t{{ page.view.scripts }}\n</body>');
888
+ }
889
+ }
890
+
891
+ // ginaLoader cannot be deferred
892
+ if ( !localOptions.template.javascriptsExcluded || localOptions.template.javascriptsExcluded != '**' ) {
893
+ layout = layout.replace(/\<\/head\>/i, '\t'+ localOptions.template.ginaLoader +'\n</head>');
894
+ }
895
+
896
+ } else if ( hasViews() && self.isCacheless() && self.isXMLRequest() ) {
897
+
898
+ if (isWithoutLayout) {
899
+ delete data.page.view.scripts;
900
+ delete data.page.view.stylesheets;
901
+ }
902
+ // means that we don't want GFF context or we already have it loaded
903
+ viewInfos = JSON.clone(data.page.view);
904
+ if ( !isWithoutLayout )
905
+ viewInfos.assets = assets;
906
+
907
+ XHRData = '\n<input type="hidden" id="gina-without-layout-xhr-data" value="'+ encodeURIComponent(JSON.stringify(data.page.data)) +'">';
908
+ XHRView = '\n<input type="hidden" id="gina-without-layout-xhr-view" value="'+ encodeURIComponent(JSON.stringify(viewInfos)) +'">';
909
+
910
+
911
+ layout += XHRData + XHRView;
912
+
913
+ } else { // other envs like prod ...
914
+
915
+ // adding javascripts
916
+ // cleanup first
917
+ layout.replace('{{ page.view.scripts }}', '');
918
+ // placed in the HEAD excepted when rendering a partial or when `isDeferModeEnabled` == true
919
+ // if (isLoadingPartial) {
920
+ // layout += '\t{{ page.view.scripts }}';
921
+ // } else {
922
+ // if ( isDeferModeEnabled ) {
923
+ // layout = layout.replace(/\<\/head\>/i, '\t{{ page.view.scripts }}\n\t</head>');
924
+ // } else { // placed in the BODY
925
+ // layout = layout.replace(/\<\/body\>/i, '\t{{ page.view.scripts }}\n</body>');
926
+ // }
927
+ // }
928
+
929
+ // // ginaLoader cannot be deferred
930
+ // if ( !localOptions.template.javascriptsExcluded || localOptions.template.javascriptsExcluded != '**' ) {
931
+ // layout = layout.replace(/\<\/head\>/i, '\t'+ localOptions.template.ginaLoader +'\n</head>');
932
+ // }
933
+
934
+ // adding javascripts
935
+ layout.replace('{{ page.view.scripts }}', '');
936
+ if (isLoadingPartial) {
937
+ layout += '\t{{ page.view.scripts }}\n';
938
+ if ( !localOptions.template.javascriptsExcluded || localOptions.template.javascriptsExcluded != '**' ) {
939
+ layout += '\t'+ localOptions.template.ginaLoader +'\n';
940
+ }
941
+ } else {
942
+ if ( isDeferModeEnabled && /\<\/head\>/i.test(layout) ) { // placed in the HEAD
943
+ layout = layout.replace(/\<\/head\>/i, '\t{{ page.view.scripts }}\n\t</head>');
944
+
945
+ } else { // placed in the BODY
946
+ layout = layout.replace(/\<\/body\>/i, '\t{{ page.view.scripts }}\n</body>');
947
+ }
948
+ // ginaLoader cannot be deferred
949
+ if ( !localOptions.template.javascriptsExcluded || localOptions.template.javascriptsExcluded != '**' ) {
950
+ layout = layout.replace(/\<\/head\>/i, '\t'+ localOptions.template.ginaLoader +'\n</head>');
951
+ }
952
+ }
953
+ }
954
+
955
+
956
+ layout = whisper(dic, layout, /\{{ ([a-zA-Z.]+) \}}/g );
957
+ dic['page.content'] = layout;
958
+ /**
959
+ // special case for template without layout in debug mode - dev only
960
+ if ( hasViews() && localOptions.debugMode && self.isCacheless() && !/\{\# Gina Toolbar \#\}/.test(layout) ) {
961
+ try {
962
+
963
+ layout = layout.replace(/<\/body>/i, plugin + '\n\t</body>');
964
+ layout = whisper(dic, layout, /\{{ ([a-zA-Z.]+) \}}/g );
965
+ //swig.invalidateCache();
966
+ layout = swig.compile(layout, mapping)(data);
967
+
968
+
969
+ } catch (err) {
970
+ filename = localOptions.template.html;
971
+ filename += ( typeof(data.page.view.namespace) != 'undefined' && data.page.view.namespace != '' && new RegExp('^' + data.page.view.namespace +'-').test(data.page.view.file) ) ? '/' + data.page.view.namespace + data.page.view.file.split(data.page.view.namespace +'-').join('/') + ( (data.page.view.ext != '') ? data.page.view.ext: '' ) : '/' + data.page.view.file+ ( (data.page.view.ext != '') ? data.page.view.ext: '' );
972
+ self.throwError(local.res, 500, new Error('Compilation error encountered while trying to process template `'+ filename + '`\n'+(err.stack||err.message)));
973
+ return;
974
+ }
975
+ }
976
+ else if (hasViews() && localOptions.debugMode && self.isCacheless()) {
977
+ try {
978
+ //layout = whisper(dic, layout, /\{{ ([a-zA-Z.]+) \}}/g );
979
+ layout = swig.compile(layout, mapping)(data);
980
+ } catch (err) {
981
+ filename = localOptions.template.html;
982
+ filename += ( typeof(data.page.view.namespace) != 'undefined' && data.page.view.namespace != '' && new RegExp('^' + data.page.view.namespace +'-').test(data.page.view.file) ) ? '/' + data.page.view.namespace + data.page.view.file.split(data.page.view.namespace +'-').join('/') + ( (data.page.view.ext != '') ? data.page.view.ext: '' ) : '/' + data.page.view.file+ ( (data.page.view.ext != '') ? data.page.view.ext: '' );
983
+ self.throwError(local.res, 500, new Error('Compilation error encountered while trying to process template `'+ filename + '`\n'+(err.stack||err.message)));
984
+ return;
985
+ }
986
+ }
987
+ */
988
+
989
+
990
+ if ( !local.res.headersSent ) {
991
+ local.res.statusCode = ( typeof(localOptions.conf.server.coreConfiguration.statusCodes[data.page.data.status]) != 'undefined' ) ? data.page.data.status : 200; // by default
992
+ //catching errors
993
+ if (
994
+ typeof(data.page.data.errno) != 'undefined' && /^2/.test(data.page.data.status) && typeof(localOptions.conf.server.coreConfiguration.statusCodes[data.page.data.status]) != 'undefined'
995
+ || typeof(data.page.data.status) != 'undefined' && !/^2/.test(data.page.data.status) && typeof(localOptions.conf.server.coreConfiguration.statusCodes[data.page.data.status]) != 'undefined'
996
+ ) {
997
+
998
+ try {
999
+ local.res.statusMessage = localOptions.conf.server.coreConfiguration.statusCodes[data.page.data.status];
1000
+ } catch (err){
1001
+ local.res.statusCode = 500;
1002
+ local.res.statusMessage = err.stack||err.message||localOptions.conf.server.coreConfiguration.statusCodes[local.res.statusCode];
1003
+ }
1004
+ }
1005
+
1006
+ local.res.setHeader('content-type', localOptions.conf.server.coreConfiguration.mime['html'] + '; charset='+ localOptions.conf.encoding );
1007
+
1008
+ try {
1009
+
1010
+ // escape special chars
1011
+ var blacklistRe = new RegExp('[\<\>]', 'g');
1012
+ // DO NOT REPLACE IT BY JSON.clone() !!!!
1013
+
1014
+ data.page.data = JSON.parse(JSON.stringify(data.page.data).replace(blacklistRe, '\$&'));
1015
+
1016
+ } catch (err) {
1017
+ filename = localOptions.template.html;
1018
+ filename += ( typeof(data.page.view.namespace) != 'undefined' && data.page.view.namespace != '' && new RegExp('^' + data.page.view.namespace +'-').test(data.page.view.file) ) ? '/' + data.page.view.namespace + data.page.view.file.split(data.page.view.namespace +'-').join('/') + ( (data.page.view.ext != '') ? data.page.view.ext: '' ) : '/' + data.page.view.file+ ( (data.page.view.ext != '') ? data.page.view.ext: '' );
1019
+ self.throwError(local.res, 500, new Error('Controller::render(...) compilation error encountered while trying to process template `'+ filename + '`\n' + (err.stack||err.message||err) ));
1020
+ return;
1021
+ }
1022
+
1023
+
1024
+ // Only available for http/2.0 for now
1025
+ if ( !self.isXMLRequest() && /http\/2/.test(localOptions.conf.server.protocol) ) {
1026
+ try {
1027
+ // TODO - button in toolbar to empty url assets cache
1028
+ if ( /** self.isCacheless() ||*/ typeof(localOptions.template.assets) == 'undefined' || typeof(localOptions.template.assets[local.req.url]) == 'undefined' ) {
1029
+ // assets string -> object
1030
+ //assets = self.serverInstance.getAssets(localOptions.conf, layout.toString(), swig, data);
1031
+ assets = self.serverInstance.getAssets(localOptions.conf, layout, swig, data);
1032
+ localOptions.template.assets = JSON.parse(assets);
1033
+ }
1034
+
1035
+ // only for toolbar - TODO hasToolbar()
1036
+ if (
1037
+ self.isCacheless() && hasViews() && !isWithoutLayout
1038
+ || hasViews() && localOptions.debugMode
1039
+ || self.isCacheless() && hasViews() && self.isXMLRequest()
1040
+ ) {
1041
+ layout = layout.replace('{"assets":"${assets}"}', assets );
1042
+ }
1043
+
1044
+ } catch (err) {
1045
+ self.throwError(local.res, 500, new Error('Controller::render(...) calling getAssets(...) \n' + (err.stack||err.message||err) ));
1046
+ return;
1047
+ }
1048
+ }
1049
+
1050
+ // Last compilation before rendering
1051
+ layout = swig.compile(layout, mapping)(data);
1052
+
1053
+ if ( !local.res.headersSent ) {
1054
+ if ( local.options.isRenderingCustomError ) {
1055
+ local.options.isRenderingCustomError = false;
1056
+ }
1057
+ local.res.end(layout);
1058
+ }
1059
+
1060
+ console.info(local.req.method +' ['+local.res.statusCode +'] '+ local.req.url);
1061
+
1062
+ } else if (typeof(local.next) != 'undefined') {
1063
+ // local.next();
1064
+ return local.next();
1065
+ } else {
1066
+ if ( typeof(local.req.params.errorObject) != 'undefined' ) {
1067
+ self.throwError(local.req.params.errorObject);
1068
+ return;
1069
+ }
1070
+ local.res.end('Unexpected controller error while trying to render.');
1071
+ return;
1072
+ }
1073
+ } catch (err) {
1074
+ self.throwError(local.res, 500, err);
1075
+ return;
1076
+ }
1077
+ }
1078
+
1079
+
1080
+ this.isXMLRequest = function() {
1081
+ return local.options.isXMLRequest;
1082
+ }
1083
+
1084
+ this.isWithCredentials = function() {
1085
+ return ( /true/.test(local.options.withCredentials) ) ? true : false;
1086
+ }
1087
+
1088
+ /**
1089
+ * Render JSON
1090
+ *
1091
+ * @param {object|string} jsonObj
1092
+ * @param {object} [req]
1093
+ * @param {object} [res]
1094
+ *
1095
+ * @callback {function} [next]
1096
+ *
1097
+ * */
1098
+ this.renderJSON = function(jsonObj) {
1099
+
1100
+ // preventing multiple call of self.renderJSON() when controller is rendering from another required controller
1101
+ if (local.options.renderingStack.length > 1) {
1102
+ return false
1103
+ }
1104
+ if ( self.isProcessingError ) {
1105
+ return;
1106
+ }
1107
+
1108
+ var request = local.req;
1109
+ var response = local.res;
1110
+ var next = local.next || null;
1111
+ // var stream = null;
1112
+ // if ( /http\/2/.test(local.options.conf.server.protocol) ) {
1113
+ // stream = response.stream;
1114
+ // }
1115
+
1116
+ if (!jsonObj) {
1117
+ jsonObj = {}
1118
+ }
1119
+
1120
+ try {
1121
+ // just in case
1122
+ if ( typeof(jsonObj) == 'string') {
1123
+ jsonObj = JSON.parse(jsonObj)
1124
+ }
1125
+
1126
+ // if( typeof(local.options) != "undefined" && typeof(local.options.charset) != "undefined" ){
1127
+ // response.setHeader("charset", local.options.charset);
1128
+ // }
1129
+
1130
+
1131
+ //catching errors
1132
+ if (
1133
+ typeof(jsonObj.errno) != 'undefined' && response.statusCode == 200
1134
+ || typeof(jsonObj.status) != 'undefined' && jsonObj.status != 200 && typeof(local.options.conf.server.coreConfiguration.statusCodes[jsonObj.status]) != 'undefined'
1135
+ ) {
1136
+
1137
+ try {
1138
+ response.statusCode = jsonObj.status;
1139
+ response.statusMessage = local.options.conf.server.coreConfiguration.statusCodes[jsonObj.status];
1140
+ } catch (err){
1141
+ response.statusCode = 500;
1142
+ response.statusMessage = err.stack;
1143
+ }
1144
+ }
1145
+
1146
+
1147
+ // Internet Explorer override
1148
+ if ( /msie/i.test(request.headers['user-agent']) ) {
1149
+ response.setHeader('content-type', 'text/plain' + '; charset='+ local.options.conf.encoding)
1150
+ } else {
1151
+ response.setHeader('content-type', local.options.conf.server.coreConfiguration.mime['json'] + '; charset='+ local.options.conf.encoding)
1152
+ }
1153
+
1154
+ if ( !response.headersSent ) {
1155
+ console.info(request.method +' ['+ response.statusCode +'] '+ request.url);
1156
+
1157
+ if ( local.options.isXMLRequest && self.isWithCredentials() ) {
1158
+
1159
+ var data = JSON.stringify(jsonObj);
1160
+ var len = 0;
1161
+ // content length must be the right size !
1162
+ if ( typeof(data) === 'string') {
1163
+ len = Buffer.byteLength(data, 'utf8')
1164
+ } else {
1165
+ len = data.length
1166
+ }
1167
+
1168
+ if (!response.headersSent)
1169
+ response.setHeader("content-length", len);
1170
+
1171
+
1172
+ // if (stream && !stream.destroyed) {
1173
+ // //stream.respond(header);
1174
+ // stream.end(data);
1175
+ // } else {
1176
+ response.write(data);
1177
+
1178
+ // required to close connection
1179
+ setTimeout(function () {
1180
+ response.end();
1181
+ try {
1182
+ response.headersSent = true;
1183
+ } catch(err) {
1184
+ // Ignoring warning
1185
+ //console.warn(err);
1186
+ }
1187
+
1188
+ if ( next ) {
1189
+ next()
1190
+ }
1191
+ }, 200);
1192
+
1193
+
1194
+
1195
+ return // force completion
1196
+ // }
1197
+
1198
+
1199
+ } else { // normal case
1200
+ response.end(JSON.stringify(jsonObj));
1201
+ if (!response.headersSent) {
1202
+ try {
1203
+ response.headersSent = true;
1204
+ } catch(err) {
1205
+ // Ignoring warning
1206
+ //console.warn(err);
1207
+ }
1208
+ }
1209
+ if ( next ) {
1210
+ next()
1211
+ }
1212
+
1213
+ return;
1214
+ }
1215
+ }
1216
+ } catch (err) {
1217
+ self.throwError(response, 500, err);
1218
+ return;
1219
+ }
1220
+
1221
+ }
1222
+
1223
+
1224
+ this.renderTEXT = function(content) {
1225
+
1226
+ // preventing multiple call of self.renderTEXT() when controller is rendering from another required controller
1227
+ if (local.options.renderingStack.length > 1) {
1228
+ return false
1229
+ }
1230
+
1231
+ if ( typeof(content) != "string" ) {
1232
+ content = content.toString();
1233
+ }
1234
+
1235
+ // if (typeof(options) != "undefined" && typeof(options.charset) !="undefined") {
1236
+ // local.res.setHeader("charset", options.charset);
1237
+ // }
1238
+ if ( !local.res.getHeaders()['content-type'] ) {
1239
+ local.res.setHeader('content-type', 'text/plain' + '; charset='+ local.options.conf.encoding);
1240
+ }
1241
+
1242
+ if ( !local.res.headersSent ) {
1243
+ console.info(local.req.method +' ['+local.res.statusCode +'] '+ local.req.url);
1244
+ local.res.end(content);
1245
+ try {
1246
+ local.res.headersSent = true
1247
+ } catch(err) {
1248
+ // Ignoring warning
1249
+ //console.warn(err);
1250
+ }
1251
+ }
1252
+ }
1253
+
1254
+ var parseDataObject = function(o, obj, override) {
1255
+
1256
+ for (var i in o) {
1257
+ if ( o[i] !== null && typeof(o[i]) == 'object' || override && o[i] !== null && typeof(o[i]) == 'object' ) {
1258
+ parseDataObject(o[i], obj);
1259
+ } else if (o[i] == '_content_'){
1260
+ o[i] = obj
1261
+ }
1262
+ }
1263
+
1264
+ return o
1265
+ }
1266
+
1267
+ /**
1268
+ * Set data
1269
+ *
1270
+ * @param {string} nave - variable name to set
1271
+ * @param {string|object} value - value to set
1272
+ * @param {boolean} [override]
1273
+ *
1274
+ * @returns {void}
1275
+ * */
1276
+ var set = function(name, value, override) {
1277
+
1278
+ var override = ( typeof(override) != 'undefined' ) ? override : false;
1279
+
1280
+ if ( typeof(name) == 'string' && /\./.test(name) ) {
1281
+ var keys = name.split(/\./g)
1282
+ , newObj = {}
1283
+ , str = '{'
1284
+ , _count = 0;
1285
+
1286
+ for (var k = 0, len = keys.length; k<len; ++k) {
1287
+ str += "\""+ keys.splice(0,1)[0] + "\":{";
1288
+
1289
+ ++_count;
1290
+ if (k == len-1) {
1291
+ str = str.substr(0, str.length-1);
1292
+ str += "\"_content_\"";
1293
+ for (var c = 0; c<_count; ++c) {
1294
+ str += "}"
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ newObj = parseDataObject(JSON.parse(str), value, override);
1300
+ local.userData = merge(local.userData, newObj);
1301
+
1302
+ } else if ( typeof(local.userData[name]) == 'undefined' ) {
1303
+ local.userData[name] = value.replace(/\\/g, '')
1304
+ }
1305
+ }
1306
+
1307
+ /**
1308
+ * Get data
1309
+ *
1310
+ * @param {String} variable Data name to set
1311
+ * @returns {Object | String} data Data object or String
1312
+ * */
1313
+ var get = function(variable) {
1314
+ return local.userData[variable]
1315
+ }
1316
+
1317
+ /**
1318
+ * Set resources
1319
+ *
1320
+ * @param {object} template - template configuration
1321
+ * */
1322
+ var setResources = function(viewConf) {
1323
+ if (!viewConf) {
1324
+ self.throwError(500, new Error('No views configuration found. Did you try to add views before using Controller::render(...) ? Try to run: gina view:add '+ options.conf.bundle +' @'+ options.conf.projectName));
1325
+ return;
1326
+ }
1327
+
1328
+ var authority = ( typeof(local.req.headers['x-forwarded-proto']) != 'undefined' ) ? local.req.headers['x-forwarded-proto'] : local.options.conf.server.scheme;
1329
+ authority += '://'+ local.req.headers.host;
1330
+ var useWebroot = false;
1331
+ if ( !/^\/$/.test(local.options.conf.server.webroot) && local.options.conf.server.webroot.length > 0 && local.options.conf.hostname.replace(/\:\d+$/, '') == authority ) {
1332
+ useWebroot = true
1333
+ }
1334
+
1335
+ var reURL = new RegExp('^'+ local.options.conf.server.webroot);
1336
+
1337
+ var cssStr = '', jsStr = '';
1338
+
1339
+ //Get css
1340
+ if( viewConf.stylesheets ) {
1341
+ cssStr = getNodeRes('css', viewConf.stylesheets, useWebroot, reURL)
1342
+ }
1343
+ //Get js
1344
+ if( viewConf.javascripts ) {
1345
+ jsStr = getNodeRes('js', viewConf.javascripts, useWebroot, reURL)
1346
+ }
1347
+
1348
+ set('page.view.stylesheets', cssStr);
1349
+ set('page.view.scripts', jsStr);
1350
+ }
1351
+
1352
+ /**
1353
+ * Get node resources
1354
+ *
1355
+ * @param {string} type
1356
+ * @param {string} resStr
1357
+ * @param {array} resArr
1358
+ * @param {object} resObj
1359
+ *
1360
+ * @returns {object} content
1361
+ *
1362
+ * @private
1363
+ * */
1364
+ var getNodeRes = function(type, resArr, useWebroot, reURL) {
1365
+
1366
+ var r = 0
1367
+ , rLen = resArr.length
1368
+ , obj = null
1369
+ , str = ''
1370
+ ;
1371
+ switch(type){
1372
+ case 'css':
1373
+ for (; r < rLen; ++r) {
1374
+ obj = resArr[r];
1375
+ if (useWebroot && !reURL.test(obj.url) ) {
1376
+ obj.url = local.options.conf.server.webroot + obj.url.substr(1);
1377
+ }
1378
+ // TODO - add support for cdn
1379
+ if (!/\:\/\//.test(obj.url) ) {
1380
+ obj.url = local.options.conf.hostname + obj.url;
1381
+ }
1382
+
1383
+ if (obj.media)
1384
+ str += '\n\t\t<link href="'+ obj.url +'" media="'+ obj.media +'" rel="'+ obj.rel +'" type="'+ obj.type +'">';
1385
+ else
1386
+ str += '\n\t\t<link href="'+ obj.url +'" rel="'+ obj.rel +'" type="'+ obj.type +'">';
1387
+ }
1388
+
1389
+ return str;
1390
+ break;
1391
+
1392
+ case 'js':
1393
+ var deferMode = (local.options.template.javascriptsDeferEnabled) ? ' defer' : '';
1394
+
1395
+ for (; r < rLen; ++r) {
1396
+ obj = resArr[r];
1397
+ if (useWebroot && !reURL.test(obj.url) ) {
1398
+ obj.url = local.options.conf.server.webroot + obj.url.substr(1);
1399
+ }
1400
+ // TODO - add support for cdn
1401
+ if (!/\:\/\//.test(obj.url) ) {
1402
+ obj.url = local.options.conf.hostname + obj.url;
1403
+ }
1404
+ str += '\n\t\t<script'+ deferMode +' type="'+ obj.type +'" src="'+ obj.url +'"></script>'
1405
+ }
1406
+
1407
+ return str;
1408
+ break;
1409
+ }
1410
+ }
1411
+
1412
+ /**
1413
+ * TODO - SuperController.setMeta()
1414
+ * */
1415
+ // this.setMeta = function(metaName, metacontent) {
1416
+ //
1417
+ // }
1418
+
1419
+ var getData = function() {
1420
+ return refToObj( local.userData )
1421
+ }
1422
+
1423
+
1424
+ var isValidURL = function(url){
1425
+ var re = /(http|ftp|https|sftp):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&amp;:\/~+#-]*[\w@?^=%&amp;\/~+#-])?/;
1426
+ return (re.test(url)) ? true : false
1427
+ }
1428
+
1429
+ /**
1430
+ * Set method - Override current method
1431
+ * E.g.: in case of redirect, to force PUT to GET
1432
+ *
1433
+ * @param {string} requestMethod - GET, POST, PUT, DELETE
1434
+ */
1435
+ var localRequestMethod = null, localRequestMethodParams = null;
1436
+ this.setRequestMethod = function(requestMethod, conf) {
1437
+ // http/2 case
1438
+ if ( /http\/2/i.test(conf.server.protocolShort) ) {
1439
+ local.req.headers[':method'] = local.req.method.toUpperCase()
1440
+ }
1441
+
1442
+ localRequestMethod = local.req.method = local.req.routing.method = requestMethod.toUpperCase();
1443
+
1444
+ local.res.setHeader('access-control-allow-methods', localRequestMethod);
1445
+
1446
+ return localRequestMethod;
1447
+ }
1448
+
1449
+ this.getRequestMethod = function() {
1450
+ return localRequestMethod;
1451
+ }
1452
+
1453
+ this.setRequestMethodParams = function(params) {
1454
+ localRequestMethodParams = local.req[local.req.method.toLowerCase()] = localRequestMethodParams = params
1455
+ }
1456
+
1457
+ this.getRequestMethodParams = function() {
1458
+ return (localRequestMethodParams) ? localRequestMethodParams : local.req[local.req.method.toLowerCase()]
1459
+ }
1460
+
1461
+ /**
1462
+ * isStaticRoute
1463
+ * Trying to determine if url is a `statics` ressource
1464
+ *
1465
+ * @param {string} url
1466
+ * @param {string} method
1467
+ *
1468
+ * @returns {boolean} isStaticRoute
1469
+ */
1470
+ var isStaticRoute = function(url, method, bundle, env, conf) {
1471
+
1472
+ if ( !/get/i.test(method) ) {
1473
+ return false
1474
+ }
1475
+
1476
+ // priority to statics - this portion of code has been duplicated to Server.js
1477
+
1478
+ var staticsArr = conf[bundle][env].publicResources;
1479
+ var staticProps = {
1480
+ firstLevel : '/' + url.split(/\//g)[1] + '/',
1481
+ // to be considered as a stativ content, url must content at least 2 caracters after last `.`: .js, .html are ok
1482
+ isStaticFilename : /(\.([A-Za-z0-9]+){2}|\/)$/.test(url)
1483
+ };
1484
+
1485
+ // handle resources from public with webroot in url
1486
+ if ( staticProps.isStaticFilename && conf[bundle][env].server.webroot != '/' && staticProps.firstLevel == conf[bundle][env].server.webroot ) {
1487
+ var matchedFirstInUrl = url.replace(conf[bundle][env].server.webroot, '').match(/[A-Za-z0-9_-]+\/?/);
1488
+ if ( matchedFirstInUrl && matchedFirstInUrl.length > 0 ) {
1489
+ staticProps.firstLevel = conf[bundle][env].server.webroot + matchedFirstInUrl[0]
1490
+ }
1491
+ }
1492
+
1493
+ if (
1494
+ staticProps.isStaticFilename && staticsArr.indexOf(url) > -1
1495
+ || staticProps.isStaticFilename && staticsArr.indexOf( url.replace(url.substr(url.lastIndexOf('/')+1), '') ) > -1
1496
+ || staticProps.isStaticFilename && staticsArr.indexOf(staticProps.firstLevel) > -1
1497
+ ) {
1498
+ return true
1499
+ }
1500
+
1501
+ return false;
1502
+ }
1503
+
1504
+ /**
1505
+ * redirect
1506
+ *
1507
+ * TODO - improve redirect based on `utils.routing`
1508
+ * e.g.: self.redirect('project-get', { companyId: companyId, clientId: clientId, id: projectId }, true)
1509
+ *
1510
+ * How to avoid redirect inside popin context
1511
+ * N.B.: When you are in a popin context, add an `id` to your template tag so it can be ignored by the default PopinHandler
1512
+ * E.g.: id="delete-link" -> <a href="#" id="delete-link">delete</a>
1513
+ *
1514
+ * You have to ways of using this method
1515
+ *
1516
+ * 1) Through routing.json
1517
+ * ---------------------
1518
+ * Allows you to redirect to an internal [ route ], an internal [ path ], or an external [ url ]
1519
+ *
1520
+ * For this to work you have to set in your routing.json a new route using "param":
1521
+ * { "control": "redirect", "route": "one-valid-route" }
1522
+ * OR
1523
+ * { "control": "redirect", "url": "http://www.somedomain.com/page.html" }
1524
+ *
1525
+ * OR
1526
+ * { "control": "redirect", "path": "/", "ignoreWebRoot": true }
1527
+ *
1528
+ * OR
1529
+ * { "control": "redirect", "url": "http://home@public/production", "ignoreWebRoot": true }
1530
+ *
1531
+ * if you are free to use the redirection [ code ] of your choice, we've set it to 301 by default
1532
+ *
1533
+ *
1534
+ * 2) By calling this.redirect(rule, [ignoreWebRoot]):
1535
+ * ------------------------------------------------
1536
+ * where `this` is :
1537
+ * - a Controller instance
1538
+ *
1539
+ * Where `rule` is either a string defining
1540
+ * - the rule/route name
1541
+ * => home (will use same bundle, same protocol scheme & same environment)
1542
+ * => home@public (will use same protocol scheme & same environment)
1543
+ * => http://home@public/dev (port style for more precision)
1544
+ *
1545
+ * - an URI
1546
+ * => /home
1547
+ *
1548
+ * - a URL
1549
+ * => http://www.google.com/
1550
+ *
1551
+ *
1552
+ * And Where `ignoreWebRoot` is an optional parameter used to ignore web root settings (Standalone mode or user set web root)
1553
+ * `ignoreWebRoot` behaves the like set to `false` by default
1554
+ *
1555
+ * N.B.: Gina will tell browsers not to cache redirections if you are using `dev` environement
1556
+ *
1557
+ * Trobleshouting:
1558
+ * ---------------
1559
+ *
1560
+ * Redirecting to a popin from the controller while posting from a form
1561
+ * If this does not work, like doing a real redirect, this
1562
+ * only means that the ID you are using for the form might be
1563
+ * a duplicate one from the the main document !!!
1564
+ *
1565
+ * @param {object|string} req|rule - Request Object or Rule/Route name
1566
+ * @param {object|boolean} res|ignoreWebRoot - Response Object or Ignore WebRoot & start from domain root: /
1567
+ * @param {object} [params] TODO
1568
+ *
1569
+ * @callback [ next ]
1570
+ * */
1571
+ this.redirect = function(req, res, next) {
1572
+ var conf = self.getConfig();
1573
+ var bundle = conf.bundle;
1574
+ var env = conf.env;
1575
+ var wroot = conf.server.webroot;
1576
+ var ctx = getContext('gina');
1577
+ var routing = ctx.config.getRouting();//conf.content.routing;
1578
+ var route = '', rte = '';
1579
+ var ignoreWebRoot = null, isRelative = false;
1580
+ var originalUrl = null;
1581
+ var method = null;
1582
+ var originalMethod = null;
1583
+
1584
+ if ( typeof(req) === 'string' ) {
1585
+
1586
+ // if ( typeof(res) == 'undefined') {
1587
+ // // nothing to do
1588
+ // ignoreWebRoot = false
1589
+ // } else
1590
+ if (typeof(res) === 'string' || typeof(res) === 'number' || typeof(res) === 'boolean') {
1591
+ if ( /true|1/.test(res) ) {
1592
+ ignoreWebRoot = true
1593
+ } else if ( /false|0/.test(res) ) {
1594
+ ignoreWebRoot = false
1595
+ } else {
1596
+ res = local.res;
1597
+ var stack = __stack.splice(1).toString().split(',').join('\n');
1598
+ self.throwError(res, 500, new Error('RedirectError: @param `ignoreWebRoot` must be a boolean\n' + stack));
1599
+ return;
1600
+ }
1601
+ } else {
1602
+ // detect by default
1603
+ if (!ignoreWebRoot) {
1604
+ var re = new RegExp('^'+wroot)
1605
+ if ( re.test(req) ) {
1606
+ ignoreWebRoot = true;
1607
+ } else {
1608
+ ignoreWebRoot = false;
1609
+ }
1610
+ }
1611
+
1612
+ }
1613
+
1614
+ if ( req.substr(0,1) === '/') { // is relative (not checking if the URI is defined in the routing.json)
1615
+ // if (wroot.substr(wroot.length-1,1) == '/') {
1616
+ // wroot = wroot.substr(wroot.length-1,1).replace('/', '')
1617
+ // }
1618
+
1619
+ if ( /^\//.test(req) && !ignoreWebRoot )
1620
+ req = req.substr(1);
1621
+
1622
+ rte = ( ignoreWebRoot != null && ignoreWebRoot ) ? req : wroot + req;
1623
+ // cleaning url in case of ?param=value
1624
+ originalUrl = rte;
1625
+ rte = rte.replace(/\?(.*)/, '');
1626
+
1627
+ req = local.req;
1628
+ originalMethod = ( typeof(req.originalMethod) != 'undefined') ? req.originalMethod : req.method;
1629
+ console.debug('[ BUNDLE ][ '+ local.options.conf.bundle +' ][ Controller ] trying to get route: ', rte, bundle, req.method);
1630
+ if ( !ignoreWebRoot || !isStaticRoute(rte, req.method, bundle, env, ctx.config.envConf) && !ignoreWebRoot ) {
1631
+ req.routing = lib.routing.getRouteByUrl(rte, bundle, req.method, req);
1632
+ // try alternative method
1633
+ if (!req.routing) {
1634
+ req.routing = lib.routing.getRouteByUrl(rte, bundle, 'GET', req, true); // true == override
1635
+ // if still (!req.routing) { should throw a 404 }
1636
+ if (req.routing) {
1637
+ method = req.method = 'GET'
1638
+ }
1639
+ }
1640
+
1641
+ //route = route = req.routing.name;
1642
+ } else {
1643
+ req.routing = {
1644
+ param : {
1645
+ url: rte
1646
+ }
1647
+ }
1648
+ }
1649
+
1650
+ res = local.res;
1651
+ next = local.next;
1652
+ isRelative = true;
1653
+
1654
+ req.routing.param.path = rte
1655
+ } else if ( isValidURL(req) ) { // might be an URL
1656
+ rte = req;
1657
+ originalUrl = rte;
1658
+ rte = rte.replace(/\?(.*)/, '');
1659
+
1660
+ req = local.req;
1661
+ res = local.res;
1662
+ next = local.next;
1663
+
1664
+ req.routing.param.url = rte
1665
+ } else { // is by default a route name
1666
+
1667
+ if ( /\@/.test(req) ) {
1668
+ var rteArr = req.split(/\//);
1669
+ if ( typeof(rteArr[1]) != 'undefined' )
1670
+ env = rteArr[1];
1671
+
1672
+ rte = route = rteArr[0];
1673
+ rteArr = rteArr[0].split(/\@/);
1674
+
1675
+ bundle = rteArr[1];
1676
+
1677
+ } else {
1678
+ rte = route = ( new RegExp('^/'+conf.bundle+'-$').test(req) ) ? req : wroot.match(/[^/]/g).join('') +'-'+ req;
1679
+ }
1680
+
1681
+
1682
+ req = local.req;
1683
+ res = local.res;
1684
+ next = local.next;
1685
+
1686
+ req.routing.param.route = routing[rte]
1687
+ }
1688
+
1689
+ } else {
1690
+ route = req.routing.param.route;
1691
+ }
1692
+
1693
+ if ( !originalMethod ) {
1694
+ originalMethod = ( typeof(req.originalMethod) != 'undefined') ? req.originalMethod : req.method;
1695
+ }
1696
+
1697
+ var path = originalUrl || req.routing.param.path || '';
1698
+ var url = req.routing.param.url;
1699
+ var code = req.routing.param.code || 301;
1700
+
1701
+ var keepParams = req.routing.param['keep-params'] || false;
1702
+
1703
+ var condition = true; //set by default for url @ path redirect
1704
+
1705
+ if (route) { // will go with route first
1706
+ condition = ( typeof(routing[route]) != 'undefined') ? true : false;
1707
+ }
1708
+
1709
+ if ( !self.forward404Unless(condition, req, res) ) { // forward to 404 if bad route
1710
+
1711
+ var isProxyHost = ( typeof(local.req.headers.host) != 'undefined' && local.options.conf.server.scheme +'://'+ local.req.headers.host != local.options.conf.hostname || typeof(local.req.headers[':authority']) != 'undefined' && local.options.conf.server.scheme +'://'+ local.req.headers[':authority'] != local.options.conf.hostname ) ? true : false;
1712
+ var hostname = (isProxyHost) ? ctx.config.envConf[bundle][env].hostname.replace(/\:\d+$/, '') : ctx.config.envConf[bundle][env].hostname;
1713
+
1714
+ // if ( !/\:\d+$/.test(req.headers.host) )
1715
+ // hostname = hostname.replace(/\:\d+$/, '');
1716
+
1717
+ if (route) { // will go with route first
1718
+
1719
+ if ( /\,/.test(routing[route].url) ) {
1720
+ var paths = routing[route].url.split(/\,/g);
1721
+ path = (ignoreWebRoot) ? paths[0].replace(wroot, '') : paths[0];
1722
+ } else {
1723
+ path = (ignoreWebRoot) ? routing[route].url.replace(wroot, '') : routing[route].url;
1724
+ }
1725
+
1726
+ if (bundle != conf.bundle) {
1727
+ path = hostname + path;
1728
+ }
1729
+ } else if (url && !path) {
1730
+ path = ( (/\:\/\//).test(url) ) ? url : req.scheme + '://' + url;
1731
+
1732
+ if (/\@/.test(path)) {
1733
+ path = lib.routing.getRoute(path).toUrl(ignoreWebRoot);
1734
+ }
1735
+
1736
+ //} else if(path && typeof(isRelative) != 'undefined') {
1737
+ // nothing to do, just ignoring
1738
+ //} else {
1739
+ } else if ( !path && typeof(isRelative) == 'undefined' ) {
1740
+
1741
+ path = hostname + path
1742
+ //path = local.req.headers.host + path
1743
+ }
1744
+
1745
+
1746
+
1747
+ if (!local.res.headersSent) {
1748
+
1749
+ // backing up oldParams
1750
+ var oldParams = local.req[originalMethod.toLowerCase()];
1751
+ var requestParams = req[req.method.toLowerCase()] || {};
1752
+ if ( typeof(requestParams) != 'undefined' && typeof(requestParams.error) != 'undefined' ) {
1753
+ var redirectError = requestParams.error;
1754
+ self.throwError(requestParams.error);
1755
+ return;
1756
+ }
1757
+
1758
+ if (
1759
+ !/GET/i.test(req.method)
1760
+ ||
1761
+ originalMethod && !/GET/i.test(originalMethod)
1762
+ ) { // trying to redirect using the wrong method ?
1763
+
1764
+ console.warn(new Error('Your are trying to redirect using the wrong method: `'+ req.method+'`.\nThis can often occur while redirecting from a controller to another controller or from a bundle to another.\nA redirection is not permitted in this scenario.\nD\'ont panic :)\nSwitching request method to `GET` method instead.\n').message);
1765
+ method = local.req.method = self.setRequestMethod('GET', conf);
1766
+ code = 303;
1767
+ }
1768
+
1769
+
1770
+ // merging new & olds params
1771
+ requestParams = merge(requestParams, oldParams);
1772
+ // remove session to prevent reaching the 2000 chars limit
1773
+ // if you need the session, you need to find another way to retrieve while in the next route
1774
+ if ( typeof(requestParams.session) != 'undefined' ) {
1775
+ delete requestParams.session;
1776
+ }
1777
+ if ( typeof(requestParams) != 'undefined' && requestParams.count() > 0 ) {
1778
+ //if ( typeof(requestParams.error) != 'undefined' )
1779
+
1780
+ var inheritedData = null;
1781
+ if ( /\?/.test(path) ) {
1782
+ inheritedData = '&inheritedData='+ encodeURIComponent(JSON.stringify(requestParams));
1783
+ } else {
1784
+ inheritedData = '?inheritedData='+ encodeURIComponent(JSON.stringify(requestParams));
1785
+ }
1786
+
1787
+ if ( inheritedData.length > 2000 ) {
1788
+ var error = new ApiError('Controller::redirect(...) exceptions: `inheritedData` reached 2000 chars limit', 424);
1789
+ self.throwError(error);
1790
+ return;
1791
+ }
1792
+
1793
+ // if redirecting from a xhrRequest
1794
+ if ( self.isXMLRequest() ) {
1795
+ // `requestParams` should be stored in the session to avoid passing datas in clear
1796
+ var redirectObj = { location: path, isXhrRedirect: true };
1797
+ if (requestParams.count() > 0) {
1798
+ var userSession = req.session.user || req.session;
1799
+ if ( userSession && local.haltedRequestUrlResumed ) {
1800
+ // will be reused for server.js on `case : 'GET'`
1801
+ userSession.inheritedData = requestParams;
1802
+ } else { // will be passed in clear
1803
+ redirectObj.location += inheritedData;
1804
+ }
1805
+ }
1806
+
1807
+ self.renderJSON(redirectObj);
1808
+ return;
1809
+ }
1810
+
1811
+ path += inheritedData;
1812
+ }
1813
+
1814
+ var ext = 'html';
1815
+ res.setHeader('content-type', local.options.conf.server.coreConfiguration.mime[ext]);
1816
+
1817
+ if (
1818
+ typeof(local.res._headers) != 'undefined'
1819
+ && typeof(local.res._headers['access-control-allow-methods']) != 'undefined'
1820
+ && local.res._headers['access-control-allow-methods'] != req.method
1821
+ ||
1822
+ !new RegExp(req.method, 'i').test( res.getHeader('access-control-allow-methods') )
1823
+ ) {
1824
+ res.setHeader('access-control-allow-methods', req.method.toUpperCase() );
1825
+ }
1826
+ //path += '?query='+ JSON.stringify(self.getRequestMethodParams());
1827
+ local.req[req.method.toLowerCase()] = self.getRequestMethodParams() || {};
1828
+
1829
+ var headInfos = {
1830
+ 'location': path
1831
+ };
1832
+
1833
+ if (self.isCacheless()) {
1834
+ res.writeHead(code, merge(headInfos, {
1835
+ 'cache-control': 'no-cache, no-store, must-revalidate', // preventing browsers from using cache
1836
+ 'pragma': 'no-cache',
1837
+ 'expires': '0'
1838
+ }))
1839
+ } else {
1840
+ res.writeHead(code, headInfos)
1841
+ }
1842
+ // in case of query from another bundle waiting for a response
1843
+ var redirectObject = JSON.stringify({ status: code, headers: headInfos });
1844
+ res.end(redirectObject);
1845
+ try {
1846
+ local.res.headersSent = true;// done for the render() method
1847
+ } catch(err){
1848
+ // ignoring the warning
1849
+ }
1850
+
1851
+ console.info(local.req.method.toUpperCase() +' ['+code+'] '+ path);
1852
+
1853
+ if ( typeof(next) != 'undefined' )
1854
+ next();
1855
+ else
1856
+ return;
1857
+ }
1858
+
1859
+ }
1860
+ }
1861
+
1862
+ /**
1863
+ * Move files to assets dir
1864
+ *
1865
+ * @param {object} res
1866
+ * @param {collection} files
1867
+ *
1868
+ * @callback cb
1869
+ * @param {object} [err]
1870
+ * */
1871
+ var movefiles = function (i, res, files, cb) {
1872
+ if (!files.length || files.length == 0) {
1873
+ cb(false)
1874
+ } else {
1875
+ if ( fs.existsSync(files[i].target) ) new _(files[i].target).rmSync();
1876
+
1877
+ var sourceStream = fs.createReadStream(files[i].source);
1878
+ var destinationStream = fs.createWriteStream(files[i].target);
1879
+
1880
+ sourceStream
1881
+ .pipe(destinationStream)
1882
+ .on('error', function () {
1883
+ var err = 'Error on SuperController::copyFile(...): Not found ' + files[i].source + ' or ' + files[i].target;
1884
+ cb(err)
1885
+ })
1886
+ .on('close', function () {
1887
+
1888
+ try {
1889
+ fs.unlinkSync(files[i].source);
1890
+ files.splice(i, 1);
1891
+ } catch (err) {
1892
+ cb(err)
1893
+ }
1894
+
1895
+ movefiles(i, res, files, cb)
1896
+ })
1897
+ }
1898
+ }
1899
+
1900
+ this.getBundleStatus = function(req, res, next) {
1901
+ self.renderJSON({
1902
+ status: 200,
1903
+ isAlive: true,
1904
+ message: 'I am alive !'
1905
+ });
1906
+ }
1907
+
1908
+ this.checkBundleStatus = async function(bundle, cb) {
1909
+ var opt = self.getConfig('app').proxy[bundle];
1910
+ var route = lib.routing.getRoute('bundle-status@'+bundle);
1911
+ opt.method = 'GET';
1912
+ opt.path = route.url;
1913
+ var response = { isAlive: false }, error = false;
1914
+ await util.promisify(self.query)(opt, {})
1915
+ .then( function onQueryResponse(_status) {
1916
+ response = _status
1917
+ });
1918
+
1919
+ if (cb) {
1920
+ cb(error, response);
1921
+ } else {
1922
+ return response;
1923
+ }
1924
+ }
1925
+
1926
+ /**
1927
+ * downloadFromURL
1928
+ * Download from an URL
1929
+ * - attachment/inline
1930
+ * OR
1931
+ * - locally: `Controller.store(target, cb)` must be called to store on `onComplete` event
1932
+ *
1933
+ * @param {string} url - eg.: https://upload.wikimedia.org/wikipedia/fr/2/2f/Firefox_Old_Logo.png
1934
+ * @param {object} [options]
1935
+ *
1936
+ *
1937
+ * */
1938
+ this.downloadFromURL = function(url, options) {
1939
+
1940
+ var defaultOptions = {
1941
+ // file name i you want to rename the file
1942
+ file: null,
1943
+ fileSize: null,
1944
+ // only if you want to store locally the downloaded file
1945
+ toLocalDir: false, // this option will disable attachment download
1946
+ // content-disposition (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
1947
+ contentDisposition: 'attachment',
1948
+ // content-type (https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Configuring_server_MIME_types)
1949
+ contentType: 'application/octet-stream',
1950
+
1951
+ agent: false,
1952
+ // set to false to ignore certificate verification
1953
+ rejectUnauthorized: true,
1954
+ //responseType: 'blob',
1955
+ port: 80,
1956
+ method: 'GET',
1957
+ keepAlive: true,
1958
+ headers: {}
1959
+ };
1960
+
1961
+ var opt = ( typeof(options) != 'undefined' ) ? merge(options, defaultOptions) : defaultOptions;
1962
+
1963
+ var requestOptions = {};
1964
+ for (var o in opt) {
1965
+ if ( !/(toLocalDir|contentDisposition|contentType|file)/.test(o) )
1966
+ requestOptions[o] = opt[o];
1967
+ }
1968
+
1969
+ // defining protocol & scheme
1970
+ var protocol = null;
1971
+ var scheme = null;
1972
+
1973
+ if ( /\:\/\//.test(url) ) {
1974
+ scheme = url.match(/^\w+\:/)[0];
1975
+ scheme = scheme.substr(0, scheme.length-1);
1976
+
1977
+ if ( !/^http/.test(scheme) ) {
1978
+ self.throwError(local.res, 500, new Error('[ '+ scheme +' ] Scheme not supported. Ref.: `http` or `https` only'));
1979
+ return;
1980
+ }
1981
+
1982
+
1983
+
1984
+ } else { // by default
1985
+ scheme = 'http';
1986
+ }
1987
+
1988
+ requestOptions.scheme = scheme +':';
1989
+
1990
+ //defining port
1991
+ var port = url.match(/\:\d+\//) || null;
1992
+ if ( port != null ) {
1993
+ port = port[0].substr(1, port[0].length-2);
1994
+ requestOptions.port = ~~port;
1995
+ }
1996
+
1997
+ // defining hostname & path
1998
+ var parts = url.replace(new RegExp( scheme + '\:\/\/'), '').split(/\//g);
1999
+ requestOptions.host = parts[0].replace(/\:\d+/, '');
2000
+ requestOptions.path = '/' + parts.splice(1).join('/');
2001
+
2002
+
2003
+ // extension and mime
2004
+ var filename = url.split(/\//g).pop();
2005
+ if (!filename) {
2006
+ self.throwError(local.res, 500, new Error('Filename not found in url: `'+ url +'`'));
2007
+ return;
2008
+ }
2009
+
2010
+
2011
+ if ( !/\.\w+$/.test(filename) ) {
2012
+ self.throwError(local.res, 500, new Error('[ '+ filename +' ] extension not found.'));
2013
+ return;
2014
+ }
2015
+
2016
+
2017
+ // filename renaming
2018
+ if (opt.file)
2019
+ filename = opt.file;
2020
+
2021
+ if ( opt.contentDisposition == 'attachment') {
2022
+ opt.contentDisposition += '; filename=' + filename;
2023
+ }
2024
+
2025
+ var ext = filename.match(/\.\w+$/)[0].substr(1)
2026
+ , contentType = null
2027
+ , tmp = _(GINA_TMPDIR +'/'+ filename, true)
2028
+ ;
2029
+
2030
+ if ( typeof(local.options.conf.server.coreConfiguration.mime[ext]) != 'undefined' ) {
2031
+
2032
+ contentType = (opt.contentType != defaultOptions.contentType) ? opt.contentType : local.options.conf.server.coreConfiguration.mime[ext];
2033
+
2034
+ } else { // extension not supported
2035
+ self.throwError(local.res, 500, new Error('[ '+ ext +' ] Extension not supported. Ref.: gina/core mime.types'));
2036
+ return;
2037
+ }
2038
+
2039
+ // defining responseType
2040
+ requestOptions.headers['content-type'] = contentType;
2041
+ requestOptions.headers['content-disposition'] = opt.contentDisposition;
2042
+
2043
+ var browser = require(''+ scheme);
2044
+ //console.debug('requestOptions: \n', JSON.stringify(requestOptions, null, 4));
2045
+
2046
+ browser.get(requestOptions, function(response) {
2047
+
2048
+ local.res.setHeader('content-type', contentType + '; charset='+ local.options.conf.encoding);
2049
+ local.res.setHeader('content-disposition', opt.contentDisposition);
2050
+ if (opt.fileSize) {
2051
+ local.res.setHeader('content-length', opt.fileSize);
2052
+ }
2053
+ //local.res.setHeader('content-length', opt.fileSize);
2054
+ // local.res.setHeader('cache-control', 'must-revalidate');
2055
+ // local.res.setHeader('pragma', 'must-revalidate');
2056
+
2057
+ // response.on('end', function onResponsePipeEnd(){
2058
+ // self.renderJSON({ url: url});
2059
+ // //local.res.end( Buffer.from(data) );
2060
+ // //local.res.headersSent = true;
2061
+
2062
+ // // if ( typeof(local.next) != 'undefined')
2063
+ // // local.next();
2064
+ // // else
2065
+ // // return;
2066
+ // });
2067
+
2068
+ response.pipe(local.res);
2069
+ });
2070
+
2071
+ return;
2072
+
2073
+ }
2074
+
2075
+
2076
+ /**
2077
+ * Download to targeted filename.ext - Will create target if new
2078
+ * Use `cb` callback or `onComplete` event
2079
+ *
2080
+ * @param {string} filename
2081
+ * @param {object} options
2082
+ **/
2083
+ this.downloadFromLocal = function(filename) {
2084
+
2085
+ var file = filename.split(/\//g).pop();
2086
+ var ext = file.split(/\./g).pop()
2087
+ , contentType = null
2088
+ ;
2089
+
2090
+ if ( typeof(local.options.conf.server.coreConfiguration.mime[ext]) != 'undefined' ) {
2091
+
2092
+ contentType = local.options.conf.server.coreConfiguration.mime[ext];
2093
+ local.res.setHeader('content-type', contentType);
2094
+ local.res.setHeader('content-disposition', 'attachment; filename=' + file);
2095
+
2096
+ var filestream = fs.createReadStream(filename);
2097
+ filestream.pipe(local.res);
2098
+
2099
+ } else { // extension not supported
2100
+ self.throwError(local.res, 500, new Error('[ '+ ext +' ] Extension not supported. Ref.: gina/core mime.types'));
2101
+ return;
2102
+ }
2103
+ }
2104
+
2105
+
2106
+ /**
2107
+ * Store file(s) to a targeted directory - Will create target if new
2108
+ * You only need to provide the destination path
2109
+ * Use `cb` callback or `onComplete` event
2110
+ *
2111
+ * @param {string} target is the upload dir destination
2112
+ * @param {array} [files]
2113
+ *
2114
+ * @callback [cb]
2115
+ * @param {object} error
2116
+ * @param {array} files
2117
+ *
2118
+ * @event
2119
+ * @param {object} error
2120
+ * @param {array} files
2121
+ *
2122
+ * */
2123
+ this.store = async function(target, files, cb) {
2124
+
2125
+
2126
+ var start = function(target, files, cb) {
2127
+
2128
+ if (arguments.length == 2 && typeof(arguments[1]) == 'function' ) {
2129
+ var cb = arguments[1];
2130
+ }
2131
+
2132
+ if ( typeof(files) == 'undefined' || typeof(files) == 'function' ) {
2133
+ files = local.req.files
2134
+ }
2135
+
2136
+ var uploadedFiles = [];
2137
+
2138
+ if ( typeof(files) == 'undefined' || files.count() == 0 ) {
2139
+ if (cb) {
2140
+ cb(new Error('No file to upload'))
2141
+ } else {
2142
+ self.emit('uploaded', new Error('No file to upload'))
2143
+ }
2144
+ } else {
2145
+ // saving files
2146
+ var uploadDir = new _(target)
2147
+ , list = []
2148
+ , i = 0
2149
+ , folder = uploadDir.mkdirSync();
2150
+
2151
+ if (folder instanceof Error) {
2152
+ if (cb) {
2153
+ cb(folder)
2154
+ } else {
2155
+ self.emit('uploaded', folder)
2156
+ }
2157
+ } else {
2158
+ // files list
2159
+ var fileName = null;
2160
+ for (var len = files.length; i < len; ++i ){
2161
+
2162
+ fileName = files[i].filename || files[i].originalFilename
2163
+
2164
+ list[i] = {
2165
+ source: files[i].path,
2166
+ target: _(uploadDir.toString() + '/' + fileName)
2167
+ };
2168
+
2169
+ uploadedFiles[i] = {
2170
+ file : fileName,
2171
+ filename : list[i].target,
2172
+ size : files[i].size,
2173
+ type : files[i].type,
2174
+ encoding : files[i].encoding
2175
+ };
2176
+
2177
+ }
2178
+
2179
+ movefiles(0, local.res, list, function (err) {
2180
+ if (err) {
2181
+ if (cb) {
2182
+ cb(new Error('No file to upload'))
2183
+ } else {
2184
+ self.emit('uploaded', new Error('No file to upload'))
2185
+ }
2186
+ } else {
2187
+ if (cb) {
2188
+ cb(false, uploadedFiles)
2189
+ } else {
2190
+ self.emit('uploaded', false, uploadedFiles)
2191
+ }
2192
+ }
2193
+ })
2194
+ }
2195
+ }
2196
+ }
2197
+
2198
+ if ( typeof(cb) == 'undefined' ) {
2199
+
2200
+ return {
2201
+ onComplete : function(cb){
2202
+ self.on('uploaded', cb);
2203
+ start(target, files)
2204
+ }
2205
+ }
2206
+ } else {
2207
+ start(target, files, cb)
2208
+ }
2209
+ }
2210
+
2211
+
2212
+ /**
2213
+ * Query
2214
+ *
2215
+ * Allows you to act as a proxy between your frontend and a 1/3 API
2216
+ * */
2217
+ function sha256(s) {
2218
+ return crypto.createHash('sha256').update(s).digest('base64');
2219
+ }
2220
+ local.query.data = {};
2221
+ local.query.options = {
2222
+ host : undefined, // Must be an IP
2223
+ hostname : undefined, // cname of the host e.g.: `www.google.com` or `localhost`
2224
+ path : undefined, // e.g.: /test.html
2225
+ port : 80, // #80 by default but can be 3000 or <bundle>@<project>/<environment>
2226
+ method : 'GET', // POST | GET | PUT | DELETE
2227
+ keepAlive: true,
2228
+ auth: undefined, // use `"username:password"` for basic authentification
2229
+
2230
+ // set to false to ignore certificate verification when requesting on https (443)
2231
+ // same as process.env.NODE_TLS_REJECT_UNAUTHORIZED = "1";
2232
+ rejectUnauthorized: true,
2233
+
2234
+ headers: {
2235
+ 'content-type': 'application/json',
2236
+ 'content-length': local.query.data.length
2237
+ },
2238
+ agent : false/**,
2239
+ checkServerIdentity: function(host, cert) {
2240
+ // Make sure the certificate is issued to the host we are connected to
2241
+ const err = tls.checkServerIdentity(host, cert);
2242
+ if (err) {
2243
+ return err;
2244
+ }
2245
+
2246
+ // Pin the public key, similar to HPKP pin-sha25 pinning
2247
+ const pubkey256 = 'pL1+qb9HTMRZJmuC/bB/ZI9d302BYrrqiVuRyW+DGrU=';
2248
+ if (sha256(cert.pubkey) !== pubkey256) {
2249
+ const msg = 'Certificate verification error: ' +
2250
+ `The public key of '${cert.subject.CN}' ` +
2251
+ 'does not match our pinned fingerprint';
2252
+ return new Error(msg);
2253
+ }
2254
+
2255
+ // Pin the exact certificate, rather then the pub key
2256
+ const cert256 = '25:FE:39:32:D9:63:8C:8A:FC:A1:9A:29:87:' +
2257
+ 'D8:3E:4C:1D:98:DB:71:E4:1A:48:03:98:EA:22:6A:BD:8B:93:16';
2258
+ if (cert.fingerprint256 !== cert256) {
2259
+ const msg = 'Certificate verification error: ' +
2260
+ `The certificate of '${cert.subject.CN}' ` +
2261
+ 'does not match our pinned fingerprint';
2262
+ return new Error(msg);
2263
+ }
2264
+
2265
+ // This loop is informational only.
2266
+ // Print the certificate and public key fingerprints of all certs in the
2267
+ // chain. Its common to pin the public key of the issuer on the public
2268
+ // internet, while pinning the public key of the service in sensitive
2269
+ // environments.
2270
+ do {
2271
+ console.debug('Subject Common Name:', cert.subject.CN);
2272
+ console.debug(' Certificate SHA256 fingerprint:', cert.fingerprint256);
2273
+
2274
+ hash = crypto.createHash('sha256');
2275
+ console.debug(' Public key ping-sha256:', sha256(cert.pubkey));
2276
+
2277
+ lastprint256 = cert.fingerprint256;
2278
+ cert = cert.issuerCertificate;
2279
+ } while (cert.fingerprint256 !== lastprint256);
2280
+
2281
+ }*/
2282
+
2283
+ };
2284
+
2285
+ this.query = function() { // options, data, callback
2286
+ var err = null;
2287
+ var options = arguments[0];
2288
+ var data = arguments[1] || {};
2289
+ var callback = null;
2290
+ if ( typeof(arguments[arguments.length-1]) == 'function' ) {
2291
+ callback = arguments[arguments.length-1];
2292
+ } else {
2293
+ data = arguments[arguments.length-1]
2294
+ }
2295
+ // preventing multiple call of self.query() when controller is rendering from another required controller
2296
+ if (
2297
+ typeof(local.options) != 'undefined'
2298
+ && typeof(local.options.renderingStack) != 'undefined'
2299
+ && local.options.renderingStack.length > 1
2300
+ ) {
2301
+ return false
2302
+ }
2303
+ self.isProcessingError = false; // by default
2304
+
2305
+ var queryData = {}
2306
+ , defaultOptions = local.query.options
2307
+ , path = options.path
2308
+ , browser = null
2309
+ ;
2310
+
2311
+ // options must be used as a copy in case of multiple calls of self.query(options, ...)
2312
+ options = merge(JSON.clone(options), defaultOptions);
2313
+
2314
+ for (var o in options) {//cleaning
2315
+ if ( typeof(options[o]) == 'undefined' || options[o] == undefined) {
2316
+ delete options[o]
2317
+ }
2318
+ }
2319
+
2320
+ if (self.isCacheless() || self.isLocalScope() ) {
2321
+ options.rejectUnauthorized = false;
2322
+ }
2323
+
2324
+ if ( !options.host && !options.hostname ) {
2325
+ err = new Error('SuperController::query() needs at least a `host IP` or a `hostname`');
2326
+ if (callback) {
2327
+ return callback(err)
2328
+ }
2329
+ self.emit('query#complete', err)
2330
+ }
2331
+
2332
+
2333
+
2334
+ // if (arguments.length <3) {
2335
+ // if ( typeof(data) == 'function') {
2336
+ // var callback = data;
2337
+ // var data = undefined;
2338
+ // } else {
2339
+ // callback = undefined;
2340
+ // }
2341
+ // }
2342
+ if ( typeof(data) != 'undefined' && data.count() > 0) {
2343
+
2344
+ queryData = '?';
2345
+ // TODO - if 'application/json' && method == (put|post)
2346
+ if ( ['put', 'post'].indexOf(options.method.toLowerCase()) >-1 && /(text\/plain|application\/json|application\/x\-www\-form)/i.test(options.headers['content-type']) ) {
2347
+ // replacing
2348
+ queryData = encodeURIComponent(JSON.stringify(data))
2349
+ //queryData = JSON.stringify(data)
2350
+
2351
+ } else {
2352
+ //Sample request.
2353
+ //options.path = '/updater/start?release={"version":"0.0.5-dev","url":"http://10.1.0.1:8080/project/bundle/repository/archive?ref=0.0.5-dev","date":1383669077141}&pid=46493';
2354
+ // do not alter the orignal data
2355
+ var tmpData = JSON.clone(data);
2356
+ for (let d in tmpData) {
2357
+ if ( typeof(tmpData[d]) == 'object') {
2358
+ tmpData[d] = JSON.stringify(tmpData[d]);
2359
+ }
2360
+ queryData += d + '=' + encodeURIComponent(tmpData[d]) + '&';
2361
+ }
2362
+
2363
+ queryData = queryData.substring(0, queryData.length-1);
2364
+ queryData = queryData.replace(/\s/g, '%20');
2365
+
2366
+ options.path += queryData;
2367
+ }
2368
+
2369
+ } else {
2370
+ queryData = ''
2371
+ }
2372
+
2373
+
2374
+ // Internet Explorer override
2375
+ if ( local.req != null && /msie/i.test(local.req.headers['user-agent']) ) {
2376
+ options.headers['content-type'] = 'text/plain';
2377
+ } else {
2378
+ options.headers['content-type'] = local.options.conf.server.coreConfiguration.mime['json'];
2379
+ }
2380
+
2381
+ // if ( typeof(local.req.headers.cookie) == 'undefined' && typeof(local.res._headers['set-cookie']) != 'undefined' ) { // useful for CORS : forward cookies from the original request
2382
+ // //options.headers.cookie = local.req.headers.cookie;
2383
+ // var originalResponseCookies = local.res._headers['set-cookie'];
2384
+ // options.headers.cookie = [];
2385
+ // for (var c = 0, cLen = originalResponseCookies.length; c < cLen; ++c) {
2386
+ // options.headers.cookie.push(originalResponseCookies[c])
2387
+ // }
2388
+ // }
2389
+
2390
+ //you need this, even when empty.
2391
+ options.headers['content-length'] = queryData.length;
2392
+
2393
+ // adding gina headers
2394
+ if ( local.req != null && typeof(local.req.ginaHeaders) != 'undefined' ) {
2395
+ // gina form headers
2396
+ for (let h in local.req.ginaHeaders.form) {
2397
+ let k = h.substr(0,1).toUpperCase() + h.substr(1);
2398
+ options.headers['X-Gina-Form-' + k ] = local.req.ginaHeaders.form[h];
2399
+ }
2400
+ }
2401
+
2402
+ var ctx = getContext()
2403
+ , protocol = null
2404
+ , scheme = null
2405
+ ;
2406
+ // cleanup options.path
2407
+ if (/\:\/\//.test(options.path)) {
2408
+
2409
+ var hArr = options.path.split(/^(https|http)\:\/\//);
2410
+ var domain = hArr[1] +'://';
2411
+ var host = hArr[2].split(/\//)[0];
2412
+ var port = parseInt(host.split(/\:/)[1] || 80);
2413
+
2414
+ options.port = port;
2415
+ options.host = domain + host.replace(':'+port, '');
2416
+ options.path = options.path
2417
+ .replace(options.host, '')
2418
+ .replace(':'+port, '');
2419
+ }
2420
+
2421
+ // retrieve protocol & scheme: if empty, take the bundles protocol
2422
+ protocol = options.protocol || ctx.gina.config.envConf[ctx.bundle][ctx.env].server.protocol;// bundle servers's protocol by default
2423
+ protocol = protocol.match(/[.a-z 0-9]+/ig)[0];
2424
+ scheme = options.scheme || ctx.gina.config.envConf[ctx.bundle][ctx.env].server.scheme;// bundle servers's scheme by default
2425
+ scheme = scheme.match(/[a-z 0-9]+/ig)[0];
2426
+
2427
+ //retrieving dynamic host, hostname & port
2428
+ if ( /\@/.test(options.hostname) ) {
2429
+
2430
+ var bundle = ( options.hostname.replace(/(.*)\:\/\//, '') ).split(/\@/)[0];
2431
+
2432
+ // No shorcut possible because conf.hostname might differ from user inputs
2433
+ options.host = ctx.gina.config.envConf[bundle][ctx.env].host.replace(/(.*)\:\/\//, '').replace(/\:\d+/, '');
2434
+ options.hostname = ctx.gina.config.envConf[bundle][ctx.env].hostname;
2435
+ options.port = ctx.gina.config.envConf[bundle][ctx.env].server.port;
2436
+
2437
+ options.protocol = ctx.gina.config.envConf[bundle][ctx.env].server.protocol;
2438
+ options.scheme = ctx.gina.config.envConf[bundle][ctx.env].server.scheme;
2439
+ // might be != from the bundle requesting
2440
+ //options.protocol = ctx.gina.config.envConf[bundle][ctx.env].content.settings.server.protocol || ctx.gina.config.envConf[bundle][ctx.env].server.protocol;
2441
+ //options.scheme = ctx.gina.config.envConf[bundle][ctx.env].content.settings.server.scheme || ctx.gina.config.envConf[bundle][ctx.env].server.scheme;
2442
+ }
2443
+
2444
+ if ( typeof(options.protocol) == 'undefined' ) {
2445
+ options.protocol = protocol
2446
+ }
2447
+ if ( typeof(options.scheme) == 'undefined' ) {
2448
+ options.scheme = scheme
2449
+ }
2450
+
2451
+
2452
+ // reformating scheme
2453
+ if( !/\:$/.test(options.scheme) )
2454
+ options.scheme += ':';
2455
+
2456
+ try {
2457
+ options.queryData = queryData;
2458
+ var protocolVersion = ~~options.protocol.match(/\/(.*)$/)[1].replace(/\.\d+/, '');
2459
+ var httpLib = options.protocol.match(/^(.*)\//)[1] + ( (protocolVersion >= 2) ? protocolVersion : '' );
2460
+ if ( !/http2/.test(httpLib) && /https/.test(options.scheme) ) {
2461
+ httpLib += 's';
2462
+ }
2463
+
2464
+ browser = require(''+ httpLib);
2465
+
2466
+ if ( /http2/.test(httpLib) ) {
2467
+ return handleHTTP2ClientRequest(browser, options, callback);
2468
+ } else {
2469
+ return handleHTTP1ClientRequest(browser, options, callback);
2470
+ }
2471
+
2472
+
2473
+ } catch(err) {
2474
+ if (callback) {
2475
+ return callback(err)
2476
+ }
2477
+ self.emit('query#complete', err)
2478
+ }
2479
+
2480
+ }
2481
+
2482
+ var handleHTTP1ClientRequest = function(browser, options, callback) {
2483
+
2484
+ var altOpt = JSON.clone(options);
2485
+
2486
+ altOpt.protocol = options.scheme;
2487
+ altOpt.hostname = options.host;
2488
+ altOpt.port = options.port;
2489
+ if ( typeof(altOpt.encKey) != 'undefined' ) {
2490
+ try {
2491
+ altOpt.encKey = fs.readFileSync(options.encKey);
2492
+ } catch(err) {
2493
+ self.emit('query#complete', err);
2494
+ }
2495
+
2496
+ } else {
2497
+ console.warn('[ CONTROLLER ][ HTTP/1.0#query ] options.encKey not found !');
2498
+ }
2499
+
2500
+ if ( typeof(altOpt.encCert) != 'undefined' ) {
2501
+ try {
2502
+ altOpt.encCert = fs.readFileSync(options.encCert);
2503
+ } catch(err) {
2504
+ self.emit('query#complete', err);
2505
+ }
2506
+
2507
+ } else {
2508
+ console.warn('[ CONTROLLER ][ HTTP/1.0#query ] options.encCert not found !');
2509
+ }
2510
+
2511
+ altOpt.agent = new browser.Agent(altOpt);
2512
+
2513
+ var req = browser.request(altOpt, function(res) {
2514
+
2515
+ res.setEncoding('utf8');
2516
+
2517
+ // upgrade response headers to handler
2518
+ if ( typeof(res.headers['access-control-allow-credentials']) != 'undefined' )
2519
+ local.options.withCredentials = res.headers['access-control-allow-credentials'];
2520
+
2521
+
2522
+ var data = '', err = false;
2523
+
2524
+ res.on('data', function onData (chunk) {
2525
+ data += chunk;
2526
+ });
2527
+
2528
+ res.on('end', function onEnd(err) {
2529
+
2530
+
2531
+ // exceptions filter
2532
+ if ( typeof(data) == 'string' && /^Unknown ALPN Protocol/.test(data) ) {
2533
+ err = {
2534
+ status: 500,
2535
+ error: new Error(data)
2536
+ };
2537
+
2538
+ if ( typeof(callback) != 'undefined' ) {
2539
+ callback(err)
2540
+ } else {
2541
+ self.emit('query#complete', err)
2542
+ }
2543
+
2544
+ return
2545
+ }
2546
+
2547
+ //Only when needed.
2548
+ if ( typeof(callback) != 'undefined' ) {
2549
+ if ( typeof(data) == 'string' && /^(\{|%7B|\[{)|\[\]/.test(data) ) {
2550
+ try {
2551
+ data = JSON.parse(data)
2552
+ } catch (err) {
2553
+ data = {
2554
+ status : 500,
2555
+ error : err
2556
+ };
2557
+ console.error(err);
2558
+ }
2559
+ }
2560
+
2561
+ try {
2562
+ if ( data.status && !/^2/.test(data.status) && typeof(local.options.conf.server.coreConfiguration.statusCodes[data.status]) != 'undefined' ) {
2563
+ self.throwError(data);
2564
+ return;
2565
+ } else {
2566
+ callback( false, data );
2567
+ return;
2568
+ }
2569
+ } catch (e) {
2570
+ var infos = local.options, controllerName = infos.controller.substr(infos.controller.lastIndexOf('/'));
2571
+ var msg = 'Controller Query Exception while catching back.\nBundle: '+ infos.bundle +'\nController File: /controllers'+ controllerName +'\nControl: this.'+ infos.control +'(...)\n\r' + e.stack;
2572
+ var exception = new Error(msg);
2573
+ exception.status = 500;
2574
+ self.throwError(exception);
2575
+ return;
2576
+ }
2577
+
2578
+ } else {
2579
+ if ( typeof(data) == 'string' && /^(\{|%7B|\[{)|\[\]/.test(data) ) {
2580
+ try {
2581
+ data = JSON.parse(data)
2582
+ } catch (err) {
2583
+ data = {
2584
+ status : 500,
2585
+ error : data
2586
+ }
2587
+ self.emit('query#complete', data)
2588
+ }
2589
+ }
2590
+
2591
+ if ( data.status && !/^2/.test(data.status) && typeof(local.options.conf.server.coreConfiguration.statusCodes[data.status]) != 'undefined' ) {
2592
+ self.emit('query#complete', data)
2593
+ } else {
2594
+ self.emit('query#complete', false, data)
2595
+ }
2596
+ }
2597
+ })
2598
+ });
2599
+
2600
+
2601
+ //starting from from >0.10.15
2602
+ req.on('error', function onError(err) {
2603
+
2604
+
2605
+ if (
2606
+ typeof(err.code) != 'undefined' && /ECONNREFUSED|ECONNRESET/.test(err.code)
2607
+ || typeof(err.cause) != 'undefined' && typeof(err.cause.code) != 'undefined' && /ECONNREFUSED|ECONNRESET/.test(err.cause.code)
2608
+ ) {
2609
+
2610
+ var port = getContext('gina').ports[options.protocol][options.scheme.replace(/\:/, '')][ options.port ];//err.port || err.cause.port
2611
+ if ( typeof(port) != 'undefined' ) {
2612
+ err.accessPoint = port;
2613
+ err.message = '`Controller::query()` could not connect to [ ' + err.accessPoint + ' ] using port '+options.port+'.\n';
2614
+ }
2615
+ }
2616
+
2617
+
2618
+ console.error(err.stack||err.message);
2619
+ // you can get here if :
2620
+ // - you are trying to query using: `enctype="multipart/form-data"`
2621
+ // -
2622
+ if ( typeof(callback) != 'undefined' ) {
2623
+
2624
+ callback(err)
2625
+
2626
+ } else {
2627
+ var error = {
2628
+ status : 500,
2629
+ error : err.stack || err.message
2630
+ };
2631
+
2632
+ self.emit('query#complete', error)
2633
+ }
2634
+ });
2635
+
2636
+
2637
+ if (req) { // don't touch this please
2638
+ if (req.write) req.write(options.queryData);
2639
+ if (req.end) req.end();
2640
+ }
2641
+
2642
+ return {
2643
+ onComplete : function(cb) {
2644
+ self.once('query#complete', function(err, data){
2645
+
2646
+ if ( typeof(data) == 'string' && /^(\{|%7B|\[{)|\[\]/.test(data) ) {
2647
+ try {
2648
+ data = JSON.parse(data)
2649
+ } catch (err) {
2650
+ data = {
2651
+ status : 500,
2652
+ error : data
2653
+ }
2654
+ }
2655
+ }
2656
+
2657
+ try {
2658
+ if ( data.status && !/^2/.test(data.status) && typeof(local.options.conf.server.coreConfiguration.statusCodes[data.status]) != 'undefined') {
2659
+ cb(data)
2660
+ } else {
2661
+ cb(err, data)
2662
+ }
2663
+ } catch (e) {
2664
+ var infos = local.options, controllerName = infos.controller.substr(infos.controller.lastIndexOf('/'));
2665
+ var msg = 'Controller Query Exception while catching back.\nBundle: '+ infos.bundle +'\nController File: /controllers'+ controllerName +'\nControl: this.'+ infos.control +'(...)\n\r' + e.stack;
2666
+ var exception = new Error(msg);
2667
+ exception.status = 500;
2668
+ self.throwError(exception);
2669
+ return;
2670
+ }
2671
+ })
2672
+ }
2673
+
2674
+ }
2675
+ }
2676
+
2677
+ var handleHTTP2ClientRequest = function(browser, options, callback) {
2678
+
2679
+ //cleanup
2680
+ options[':authority'] = options.hostname;
2681
+
2682
+ delete options.host;
2683
+
2684
+ if ( typeof(options[':path']) == 'undefined' ) {
2685
+ options[':path'] = options.path;
2686
+ delete options.path;
2687
+ }
2688
+ if ( typeof(options[':method']) == 'undefined' ) {
2689
+ options[':method'] = options.method.toUpperCase();
2690
+ delete options.method;
2691
+ }
2692
+
2693
+ // only if binary !!
2694
+ // if ( typeof(options['content-length']) == 'undefined' ) {
2695
+ // options['content-length'] = options.headers['content-length'] ;
2696
+ // delete options.headers['content-length'];
2697
+ // }
2698
+ // if ( typeof(options['content-type']) == 'undefined' ) {
2699
+ // options['content-type'] = options.headers['content-type'] ;
2700
+ // delete options.headers['content-type'];
2701
+ // }
2702
+
2703
+ if ( typeof(options[':scheme']) == 'undefined' ) {
2704
+ options[':scheme'] = options.scheme ;
2705
+ }
2706
+
2707
+ if ( typeof(options.ca) != 'undefined' ) {
2708
+ try {
2709
+ options.ca = fs.readFileSync(options.ca);
2710
+ } catch(err) {
2711
+ if ( typeof(callback) != 'undefined' ) {
2712
+ callback(err)
2713
+ } else {
2714
+ self.emit('query#complete', err);
2715
+ }
2716
+
2717
+ return;
2718
+ }
2719
+
2720
+ } else {
2721
+ console.warn('[ CONTROLLER ][ HTTP/2.0#query ] options.ca not found !');
2722
+ }
2723
+
2724
+
2725
+ var body = Buffer.from(options.queryData);
2726
+ options.headers['content-length'] = body.length;
2727
+ delete options.queryData;
2728
+
2729
+
2730
+
2731
+ const client = browser.connect(options.hostname, options);
2732
+
2733
+
2734
+ const {
2735
+ HTTP2_HEADER_PROTOCOL,
2736
+ HTTP2_HEADER_SCHEME,
2737
+ HTTP2_HEADER_AUTHORITY,
2738
+ HTTP2_HEADER_PATH,
2739
+ HTTP2_HEADER_METHOD,
2740
+ HTTP2_HEADER_STATUS
2741
+ } = browser.constants;
2742
+
2743
+
2744
+ if ( typeof(local.req.headers['x-requested-with']) != 'undefined' ) {
2745
+ options.headers['x-requested-with'] = local.req.headers['x-requested-with']
2746
+ }
2747
+
2748
+ if ( typeof(local.req.headers['access-control-allow-credentials']) != 'undefined' ) {
2749
+ options.headers['access-control-allow-credentials'] = local.req.headers['access-control-allow-credentials']
2750
+ }
2751
+
2752
+ if ( typeof(local.req.headers['content-type']) != 'undefined' && local.req.headers['content-type'] != options.headers['content-type'] ) {
2753
+ options.headers['content-type'] = local.req.headers['content-type']
2754
+ }
2755
+
2756
+ var headers = merge({
2757
+ [HTTP2_HEADER_METHOD]: options[':method'],
2758
+ [HTTP2_HEADER_PATH]: options[':path']
2759
+ }, options.headers);
2760
+
2761
+ // merging with user options
2762
+ for (var o in options) {
2763
+ if (!/^\:/.test(o) && !/headers/.test(o) && typeof(headers[o]) == 'undefined' ) {
2764
+ headers[o] = options[o]
2765
+ }
2766
+ }
2767
+
2768
+ /**
2769
+ * sessionOptions
2770
+ * endStream <boolean> true if the Http2Stream writable side should be closed initially, such as when sending a GET request that should not expect a payload body.
2771
+ * exclusive <boolean> When true and parent identifies a parent Stream, the created stream is made the sole direct dependency of the parent, with all other existing dependents made a dependent of the newly created stream. Default: false.
2772
+ * parent <number> Specifies the numeric identifier of a stream the newly created stream is dependent on.
2773
+ * weight <number> Specifies the relative dependency of a stream in relation to other streams with the same parent. The value is a number between 1 and 256 (inclusive).
2774
+ * waitForTrailers <boolean> When true, the Http2Stream will emit the 'wantTrailers' event after the final DATA frame has been sent.
2775
+ */
2776
+ var sessionOptions = {}, endStream = true;
2777
+ if ( body.length > 0 || options.headers['x-requested-with'] ) {
2778
+ endStream = false;
2779
+ sessionOptions.endStream = endStream;
2780
+ }
2781
+
2782
+
2783
+ client.on('error', (error) => {
2784
+
2785
+ console.error( '`'+ options[':path']+ '` : '+ error.stack||error.message);
2786
+ if (
2787
+ typeof(error.cause) != 'undefined' && typeof(error.cause.code) != 'undefined' && /ECONNREFUSED|ECONNRESET/.test(error.cause.code)
2788
+ || /ECONNREFUSED|ECONNRESET/.test(error.code)
2789
+ ) {
2790
+
2791
+ var port = getContext('gina').ports[options.protocol][options.scheme.replace(/\:/, '')][ options.port ];
2792
+ if ( typeof(port) != 'undefined' ) {
2793
+ error.accessPoint = port;
2794
+ error.message = 'Could not connect to [ ' + error.accessPoint + ' ].\nThe `'+port.split(/\@/)[0]+'` bundle is offline or unreachable.\n';
2795
+ }
2796
+ }
2797
+ self.throwError(error);
2798
+ return;
2799
+ });
2800
+
2801
+ client.on('connect', () => {
2802
+
2803
+ var req = client.request( headers, sessionOptions );
2804
+
2805
+
2806
+ // req.on('response', function onQueryResponse(headers, flags) {
2807
+ // for (const name in headers) {
2808
+ // console.debug(`${name}: ${headers[name]}`);
2809
+ // }
2810
+ // });
2811
+
2812
+ req.setEncoding('utf8');
2813
+ var data = '';
2814
+ req.on('data', function onQueryDataChunk(chunk) {
2815
+ data += chunk;
2816
+ });
2817
+
2818
+ req.on('error', function onQueryError(error) {
2819
+
2820
+ if (
2821
+ typeof(error.cause) != 'undefined' && typeof(error.cause.code) != 'undefined' && /ECONNREFUSED|ECONNRESET/.test(error.cause.code)
2822
+ || /ECONNREFUSED|ECONNRESET/.test(error.code)
2823
+ ) {
2824
+
2825
+ var port = getContext('gina').ports[options.protocol][options.scheme.replace(/\:/, '')][ options.port ];
2826
+ if ( typeof(port) != 'undefined' ) {
2827
+ error.accessPoint = port;
2828
+ error.message = 'Could not connect to [ ' + error.accessPoint + ' ].\n' + error.message;
2829
+ }
2830
+ }
2831
+
2832
+
2833
+ console.error(error.stack||error.message);
2834
+ // you can get here if :
2835
+ // - you are trying to query using: `enctype="multipart/form-data"`
2836
+ // - server responded with an error
2837
+ if ( typeof(callback) != 'undefined' ) {
2838
+ callback(error);
2839
+ } else {
2840
+ error = {
2841
+ status : 500,
2842
+ error : error.stack ||error.message
2843
+ };
2844
+
2845
+ self.emit('query#complete', error)
2846
+ }
2847
+
2848
+ return;
2849
+ });
2850
+
2851
+ req.on('end', function onEnd() {
2852
+
2853
+ // exceptions filter
2854
+ if ( typeof(data) == 'string' && /^Unknown ALPN Protocol/.test(data) ) {
2855
+ var err = {
2856
+ status: 500,
2857
+ error: new Error(data)
2858
+ };
2859
+
2860
+ if ( typeof(callback) != 'undefined' ) {
2861
+ callback(err)
2862
+ } else {
2863
+ self.emit('query#complete', err)
2864
+ }
2865
+
2866
+ return
2867
+ }
2868
+
2869
+ //Only when needed.
2870
+ if ( typeof(callback) != 'undefined' ) {
2871
+ if ( typeof(data) == 'string' && /^(\{|%7B|\[{)|\[\]/.test(data) ) {
2872
+ try {
2873
+ data = JSON.parse(data);
2874
+ // just in case
2875
+ if ( typeof(data.status) == 'undefined' ) {
2876
+ var currentRule = local.options.rule || local.req.routing.rule;
2877
+ console.warn( '['+ currentRule +'] ' + 'Response status code is `undefined`: switching to `200`');
2878
+ data.status = 200;
2879
+ }
2880
+ } catch (err) {
2881
+ data = {
2882
+ status : 500,
2883
+ error : err
2884
+ }
2885
+ console.error(err);
2886
+ }
2887
+ } else if ( !data && this.aborted && this.destroyed) {
2888
+ data = {
2889
+ status : 500,
2890
+ error : new Error('request aborted')
2891
+ }
2892
+ }
2893
+ //console.debug(options[':method']+ ' ['+ (data.status || 200) +'] '+ options[':path']);
2894
+ try {
2895
+ // intercepting fallback redirect
2896
+ if ( data.status && /^3/.test(data.status) && typeof(data.headers) != 'undefined' ) {
2897
+ local.res.writeHead(data.status, data.headers);
2898
+ return local.res.end();
2899
+ }
2900
+
2901
+ if ( data.status && !/^2/.test(data.status) && typeof(local.options.conf.server.coreConfiguration.statusCodes[data.status]) != 'undefined' ) {
2902
+ if ( /^5/.test(data.status) ) {
2903
+ return callback(data)
2904
+ } else {
2905
+ self.throwError(data);
2906
+ return;
2907
+ }
2908
+ } else {
2909
+ // required when control is used in an halted state
2910
+ // Ref.: resumeRequest()
2911
+ if ( self && self.isHaltedRequest() && typeof(local.onHaltedRequestResumed) != 'undefined' ) {
2912
+ local.onHaltedRequestResumed(false);
2913
+ }
2914
+ return callback( false, data )
2915
+ }
2916
+
2917
+ } catch (e) {
2918
+ var infos = local.options, controllerName = infos.controller.substr(infos.controller.lastIndexOf('/'));
2919
+ var msg = 'Controller Query Exception while catching back.\nBundle: '+ infos.bundle +'\nController File: /controllers'+ controllerName +'\nControl: this.'+ infos.control +'(...)\n\r' + e.stack;
2920
+ var exception = new Error(msg);
2921
+ exception.status = 500;
2922
+ self.throwError(exception);
2923
+ return;
2924
+ }
2925
+
2926
+ } else {
2927
+ if ( typeof(data) == 'string' && /^(\{|%7B|\[{)|\[\]/.test(data) ) {
2928
+ try {
2929
+ data = JSON.parse(data)
2930
+ } catch (e) {
2931
+ data = {
2932
+ status : 500,
2933
+ error : data
2934
+ }
2935
+ self.emit('query#complete', data)
2936
+ }
2937
+ }
2938
+
2939
+ // intercepting fallback redirect
2940
+ if ( data.status && /^3/.test(data.status) && typeof(data.headers) != 'undefined' ) {
2941
+ self.removeAllListeners(['query#complete']);
2942
+ local.res.writeHead(data.status, data.headers);
2943
+ return local.res.end();
2944
+ }
2945
+
2946
+ if ( data.status && !/^2/.test(data.status) && typeof(local.options.conf.server.coreConfiguration.statusCodes[data.status]) != 'undefined' ) {
2947
+ self.emit('query#complete', data)
2948
+ } else {
2949
+ // required when control is used in an halted state
2950
+ // Ref.: resumeRequest()
2951
+ if ( self.isHaltedRequest() && typeof(local.onHaltedRequestResumed) != 'undefined' ) {
2952
+ local.onHaltedRequestResumed(false);
2953
+ }
2954
+ self.emit('query#complete', false, data)
2955
+ }
2956
+ }
2957
+
2958
+ client.close();
2959
+ });
2960
+
2961
+ if (!endStream) {
2962
+ req.end(body);
2963
+ }
2964
+ });
2965
+
2966
+
2967
+ return {
2968
+ onComplete : function(cb) {
2969
+
2970
+ self.once('query#complete', function(err, data){
2971
+
2972
+ if ( typeof(data) == 'string' && /^(\{|%7B|\[{)|\[\]/.test(data) ) {
2973
+ try {
2974
+ data = JSON.parse(data)
2975
+ } catch (err) {
2976
+ data = {
2977
+ status : 500,
2978
+ error : data
2979
+ }
2980
+ }
2981
+ }
2982
+
2983
+ try {
2984
+ if ( data.status && !/^2/.test(data.status) && typeof(local.options.conf.server.coreConfiguration.statusCodes[data.status]) != 'undefined') {
2985
+ cb(data)
2986
+ } else {
2987
+ // required when control is used in an halted state
2988
+ // Ref.: resumeRequest()
2989
+ if ( self.isHaltedRequest() && typeof(local.onHaltedRequestResumed) != 'undefined' ) {
2990
+ local.onHaltedRequestResumed(err);
2991
+ }
2992
+
2993
+ cb(err, data)
2994
+ }
2995
+ } catch (e) {
2996
+ var infos = local.options, controllerName = infos.controller.substr(infos.controller.lastIndexOf('/'));
2997
+ var msg = 'Controller Query Exception while catching back.\nBundle: '+ infos.bundle +'\nController File: /controllers'+ controllerName +'\nControl: this.'+ infos.control +'(...)\n\r' + e.stack;
2998
+ var exception = new Error(msg);
2999
+ exception.status = 500;
3000
+ self.throwError(exception);
3001
+ return;
3002
+ }
3003
+ })
3004
+ }
3005
+ }
3006
+ }
3007
+
3008
+
3009
+ /**
3010
+ * forward404Unless
3011
+ *
3012
+ * @param {boolean} condition
3013
+ * @param {object} req
3014
+ * @param {object} res
3015
+ *
3016
+ * @callback [ next ]
3017
+ * @param {string | boolean} err
3018
+ *
3019
+ * @returns {string | boolean} err
3020
+ * */
3021
+ this.forward404Unless = function(condition, req, res, next) {
3022
+ var pathname = req.url;
3023
+
3024
+ if (!condition) {
3025
+ self.throwError(res, 404, 'Page not found\n' + pathname);
3026
+ var err = new Error('Page not found\n' + pathname);
3027
+ if ( typeof(next) != 'undefined')
3028
+ next(err)
3029
+ else
3030
+ return err
3031
+ } else {
3032
+ if ( typeof(next) != 'undefined' )
3033
+ next(false)
3034
+ else
3035
+ return false
3036
+ }
3037
+ }
3038
+
3039
+ /**
3040
+ * Get all Params
3041
+ * */
3042
+ var getParams = function(req) {
3043
+
3044
+ req.getParams = function() {
3045
+ // Clone
3046
+ var params = JSON.clone(req.params);
3047
+ switch( req.method.toLowerCase() ) {
3048
+ case 'get':
3049
+ params = merge(params, req.get, true);
3050
+ break;
3051
+
3052
+ case 'post':
3053
+ params = merge(params, req.post, true);
3054
+ break;
3055
+
3056
+ case 'put':
3057
+ params = merge(params, req.put, true);
3058
+ break;
3059
+
3060
+ case 'delete':
3061
+ params = merge(params, req.delete, true);
3062
+ break;
3063
+ }
3064
+
3065
+ return params
3066
+ }
3067
+
3068
+ req.getParam = function(name) {
3069
+
3070
+ var param = null;
3071
+ switch( req.method.toLowerCase() ) {
3072
+ case 'get':
3073
+ param = req.get[name];
3074
+ break;
3075
+
3076
+ case 'post':
3077
+ param = req.post[name];
3078
+ break;
3079
+
3080
+ case 'put':
3081
+ param= req.put[name];
3082
+ break;
3083
+
3084
+ case 'delete':
3085
+ param = req.delete[name];
3086
+ break;
3087
+ }
3088
+
3089
+ return param
3090
+ }
3091
+ }
3092
+
3093
+ /**
3094
+ * Forward request
3095
+ * Allowing x-bundle forward
3096
+ * Attention: this is a work in progres, do not use it yet
3097
+ *
3098
+ * @param {object} req
3099
+ * @param {object} res
3100
+ * @param {callback} next
3101
+ * @returns
3102
+ */
3103
+ this.forward = function(req, res, next) {
3104
+ var route = req.routing;
3105
+ if ( typeof(route.param.url) == 'undefined' || /^(null|\s*)$/.test(route.param.url) ) {
3106
+ self.throwError( new Error('`route.param.url` must be defiend in your route: `'+ route.rule +'`') );
3107
+ return;
3108
+ }
3109
+
3110
+ var param = {};
3111
+ for (let p in route.param) {
3112
+ if ( /^(url|urlIndex|control|file|title|bundle|project|hostname|port|path|method)$/.test(p) ) {
3113
+ continue;
3114
+ }
3115
+ param[p] = route.param[p]
3116
+ }
3117
+ var routeObj = null;
3118
+ if ( typeof(route.param.urlIndex) != 'undefined' ) {
3119
+ routeObj = lib.routing.getRoute(route.param.url, param, route.param.urlIndex);
3120
+ } else {
3121
+ routeObj = lib.routing.getRoute(route.param.url, param);
3122
+ }
3123
+ var ca = self.getConfig('settings').server.credentials.ca;
3124
+ var hostname = null, port = null, path = null;
3125
+ // by default
3126
+ var project = local.options.conf.projectName;
3127
+ if ( typeof(route.param.project) != 'undefined' && /^(null|\s*)$/.test(route.param.project) ) {
3128
+ project = route.param.project;
3129
+ } // TODO - add support for project pointer : getContext('gina').projects[project]
3130
+ if (/\@(.*)$/.test(route.param.url)) {
3131
+ var targetedBundle = route.param.url.substr(route.param.url.lastIndexOf('@')+1);
3132
+ hostname = targetedBundle +'@'+ project;
3133
+ port = hostname;
3134
+ var webroot = getContext('gina').config.envConf[targetedBundle][local.options.conf.env].server.webroot;
3135
+ path = (/\/$/.test(webroot)) ? webroot.substr(0, webroot.length-1) : webroot;
3136
+ } else {
3137
+ hostname = route.param.hostname;
3138
+ port = route.param.port;
3139
+ path = route.param.port;
3140
+ }
3141
+
3142
+ var method = null;
3143
+ if ( typeof(route.param.method) != 'undefined' ) {
3144
+ method = route.param.method.toLowerCase();
3145
+ } else {
3146
+ method = req.method.toLowerCase();
3147
+ }
3148
+
3149
+ var opt = {
3150
+ ca: ca,
3151
+ hostname: hostname,
3152
+ port: port,
3153
+ path: path,
3154
+ method: method
3155
+ }
3156
+ if (self.isCacheless() || self.isLocalScope() ) {
3157
+ opt.rejectUnauthorized = false;
3158
+ }
3159
+
3160
+ var obj = req[ req.method.toLowerCase() ];
3161
+ // if ( req.files != 'undefined' ) {
3162
+ // obj.files = req.files;
3163
+ // }
3164
+ self.query(opt, obj, function onForward(err, result){
3165
+ if (err) {
3166
+ self.throwError(err);
3167
+ return;
3168
+ }
3169
+
3170
+ // TODO - filter : redirect & location
3171
+
3172
+ // if ( self.isXMLRequest() || !hasViews() || !local.options.isUsingTemplate && !hasViews() || hasViews() && !local.options.isUsingTemplate ) {
3173
+ self.renderJSON(result)
3174
+ // } else {
3175
+ // self.render(result)
3176
+ // }
3177
+ });
3178
+ }
3179
+
3180
+
3181
+ /**
3182
+ * Get config
3183
+ *
3184
+ * @param {string} [name] - Conf name without extension.
3185
+ * @returns {object} config
3186
+ *
3187
+ * */
3188
+ this.getConfig = function(name) {
3189
+ if ( typeof(name) != 'undefined' ) {
3190
+ try {
3191
+ // needs to be read only
3192
+ //config = JSON.clone(local.options.conf.content[name]);
3193
+ //config = Object.freeze(local.options.conf.content[name]);
3194
+ //Object.seal(local.options.conf.content[name]);
3195
+ //Object.freeze(local.options.conf.content[name]);
3196
+ return JSON.clone(local.options.conf.content[name]);
3197
+ } catch (err) {
3198
+ return undefined;
3199
+ }
3200
+ } else {
3201
+ // config = JSON.stringify(local.options.conf);
3202
+ // return JSON.parse(config)
3203
+ return JSON.clone(local.options.conf);
3204
+ }
3205
+ }
3206
+
3207
+ /**
3208
+ * Get locales
3209
+ *
3210
+ * @param {string} [shortCountryCode] - e.g. EN
3211
+ *
3212
+ * @returns {object} locales
3213
+ * */
3214
+ this.getLocales = function (shortCountryCode) {
3215
+
3216
+ var userLocales = local.options.conf.locales;
3217
+
3218
+ if ( typeof(shortCountryCode) != 'undefined' ) {
3219
+ shortCountryCode = shortCountryCode.toLowerCase();
3220
+ var locales = new Collection( getContext('gina').locales );
3221
+
3222
+ try {
3223
+ userLocales = locales.findOne({ lang: shortCountryCode }).content
3224
+ } catch (err) {
3225
+ console.warn('language code `'+ shortCountryCode +'` not handled to setup locales: replacing by `'+ local.options.conf.content.settings.region.shortCode +'`');
3226
+ userLocales = locales.findOne({ lang: local.options.conf.content.settings.region.shortCode }).content // by default
3227
+ }
3228
+ }
3229
+
3230
+
3231
+ /**
3232
+ * Get countries list
3233
+ *
3234
+ * @param {string} [code] - e.g.: short, long, fifa, m49
3235
+ *
3236
+ * @returns {object} countries - countries code & value list
3237
+ * */
3238
+ var getCountries = function (code) {
3239
+ var list = {}, cde = 'short', name = null;
3240
+
3241
+ if ( typeof(code) != 'undefined' && typeof(userLocales[0][code]) == 'string' ) {
3242
+ cde = code
3243
+ } else if ( typeof(code) != 'undefined' ) (
3244
+ console.warn('`'+ code +'` not supported : sticking with `short` code')
3245
+ )
3246
+
3247
+
3248
+ for ( var i = 0, len = userLocales.length; i< len; ++i ) {
3249
+
3250
+ if (userLocales[i][cde]) {
3251
+
3252
+ name = userLocales[i].officialName.short || userLocales[i].full;
3253
+
3254
+ if ( name )
3255
+ list[ userLocales[i][cde] ] = name;
3256
+ }
3257
+ }
3258
+
3259
+ return list
3260
+ }
3261
+
3262
+ return {
3263
+ 'getCountries': getCountries
3264
+ }
3265
+ }
3266
+
3267
+ /**
3268
+ * Get forms rules
3269
+ *
3270
+ *
3271
+ * @returns {object} rules
3272
+ *
3273
+ * */
3274
+ this.getFormsRules = function () {
3275
+ var bundle = local.options.conf.bundle; // by default
3276
+ var form = null;
3277
+ var rule = null;
3278
+ var isGettingRulesFromAnotherBundle = false;
3279
+ var rules = {};
3280
+ if ( typeof(local.req.ginaHeaders) != 'undefined' && typeof(local.req.ginaHeaders.form) != 'undefined' ) {
3281
+ form = local.req.ginaHeaders.form;
3282
+ if ( typeof(form.rule) != 'undefined' ) {
3283
+ var ruleInfos = form.rule.split(/\@/);
3284
+ rule = ruleInfos[0];
3285
+ // rules might be located in another bundle
3286
+ if (ruleInfos[1] && ruleInfos[1] != '' && ruleInfos[1] != bundle) {
3287
+ bundle = ruleInfos[1];
3288
+ isGettingRulesFromAnotherBundle = true;
3289
+ }
3290
+ }
3291
+ }
3292
+
3293
+ if ( form && typeof(form.id) != 'undefined' ) {
3294
+ try {
3295
+ if (isGettingRulesFromAnotherBundle) {
3296
+ rules = JSON.clone(getConfig()[bundle][local.options.conf.env].content.forms.rules[form.id]) || null;
3297
+ } else {
3298
+ rules = JSON.clone(local.options.conf.content.forms).rules[form.id] || null;
3299
+ }
3300
+
3301
+ if (!rules) {
3302
+ rules = {};
3303
+ console.warn('[CONTROLLER]['+ local.options.conf.bundle +'][Backend validation] did not find matching rules for form.id `'+ form.id +'` for `'+ bundle+' bundle`. Do not Panic if you did not defined any.')
3304
+ }
3305
+ } catch (ruleErr) {
3306
+ self.throwError(ruleErr);
3307
+ return;
3308
+ }
3309
+ }
3310
+
3311
+ return rules;
3312
+ }
3313
+
3314
+ this.push = function(payload) {
3315
+
3316
+ var req = local.req, res = local.res;
3317
+ var method = req.method.toLowerCase();
3318
+ // if no session defined, will push to all active clients
3319
+ var sessionId = ( typeof(req[method].sessionID) != 'undefined' ) ? req[method].sessionID : null;
3320
+
3321
+ // resume current session
3322
+
3323
+ if (!payload) {
3324
+ payload = null;
3325
+ if ( typeof(req[method]) != 'undefined' && typeof(req[method].payload) != 'undefined' ) {
3326
+ if ( typeof(payload) == 'string' ) {
3327
+ payload = decodeURIComponent(req[method].payload)
3328
+ } else {
3329
+ payload = JSON.stringify(req[method].payload)
3330
+ }
3331
+ }
3332
+ } else if ( typeof(payload) == 'object' ) {
3333
+ payload = JSON.stringify(payload)
3334
+ }
3335
+
3336
+ try {
3337
+ var clients = null;
3338
+ if (sessionId) {
3339
+ clients = self.serverInstance.eio.getClientsBySessionId(sessionId);
3340
+ if (clients)
3341
+ clients.send(payload);
3342
+ }
3343
+
3344
+ // send to all clients if no specific sessionId defined
3345
+ if (!sessionId) {
3346
+ clients = self.serverInstance.eio.clients;
3347
+ for (var id in clients) {
3348
+ clients[id].send(payload)
3349
+ }
3350
+ }
3351
+
3352
+ res.end();
3353
+ } catch(err) {
3354
+ self.throwError(err);
3355
+ return;
3356
+ }
3357
+ }
3358
+
3359
+ var getSession = function() {
3360
+ var session = null;
3361
+ if ( typeof(local.req.session) != 'undefined') {
3362
+ session = local.req.session;
3363
+ }
3364
+ // passport override
3365
+ if (!session && typeof(local.req.session) != 'undefined' && typeof(local.req.session.user) != 'undefined') {
3366
+ session = local.req.session.user;
3367
+ }
3368
+
3369
+ return session;
3370
+ }
3371
+
3372
+ this.isHaltedRequest = function(session) {
3373
+ // trying to retrieve session since it is optional
3374
+ if ( typeof(session) == 'undefined' ) {
3375
+ session = getSession();
3376
+ // if ( typeof(local.req.session) != 'undefined' && typeof(local.req.session.haltedRequest) != 'undefined' ) {
3377
+ // session = local.req.session;
3378
+ // }
3379
+ // // passport
3380
+ // if (!session && typeof(local.req.session) != 'undefined' && typeof(local.req.session.user) != 'undefined' && typeof(local.req.session.user.haltedRequest) != 'undefined' ) {
3381
+ // session = local.req.session.user;
3382
+ // }
3383
+ if (
3384
+ !session
3385
+ ||
3386
+ typeof(session) != 'undefined'
3387
+ && typeof(session.haltedRequest) == 'undefined'
3388
+ ) {
3389
+ return false;
3390
+ }
3391
+ }
3392
+
3393
+ return (typeof(session.haltedRequest) != 'undefined' ) ? true : false;
3394
+ }
3395
+
3396
+
3397
+ local.haltedRequestUrlResumed = false;
3398
+
3399
+ this.pauseRequest = function(data, requestStorage) {
3400
+
3401
+
3402
+ // saving halted request
3403
+ var req = local.req
3404
+ , res = local.res
3405
+ , next = local.next
3406
+ , haltedRequest = {
3407
+ url : req.url,
3408
+ routing : req.routing,
3409
+ method : req.method.toLowerCase(),
3410
+ data : JSON.clone(data)
3411
+ }
3412
+ ;
3413
+
3414
+ if (
3415
+ typeof(requestStorage) == 'undefined'
3416
+ && typeof(req.session) != 'undefined'
3417
+ ) {
3418
+ requestStorage = req.session;
3419
+ }
3420
+
3421
+ if (
3422
+ typeof(requestStorage) == 'undefined'
3423
+ ) {
3424
+ var error = new ApiError('`requestStorage` is required', 424);
3425
+ self.throwError(error);
3426
+ return;
3427
+ }
3428
+
3429
+ var requestParams = {}, i = 0;
3430
+ for (var p in req.params) {
3431
+ if (i > 0) {
3432
+ requestParams[p] = req.params[p];
3433
+ }
3434
+ ++i;
3435
+ }
3436
+ if (requestParams.count() > 0) {
3437
+ haltedRequest.params = requestParams;
3438
+ }
3439
+
3440
+ requestStorage.haltedRequest = haltedRequest;
3441
+
3442
+ return requestStorage;
3443
+ }
3444
+
3445
+
3446
+ /**
3447
+ * resumeRequest
3448
+ * Used to resume an halted request
3449
+ * Requirements :
3450
+ * - a middleware attached `haltedRequest` to userSession
3451
+ * OR
3452
+ * - a persistant object where `haltedRequest` is attached
3453
+ *
3454
+ * @param {object} req
3455
+ * @param {object} res
3456
+ * @param {callback|null} next
3457
+ * @param {object} [requestStorage] - Will try to use sessionStorage if not passed
3458
+ */
3459
+ this.resumeRequest = function(requestStorage) {
3460
+
3461
+ if (local.haltedRequestUrlResumed)
3462
+ return;
3463
+
3464
+ var haltedRequest = null
3465
+ , req = local.req
3466
+ , res = local.res
3467
+ , next = local.next
3468
+ ;
3469
+
3470
+ if (
3471
+ typeof(requestStorage) == 'undefined'
3472
+ && typeof(req.session) != 'undefined'
3473
+ ) {
3474
+ requestStorage = req.session;
3475
+ }
3476
+
3477
+ if (
3478
+ typeof(requestStorage) == 'undefined'
3479
+ ||
3480
+ typeof(requestStorage) != 'undefined'
3481
+ && typeof(requestStorage.haltedRequest) == 'undefined'
3482
+ ) {
3483
+ var error = new ApiError('`requestStorage.haltedRequest` is required', 424);
3484
+ self.throwError(error);
3485
+ return;
3486
+ }
3487
+ haltedRequest = requestStorage.haltedRequest;
3488
+ var data = haltedRequest.data || {};
3489
+ // request methods cleanup
3490
+ // checkout /framework/{verrsion}/core/template/conf/(settings.json).server.supportedRequestMethods
3491
+ var serverSupportedMethods = local.options.conf.server.supportedRequestMethods;
3492
+ for (let method in serverSupportedMethods) {
3493
+ if (req.method.toLowerCase() == method) {
3494
+ data = merge(data, req[method])
3495
+ }
3496
+
3497
+ delete req[method];
3498
+ }
3499
+
3500
+
3501
+ var dataAsParams = {};
3502
+ if (data.count() > 0) {
3503
+ dataAsParams = JSON.clone(haltedRequest.data);
3504
+ }
3505
+ var url = lib.routing.getRoute(haltedRequest.routing.rule, haltedRequest.params||dataAsParams).url;
3506
+ var requiredController = self; // by default;
3507
+ if ( req.routing.namespace != haltedRequest.routing.namespace ) {
3508
+ try {
3509
+ requiredController = self.requireController(haltedRequest.routing.namespace, self._options );
3510
+ } catch (err) {
3511
+ self.throwError(err);
3512
+ }
3513
+ }
3514
+ req.routing = haltedRequest.routing;
3515
+ req.method = haltedRequest.method;
3516
+ req[haltedRequest.method] = data;
3517
+
3518
+ local.haltedRequestUrlResumed = true;
3519
+ if ( /GET/i.test(req.method) ) {
3520
+ if ( typeof(requestStorage.haltedRequest) != 'undefined' ) {
3521
+ delete requestStorage.haltedRequest;
3522
+ }
3523
+ delete requestStorage.haltedRequest;
3524
+ delete requestStorage.inheritedData;
3525
+ requestStorage.haltedRequestUrlResumed = url;
3526
+
3527
+ requiredController.redirect(url, true);
3528
+ } else {
3529
+ local.onHaltedRequestResumed = function(err) {
3530
+ if (!err) {
3531
+ delete requestStorage.haltedRequest;
3532
+ delete requestStorage.inheritedData;
3533
+ }
3534
+ }
3535
+ if ( typeof(next) == 'function' ) {
3536
+ console.warn('About to override `next` param');
3537
+ }
3538
+
3539
+ try {
3540
+ requiredController[req.routing.param.control](req, res, next);
3541
+ // consuming it
3542
+ local.onHaltedRequestResumed(false);
3543
+ } catch(err) {
3544
+ console.error('[ BUNDLE ][ '+ local.options.conf.bundle +' ][ Controller ] Could not resume haltedRequest\n' + err.stack );
3545
+ self.throwError(err);
3546
+ }
3547
+
3548
+
3549
+ }
3550
+ }
3551
+
3552
+
3553
+ this.renderCustomError = function (req, res, next) {
3554
+
3555
+ // preventing multiple call of self.renderWithoutLayout() when controller is rendering from another required controller
3556
+ if (local.options.renderingStack.length > 1) {
3557
+ return false;
3558
+ }
3559
+ local.options.isRenderingCustomError = true;
3560
+
3561
+ //local.options.isWithoutLayout = true;
3562
+
3563
+ var data = null;
3564
+ if ( typeof(req.routing.param.error) != 'undefined' ) {
3565
+ data = JSON.clone(req.routing.param.error) || {};
3566
+ delete req.routing.param.error
3567
+ }
3568
+
3569
+ var session = getSession();
3570
+ if (session) {
3571
+ data.session = JSON.clone(session)
3572
+ }
3573
+ var displayToolbar = req.routing.param.displayToolbar || false;
3574
+ if (req.routing.param.displayToolbar) {
3575
+ delete req.routing.param.displayToolbar
3576
+ }
3577
+ var isLocalOptionResetNeeded = req.routing.param.isLocalOptionResetNeeded || false;
3578
+ var errOptions = null;
3579
+ if (isLocalOptionResetNeeded) {
3580
+ delete req.routing.param.isLocalOptionResetNeeded;
3581
+ var bundleConf = JSON.clone(local.options.conf);
3582
+ var bundle = req.routing.bundle;
3583
+ var param = req.routing.param;
3584
+ var localOptions = {
3585
+ // view namespace first
3586
+ //namespace : null,
3587
+ control : param.control,
3588
+ //controller : controllerFile,
3589
+ //controller: '<span class="gina-bundle-name">' + bundle +'</span>/controllers/controller.js',
3590
+ file: param.file,
3591
+ //layout: param.file,
3592
+ //bundle : bundle,//module
3593
+ bundlePath : bundleConf.bundlesPath + '/' + bundle,
3594
+ renderingStack : bundleConf.renderingStack,
3595
+ //rootPath : self.executionPath,
3596
+ // We don't want to keep original conf untouched
3597
+ //conf : JSON.clone(conf),
3598
+ //instance: self.serverInstance,
3599
+ //template: (routeHasViews) ? bundleConf.content.templates[templateName] : undefined,
3600
+ //isUsingTemplate: local.isUsingTemplate,
3601
+ //cacheless: cacheless,
3602
+ path: null //, // user custom path : namespace should be ignored | left blank
3603
+ //assets: {}
3604
+ };
3605
+ errOptions = merge(localOptions, local.options);
3606
+
3607
+
3608
+ }
3609
+ delete local.options.namespace;
3610
+ self.render(data, displayToolbar, errOptions);
3611
+ }
3612
+
3613
+
3614
+ /**
3615
+ * Throw error
3616
+ *
3617
+ * @param {object} [ res ]
3618
+ * @param {number} code
3619
+ * @param {string} msg
3620
+ *
3621
+ * @returns {void}
3622
+ * */
3623
+ this.throwError = function(res, code, msg) {
3624
+ self.isProcessingError = true;
3625
+ var errorObject = null; // to be returned
3626
+
3627
+ // preventing multiple call of self.throwError() when controller is rendering from another required controller
3628
+ if (local.options.renderingStack.length > 1) {
3629
+ return false
3630
+ }
3631
+ var bundleConf = local.options.conf;
3632
+ var bundle = bundleConf.bundle;
3633
+ // handle error fallback
3634
+ // err.fallback must be a valide route object or a url string
3635
+ var fallback = null;
3636
+ var standardErrorMessage = null;
3637
+ if (
3638
+ arguments[0] instanceof Error
3639
+ || arguments.length == 1 && typeof(res) == 'object'
3640
+ || arguments[arguments.length-1] instanceof Error
3641
+ || typeof(arguments[arguments.length-1]) == 'string' && !(arguments[0] instanceof Error)
3642
+ ) {
3643
+
3644
+ code = ( res && typeof(res.status) != 'undefined' ) ? res.status : 500;
3645
+
3646
+ if ( typeof(statusCodes[code]) != 'undefined' ) {
3647
+ standardErrorMessage = statusCodes[code];
3648
+ } else {
3649
+ console.warn('[ ApiValidator ] statusCode `'+ code +'` not matching any definition in `'+_( getPath('gina').core + '/status.codes')+'`\nPlease contact the Gina dev team to add one if required');
3650
+ }
3651
+
3652
+ errorObject = {
3653
+ status : code,
3654
+ error : res.error || res.message || standardErrorMessage
3655
+ };
3656
+
3657
+ if ( res instanceof Error || typeof(res.stack) != 'undefined' ) {
3658
+ //errorObject.status = code;
3659
+ //errorObject.error = standardErrorMessage || res.error || res.message;
3660
+ errorObject.stack = res.stack;
3661
+ if (res.message && typeof(res.message) == 'string') {
3662
+ errorObject.message = res.message;
3663
+ } else if (res.message) {
3664
+ console.warn('[ Controller ] Ignoring message because of the format.\n'+res.message)
3665
+ }
3666
+
3667
+ } else if ( typeof(arguments[arguments.length-1]) == 'string' ) {
3668
+ // formated error
3669
+ errorObject.message = arguments[arguments.length-1]
3670
+ } else if (
3671
+ arguments[arguments.length-1] instanceof Error
3672
+ || typeof(res) == 'object' && typeof(res.stack) != 'undefined'
3673
+ ) {
3674
+ errorObject = merge(arguments[arguments.length-1], errorObject)
3675
+ }
3676
+
3677
+ if ( typeof(res.fallback) != 'undefined' ) {
3678
+ fallback = res.fallback
3679
+ }
3680
+
3681
+ res = local.res;
3682
+
3683
+ } else if (arguments.length < 3) {
3684
+ msg = code || null;
3685
+ code = res || 500;
3686
+ res = local.res;
3687
+ }
3688
+
3689
+ var responseHeaders = res.getHeaders() || local.res.getHeaders();
3690
+ var req = local.req;
3691
+ var next = local.next;
3692
+ if (!res.headersSent) {
3693
+ // DELETE request methods don't normaly use a view,
3694
+ // but if we are calling it from a view, we should render the error back to the view
3695
+ if ( self.isXMLRequest() || !hasViews() && !/delete/i.test(req.method) || !local.options.isUsingTemplate && !hasViews() || hasViews() && !local.options.isUsingTemplate ) {
3696
+ // fallback interception
3697
+ if ( fallback ) {
3698
+ if ( typeof(fallback) == 'string' ){ // string url: user provided
3699
+ return self.redirect( fallback, true )
3700
+ } else {
3701
+ // else, using url from route object
3702
+ // Reminder
3703
+ // Here, we use route.toUrl() intead of
3704
+ // route.url to support x-bundle com
3705
+ return self.redirect( fallback.toUrl() );
3706
+ }
3707
+ }
3708
+
3709
+ // allowing this.throwError(err)
3710
+ if ( typeof(code) == 'object' && !msg && typeof(code.status) != 'undefined' && typeof(code.error) != 'undefined' ) {
3711
+ msg = code.error || code.message;
3712
+ code = code.status || 500;
3713
+ }
3714
+ if ( typeof(statusCodes[code]) != 'undefined' ) {
3715
+ standardErrorMessage = statusCodes[code];
3716
+ } else {
3717
+ console.warn('[ ApiValidator ] statusCode `'+ code +'` not matching any definition in `'+_( getPath('gina').core + '/status.codes')+'`\nPlease contact the Gina dev team to add one if required');
3718
+ }
3719
+
3720
+ // if ( !local.res.getHeaders()['content-type'] /**!req.headers['content-type'] */  ) {
3721
+ // // Internet Explorer override
3722
+ // if ( typeof(req.headers['user-agent']) != 'undefined' && /msie/i.test(req.headers['user-agent']) ) {
3723
+ // res.writeHead(code, "content-type", "text/plain")
3724
+ // } else {
3725
+ // res.writeHead(code, { 'content-type': bundleConf.server.coreConfiguration.mime['json']} );
3726
+ // }
3727
+ // }
3728
+
3729
+ // TODO - test with internet explorer then remove this if working
3730
+ if ( typeof(req.headers['user-agent']) != 'undefined' ) {
3731
+ if ( /msie/i.test(req.headers['user-agent']) ) {
3732
+ res.writeHead(code, "content-type", "text/plain");
3733
+ } else {
3734
+ var contentType = ( responseHeaders && responseHeaders['content-type'])
3735
+ ? responseHeaders['content-type']
3736
+ : bundleConf.server.coreConfiguration.mime['json']+ '; charset='+ bundleConf.encoding
3737
+ ;
3738
+ res.writeHead(code, { 'content-type': contentType } );
3739
+ }
3740
+ } else if ( typeof(responseHeaders['content-type']) != 'undefined' ) {
3741
+ res.writeHead(code, { 'content-type': responseHeaders['content-type']} )
3742
+ } else {
3743
+ res.writeHead(code, "content-type", bundleConf.server.coreConfiguration.mime['json']+ '; charset='+ bundleConf.encoding);
3744
+ }
3745
+
3746
+
3747
+
3748
+ if (!errorObject) {
3749
+ errorObject = {
3750
+ status: code,
3751
+ //errors: msg.error || msg.errors || msg,
3752
+ error: standardErrorMessage || msg.error || msg,
3753
+ message: msg.message || msg,
3754
+ stack: msg.stack
3755
+ }
3756
+ }
3757
+
3758
+ var errOutput = null, output = errorObject.toString();
3759
+ if ( output == '[object Object]' ) {
3760
+ errOutput = JSON.stringify(errorObject);
3761
+ } else {
3762
+ errOutput = JSON.stringify(
3763
+ {
3764
+ status : errorObject.status,
3765
+ error : output
3766
+ }
3767
+ );
3768
+ }
3769
+
3770
+ console.error('[ BUNDLE ][ '+ bundleConf.bundle +' ][ Controller ] '+ req.method +' ['+res.statusCode +'] '+ req.url +'\n'+ errOutput);
3771
+ return res.end(errOutput);
3772
+ } else {
3773
+
3774
+
3775
+ console.error(req.method +' ['+ errorObject.status +'] '+ req.url);
3776
+
3777
+
3778
+ // intercept none HTML mime types
3779
+ var url = unescape(local.req.url) /// avoid %20
3780
+ , ext = null
3781
+ , isHtmlContent = false
3782
+ , hasCustomErrorFile = false
3783
+ , eCode = code.toString().substr(0,1) + 'xx'
3784
+ ;
3785
+ var extArr = url.substr(url.lastIndexOf('.')).match(/(\.[A-Za-z0-9]+)/);
3786
+ if (extArr) {
3787
+ ext = extArr[0].substr(1);
3788
+ }
3789
+ if ( !ext || /^(html|htm)$/i.test(ext) ) {
3790
+ isHtmlContent = true;
3791
+ }
3792
+
3793
+ if (
3794
+ isHtmlContent
3795
+ && typeof(bundleConf.content.templates._common.errorFiles) != 'undefined'
3796
+ && typeof(bundleConf.content.templates._common.errorFiles[code]) != 'undefined'
3797
+ ||
3798
+ isHtmlContent
3799
+ && typeof(bundleConf.content.templates._common.errorFiles) != 'undefined'
3800
+ && typeof(bundleConf.content.templates._common.errorFiles[eCode]) != 'undefined'
3801
+ ) {
3802
+ hasCustomErrorFile = true;
3803
+ var eFilename = null
3804
+ , eData = null
3805
+ ;
3806
+ eData = {
3807
+ isRenderingCustomError : true,
3808
+ bundle : bundle,
3809
+ status : code || null,
3810
+ //message : errorObject.message || msg || null,
3811
+ pathname : url
3812
+ };
3813
+
3814
+ if ( errorObject ) {
3815
+ eData = merge(errorObject, eData);
3816
+ }
3817
+
3818
+ if ( typeof(msg) == 'object' ) {
3819
+ if ( typeof(msg.stack) != 'undefined' ) {
3820
+ eData.stack = msg.stack
3821
+ }
3822
+ if ( !eData.message && typeof(msg.message) != 'undefined' ) {
3823
+ eData.message = msg.message
3824
+ }
3825
+ }
3826
+ if (
3827
+ code
3828
+ // See: framework/${version}/core/status.code
3829
+ && typeof(bundleConf.server.coreConfiguration.statusCodes[code]) != 'undefined'
3830
+ ) {
3831
+ eData.title = bundleConf.server.coreConfiguration.statusCodes[code];
3832
+ }
3833
+ // TODO - Remove this if not used
3834
+ // if ( typeof(local.req.routing) != 'undefined' ) {
3835
+ // eData.routing = local.req.routing;
3836
+ // }
3837
+
3838
+ if (typeof(bundleConf.content.templates._common.errorFiles[code]) != 'undefined') {
3839
+ eFilename = bundleConf.content.templates._common.errorFiles[code];
3840
+ } else {
3841
+ eFilename = bundleConf.content.templates._common.errorFiles[eCode];
3842
+ }
3843
+
3844
+ if (!local.options.isRenderingCustomError) {
3845
+ var eRule = 'custom-error-page@'+ bundle;
3846
+ var routeObj = bundleConf.content.routing[eRule];
3847
+ routeObj.rule = eRule;
3848
+ //routeObj.url = unescape(local.req.url);/// avoid %20
3849
+ routeObj.param.title = ( typeof(eData.title) != 'undefined' ) ? eData.title : 'Error ' + eData.status;
3850
+ routeObj.param.file = eFilename;
3851
+ routeObj.param.error = eData;
3852
+ routeObj.param.displayToolbar = self.isCacheless();
3853
+ routeObj.param.isLocalOptionResetNeeded = true;
3854
+
3855
+
3856
+ local.req.routing = routeObj;
3857
+ local.req.params.errorObject = errorObject;
3858
+ self.renderCustomError(local.req, res, local.next);
3859
+ return;
3860
+ }
3861
+
3862
+ }
3863
+
3864
+ // if (!errorObject) {
3865
+ // errorObject = {
3866
+ // status: code,
3867
+ // //errors: msg.error || msg.errors || msg,
3868
+ // error: standardErrorMessage || msg.error || msg,
3869
+ // message: msg.message || msg,
3870
+ // stack: msg.stack
3871
+ // }
3872
+ // }
3873
+ var msgString = '<h1 class="status">Error '+ code +'.</h1>';
3874
+
3875
+ console.error('[ BUNDLE ][ '+ local.options.conf.bundle +' ][ Controller ] `this.'+ req.routing.param.control +'(...)` ['+res.statusCode +'] '+ req.url);
3876
+ if ( typeof(msg) == 'object' ) {
3877
+
3878
+ if (msg.title) {
3879
+ msgString += '<pre class="'+ eCode +' title">'+ msg.title +'</pre>';
3880
+ }
3881
+
3882
+ if (msg.error) {
3883
+ msgString += '<pre class="'+ eCode +' message">'+ msg.error +'</pre>';
3884
+ }
3885
+
3886
+ if (msg.message) {
3887
+ msgString += '<pre class="'+ eCode +' message">'+ msg.message +'</pre>';
3888
+ }
3889
+
3890
+ if (msg.stack) {
3891
+
3892
+ if (msg.error) {
3893
+ msg.stack = msg.stack.replace(msg.error, '')
3894
+ }
3895
+
3896
+ if (msg.message) {
3897
+ msg.stack = msg.stack.replace(msg.message, '')
3898
+ }
3899
+
3900
+ msg.stack = msg.stack.replace('Error:', '').replace(' ', '');
3901
+ msgString += '<pre class="'+ eCode +' stack">'+ msg.stack +'</pre>';
3902
+ }
3903
+
3904
+ } else {
3905
+ // Generic error
3906
+ var title = null, message = null, stack = null;;
3907
+ if ( typeof(errorObject) != 'undefined' && errorObject && typeof(errorObject.error) != 'undefined' ) {
3908
+ title = errorObject.error
3909
+ }
3910
+ if (typeof(errorObject) != 'undefined' && errorObject && typeof(errorObject.message) != 'undefined' ) {
3911
+ message = errorObject.message
3912
+ }
3913
+ if (typeof(errorObject) != 'undefined' && errorObject && typeof(errorObject.stack) != 'undefined' ) {
3914
+ stack = errorObject.stack
3915
+ }
3916
+
3917
+ if (title) {
3918
+ msgString += '<pre class="'+ eCode +' title">'+ title +'</pre>';
3919
+ }
3920
+ if (message) {
3921
+ msgString += '<pre class="'+ eCode +' message">'+ message +'</pre>';
3922
+ }
3923
+ if (stack) {
3924
+ msgString += '<pre class="'+ eCode +' stack">'+ stack +'</pre>';
3925
+ }
3926
+ }
3927
+ res.writeHead(code, { 'content-type': bundleConf.server.coreConfiguration.mime[ext]+'; charset='+ bundleConf.encoding } );
3928
+ // if ( isHtmlContent && hasCustomErrorFile ) {
3929
+ // res.end(msgString);
3930
+ // } else {
3931
+ //if ( isHtmlContent && !hasCustomErrorFile ) {
3932
+ res.end(msgString);
3933
+ //}
3934
+
3935
+ return;
3936
+ }
3937
+ } else {
3938
+ if (typeof(next) != 'undefined')
3939
+ return next();
3940
+ }
3941
+ res.end();
3942
+ return;
3943
+ }
3944
+
3945
+ // converting references to objects
3946
+ var refToObj = function (arr){
3947
+ var tmp = null,
3948
+ curObj = {},
3949
+ obj = {},
3950
+ count = 0,
3951
+ data = {},
3952
+ last = null;
3953
+ for (var r in arr) {
3954
+ tmp = r.split(".");
3955
+ //Creating structure - Adding sub levels
3956
+ for (var o in tmp) {
3957
+ count++;
3958
+ if (last && typeof(obj[last]) == "undefined") {
3959
+ curObj[last] = {};
3960
+ if (count >= tmp.length) {
3961
+ // assigning.
3962
+ // !!! if null or undefined, it will be ignored while extending.
3963
+ curObj[last][tmp[o]] = (arr[r]) ? arr[r] : "undefined";
3964
+ last = null;
3965
+ count = 0;
3966
+ break
3967
+ } else {
3968
+ curObj[last][tmp[o]] = {}
3969
+ }
3970
+ } else if (tmp.length === 1) { //Just one root var
3971
+ curObj[tmp[o]] = (arr[r]) ? arr[r] : "undefined";
3972
+ obj = curObj;
3973
+ break
3974
+ }
3975
+ obj = curObj;
3976
+ last = tmp[o]
3977
+ }
3978
+ //data = merge(data, obj, true);
3979
+ data = merge(obj, data);
3980
+ obj = {};
3981
+ curObj = {}
3982
+ }
3983
+ return data
3984
+ }
3985
+
3986
+ init()
3987
+ };
3988
+
3989
+ SuperController = inherits(SuperController, EventEmitter);
3990
+ module.exports = SuperController