@themost/web
Version:
MOST Web Framework 2.0 - Web Server Module
340 lines (312 loc) • 10.5 kB
JavaScript
/**
* @license
* MOST Web Framework 2.0 Codename Blueshift
* Copyright (c) 2017, THEMOST LP All rights reserved
*
* Use of this source code is governed by an BSD-3-Clause license that can be
* found in the LICENSE file at https://themost.io/license
*/
///
var HttpError = require('@themost/common/errors').HttpError;
var AngularServerModule = require('./../angular/module').AngularServerModule;
var _ = require('lodash');
var LangUtils = require('@themost/common/utils').LangUtils;
var HttpApplicationService = require('../types').HttpApplicationService;
var HashMap = require('hashmap');
/**
* @class
* @constructor
* @param {HttpApplication} app
* @augments HttpApplicationService
*/
function TemplateCacheStrategy(app) {
TemplateCacheStrategy.super_.bind(this)(app);
var map = new HashMap();
/**
* @param {*} key
* @returns *
*/
this.get = function(key) {
return map.get(key);
};
/**
* @param {*} key
* @param {*=} value
*/
this.put = function(key, value) {
map.set(key, value);
};
this.clear = function() {
map.clear();
}
}
LangUtils.inherits(TemplateCacheStrategy, HttpApplicationService);
/**
* @interface
* @constructor
*/
function PostExecuteResultArgs() {
/**
* @property
* @name PostExecuteResultArgs#context
* @type {HttpContext}
*/
/**
* @property
* @name PostExecuteResultArgs#target
* @type {*}
*/
}
/**
* @class
* @param {HttpContext|*} $context
* @param {*} $await
* @param {*} $q
* @returns {$http}
* @constructor
* @private
*/
function HttpInternalProvider($context, $await, $q) {
function $http(requestConfig) {
var config = {
method: 'get',
cookie: $context.request.headers.cookie
};
_.assign(config, requestConfig);
var deferred = $q.defer(), promise = deferred.promise;
$await(function(resolve, reject) {
promise.success = function(fn) {
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
resolve();
});
return promise;
};
promise.error = function(fn) {
promise.then(null, function(response) {
fn(response.data, response.status, response.headers, config);
reject(new HttpError(response.status));
});
return promise;
};
$context.getApplication().executeRequest(config).then(function(response) {
response.status = response.statusCode;
response.data = response.body;
deferred.resolve(response);
}).catch(function(err) {
if (err) {
deferred.reject({ data: err.message, status:500, headers:{} });
}
});
});
return promise;
}
['get', 'delete', 'head', 'jsonp'].forEach(function(name) {
$http[name] = function(url, config) {
return $http(_.assign(config || {}, {
method: name,
url: url
}));
};
});
['post', 'put'].forEach(function(name) {
$http[name] = function(url, data, config) {
return $http(_.assign(config || {}, {
method: name,
url: url,
data: data
}));
};
});
return $http;
}
/**
* @class
* @constructor
* @implements PostExecuteResultHandler
*/
function DirectiveEngine() {
//
}
/**
* @param {PostExecuteResultArgs|*} args
* @param callback
*/
DirectiveEngine.prototype.postExecuteResult = function(args, callback) {
try {
callback = callback || function() {};
//get context and view
const context = args.context, view = args.target;
//ensure context
if (typeof context === 'undefined' || context === null) {
return callback();
}
//ensure view
if (typeof view === 'undefined' || view === null) {
return callback();
}
//ensure view result data
if (typeof view.body !== 'string') {
return callback();
}
if (!args.context.getApplication().hasService(AngularServerModule)) {
args.context.getApplication().useService(AngularServerModule);
}
/**
* @type TemplateCacheStrategy
*/
let templateCache;
if (!context.getApplication().hasService(TemplateCacheStrategy)) {
context.getApplication().useService(TemplateCacheStrategy);
}
templateCache = context.getApplication().getService(TemplateCacheStrategy);
/**
* @type {AngularServerModule}
*/
const angularServer = context.getApplication().getService(AngularServerModule);
//process result
let document = angularServer.createDocument(view.body);
//create server module
const angular = document.parentWindow.angular;
const app = angularServer.doBootstrap(angular);
/**
* @type {Array}
*/
const promises = [];
app.config(function($sceDelegateProvider) {
/**
* @method resourceUrlWhitelist
* @param {Array} whiteList
* @methodOf $sceDelegateProvider
*/
$sceDelegateProvider.resourceUrlWhitelist([
'/templates/server/*.html'
]);
});
app.service('$context', function() {
return context;
}).service('$templateCache', function() {
return {
get: function(templatePath) {
return templateCache.get(templatePath)
},
put: function(templatePath, templateContent) {
templateCache.put(templatePath, templateContent);
}
}
}).service('$serverState', function() {
return {
templatePath:view.templatePath
};
})
.service('$await', function($q, $window, $timeout, $rootScope) {
var asyncQueueLength = 0;
/**
* @param {Function} fn
*/
return function $await(fn) {
$q(function(resolve, reject) {
try {
asyncQueueLength += 1;
fn.call(document.parentWindow,function() {
asyncQueueLength -= 1;
$timeout(function() {
if (asyncQueueLength === 0) {
$rootScope.$emit('$asyncContentLoaded');
}
});
return resolve();
}, function(err) {
asyncQueueLength -= 1;
$timeout(function() {
if (asyncQueueLength === 0) {
$rootScope.$emit('$asyncContentLoaded', err);
}
});
return reject(err);
});
}
catch(err) {
asyncQueueLength -= 1;
$timeout(function() {
if (asyncQueueLength === 0) {
$rootScope.$emit('$asyncContentLoaded', err);
}
});
return reject(err);
}
});
};
});
app.service('$http', HttpInternalProvider);
//copy application services
_.forEach(_.keys(angularServer.services), function(name) {
app.service(name, angularServer.services[name]);
});
//copy application directives
_.forEach(_.keys(angularServer.directives), function(name) {
app.directive(name, angularServer.directives[name]);
});
//copy application filters
_.forEach(_.keys(angularServer.filters), function(name) {
app.filter(name, angularServer.filters[name]);
});
//copy application controllers
_.forEach(_.keys(angularServer.controllers), function(name) {
app.controller(name, angularServer.controllers[name]);
});
//get application element
var appElement = angular.element(document).find('*[server-app=\'server\']').get(0);
if (typeof appElement === 'undefined') {
appElement = angular.element(document).find('body').get(0);
}
if (appElement) {
//get $q
var $q = angular.injector(['ng']).get('$q');
//get $rootScope
var $rootScope = angular.injector(['ng']).get('$rootScope');
//set $rootScope
app.run(function($rootScope, $await) {
if (_.isObject(view.data)) {
_.assign($rootScope, view.data);
}
//wait for content
$rootScope.$on('$asyncContentLoaded', function(event, args)
{
if (args) {
//throw error
return callback(args);
}
//set view boyd
view.body = document.innerHTML;
//return
return callback();
});
//dummy async method (this is very important for views with asynchronous calls)
$await(function(resolve) {
return resolve();
});
});
//initialize app element
angular.bootstrap(appElement, ['server']);
}
else {
return callback();
}
}
catch (e) {
callback(e);
}
};
/**
* Creates a new instance of AuthHandler class
* @returns {DirectiveEngine}
*/
DirectiveEngine.createInstance = function() {
return new DirectiveEngine();
};
if (typeof exports !== 'undefined') {
module.exports.createInstance = DirectiveEngine.createInstance;
module.exports.DirectiveEngine = DirectiveEngine;
module.exports.PostExecuteResultArgs = PostExecuteResultArgs;
}