UNPKG

jii

Version:

Jii - Full-Stack JavaScript Framework

237 lines (202 loc) 8.53 kB
/** * @author <a href="http://www.affka.ru">Vladimir Kozhin</a> * @license MIT */ 'use strict'; const Jii = require('../../BaseJii'); const File = require('../../helpers/File'); const Controller = require('../../base/Controller'); const InvalidParamException = require('../../exceptions/InvalidParamException'); const ApplicationException = require('../../exceptions/ApplicationException'); const InvalidCallException = require('../../exceptions/InvalidCallException'); const _trimStart = require('lodash/trimStart'); const _isObject = require('lodash/isObject'); const _isFunction = require('lodash/isFunction'); const _template = require('lodash/template'); const IRenderer = require('../IRenderer'); class UnderscoreRenderer extends IRenderer { preInit() { this._currentWebView = null; /** * @type {[]} the view files currently being rendered. There may be multiple view files being * rendered at a moment because one view may be rendered within another. */ this._viewFiles = []; /** * @type {object} */ this._templates = {}; /** * @type {string} the default view file extension. This will be appended to view file names if they don't have file extensions. */ this.defaultExtension = ''; super.preInit(...arguments); } /** * * @param {*} view * @param {Context} context * @param {object} params * @param {Controller} controller * @param {WebView} webView * @returns {*} */ render(view, context, params, controller, webView) { var viewFile = this._findViewFile(view, controller); return this.renderFile(viewFile, context, params, controller, webView); } /** * * @param {*} view * @param {Context} context * @param {object} params * @param {Controller} controller * @param {WebView} webView * @returns {*} */ renderLayout(view, context, params, controller, webView) { return this.renderFile(view, context, params, controller, webView); } /** * * @param viewFile * @param context * @param params * @param controller * @param webView * @returns {*} */ renderFile(viewFile, context, params, controller, webView) { params = params || {}; webView = webView || this._currentWebView; viewFile = Jii.getAlias(viewFile); if (this.hasTemplate(viewFile)) { viewFile = this._localize(viewFile); } else { throw new InvalidParamException('The view file does not exist: ' + viewFile); } //Jii.trace('Rendering view file: ' + viewFile); // Append content to params params.context = context; this._viewFiles.push(viewFile); this._currentWebView = webView; var output = this.renderJsFile(viewFile, params, webView); this._viewFiles.pop(); this._currentWebView = null; return output; } renderJsFile(file, params, webView) { params = params || {}; if (!this._templates[file]) { throw new ApplicationException('Not found template in path `' + file + '`.'); } params.Jii = Jii; return this._templates[file].call(webView, params); } getTemplate(path) { if (!require('fs').existsSync(path)) { return null; } this._templates[path] = _template(require('fs').readFileSync(path).toString()); return this._templates[path] || null; } hasTemplate(path) { return this.getTemplate(path) !== null; } /** * @returns {string|boolean} the view file currently being rendered. False if no view file is being rendered. */ getViewFile() { return this._viewFiles[this._viewFiles.length - 1]; } /** * Finds the view file based on the given view name. * @param {string} view the view name or the path alias of the view file. Please refer to [[render()]] * on how to specify this parameter. * @param {object} context the context to be assigned to the view and can later be accessed via [[context]] * in the view. If the context implements [[ViewContextInterface]], it may also be used to locate * the view file corresponding to a relative view name. * @returns {string} the view file path. Note that the file may not exist. * @throws InvalidCallException if a relative view name is given while there is no active context to * determine the corresponding view file. */ _findViewFile(view, context) { context = context || null; var file = null; if (view.substr(0, 1) === '@') { // e.g. "@app/views/main" file = Jii.getAlias(view); } else if (view.substr(0, 2) === '//') { // e.g. "//layouts/main" file = Jii.app.getViewPath() + '/' + _ltrim(view, '/'); } else if (view.substr(0, 1) === '/') { // e.g. "/site/index" if (context instanceof Controller) { file = context.module.getViewPath() + '/' + _trimStart(view, '/'); } } else if (_isObject(context) && _isFunction(context.getViewPath)) { file = context.getViewPath() + '/' + view; } else { var currentViewFile = this.getViewFile(); if (currentViewFile !== false) { file = File.getFileDirectory(currentViewFile) + '/' + view; } } if (file === null) { throw new InvalidCallException('Unable to resolve view file for view ' + view + ': no active view context.'); } if (File.getFileExtension(file) !== '') { return file; } var path = file + '.' + this.defaultExtension; if (this.defaultExtension !== 'ejs' && !File.getFileExtension(path)) { path = file + '.ejs'; } return path; } /** * Returns the localized version of a specified file. * * The searching is based on the specified language code. In particular, * a file with the same name will be looked for under the subdirectory * whose name is the same as the language code. For example, given the file "path/to/view.php" * and language code "zh-CN", the localized file will be looked for as * "path/to/zh-CN/view.php". If the file is not found, it will try a fallback with just a language code that is * "zh" i.e. "path/to/zh/view.php". If it is not found as well the original file will be returned. * * If the target and the source language codes are the same, * the original file will be returned. * * @param {string} file the original file * @param {string} [language] the target language that the file should be localized to. * If not set, the value of [[\jii\base\Application.language]] will be used. * @param {string} [sourceLanguage] the language that the original file is in. * If not set, the value of [[\jii\base\Application.sourceLanguage]] will be used. * @returns {string} the matching localized file, or the original file if the localized version is not found. * If the target and the source language codes are the same, the original file will be returned. */ _localize(file, language, sourceLanguage) { language = language || null; sourceLanguage = sourceLanguage || null; if (language === null) { language = Jii.app.language; } if (sourceLanguage === null) { sourceLanguage = Jii.app.sourceLanguage; } if (language === sourceLanguage) { return file; } var desiredFile = File.getFileDirectory(file) + '/' + language + '/' + File.getFileName(file); if (this.hasTemplate(desiredFile)) { return desiredFile; } language = language.substr(0, 2); if (language === sourceLanguage) { return file; } desiredFile = File.getFileDirectory(file) + '/' + language + '/' + File.getFileName(file); return this.hasTemplate(desiredFile) ? desiredFile : file; } } module.exports = UnderscoreRenderer;