Remove old module loader

Remove old (now-unusable) module loader and components and corresponding unit
tests. Give the component loader and the component registry more meaningful names.

refs #4456
This commit is contained in:
Matthias Jentsch 2013-08-20 14:16:21 +02:00 committed by Marius Hein
parent 52c66893ab
commit 5e663846a5
11 changed files with 16 additions and 694 deletions

View File

@ -4,13 +4,13 @@
*/
/**
* A module to load, manage and query frontend components
* A module to load and manage frontend components
*
*/
define(['jquery', 'logging', 'icinga/registry'], function ($, log, registry) {
define(['jquery', 'logging', 'icinga/componentRegistry'], function ($, log, registry) {
"use strict";
var Manager = function() {
var ComponentLoader = function() {
/**
* Load the component with the given type and attach it to the target
@ -50,7 +50,7 @@ define(['jquery', 'logging', 'icinga/registry'], function ($, log, registry) {
/**
* Load all new components and remove components that were removed from
* the DOM
* the DOM from the internal registry
*
* @param {function} fin Called when the loading is completed
*/
@ -138,5 +138,5 @@ define(['jquery', 'logging', 'icinga/registry'], function ($, log, registry) {
return registry.getComponents();
};
};
return new Manager();
return new ComponentLoader();
});

View File

@ -11,7 +11,7 @@
define(['jquery'], function($) {
"use strict";
var Registry = function() {
var ComponentRegistry = function() {
var self = this;
/**
@ -133,5 +133,5 @@ define(['jquery'], function($) {
});
};
};
return new Registry();
return new ComponentRegistry();
});

View File

@ -1,118 +0,0 @@
/*global Icinga:false, document: false, define:false require:false base_url:false console:false */
/**
* ActionTable behaviour as described in
* https://wiki.icinga.org/display/cranberry/Frontend+Components#FrontendComponents-ActionTable
*
* @TODO: Row selection
*/
define(['jquery','logging','icinga/util/async'],function($,log,async) {
"use strict";
var ActionTableBehaviour = function() {
var onTableHeaderClick;
var TABLE_BASE_MATCHER = '.icinga-container table.action';
var linksInActionTable = TABLE_BASE_MATCHER+" tbody tr > a";
var actionTableRow = TABLE_BASE_MATCHER+" tbody tr";
var headerRow = TABLE_BASE_MATCHER+" > th a";
var searchField = ".icinga-container .actiontable.controls input[type=search]";
onTableHeaderClick = function (ev) {
var target = ev.currentTarget,
href = $(target).attr('href'),
destination;
if ($(target).parents('.layout-main-detail').length) {
if ($(target).parents("#icinga-main").length) {
destination = 'icinga-main';
}
else {
destination = 'icinga-detail';
}
} else {
destination = 'icinga-main';
}
async.loadToTarget(destination, href);
ev.preventDefault();
ev.stopImmediatePropagation();
return false;
};
var onLinkTagClick = function(ev) {
var target = ev.currentTarget,
href = $(target).attr('href'),
destination;
if ($(target).parents('.layout-main-detail').length) {
destination = 'icinga-detail';
} else {
destination = 'icinga-main';
}
async.loadToTarget(destination,href);
ev.preventDefault();
ev.stopImmediatePropagation();
return false;
};
var onTableRowClick = function(ev) {
ev.stopImmediatePropagation();
var target = $(ev.currentTarget),
href = target.attr('href'),
destination;
$('tr.active',target.parent('tbody').first()).removeClass("active");
target.addClass('active');
// When the tr has a href, act like it is a link
if(href) {
ev.currentTarget = target.first();
return onLinkTagClick(ev);
}
// Try to find a designated row action
var links = $("a.row-action",target);
if(links.length) {
ev.currentTarget = links.first();
return onLinkTagClick(ev);
}
// otherwise use the first anchor tag
links = $("a",target);
if(links.length) {
ev.currentTarget = links.first();
return onLinkTagClick(ev);
}
log.debug("No target for this table row found");
return false;
};
var onSearchInput = function(ev) {
ev.stopImmediatePropagation();
var txt = $(this).val();
};
this.eventHandler = {};
this.eventHandler[linksInActionTable] = {
'click' : onLinkTagClick
};
this.eventHandler[actionTableRow] = {
'click' : onTableRowClick
};
this.eventHandler[headerRow] = {
'click' : onTableHeaderClick
};
this.eventHandler[searchField] = {
'keyup' : onSearchInput
};
this.enable = function() {
};
};
return new ActionTableBehaviour();
});

View File

@ -1,113 +0,0 @@
/*global Icinga:false, document: false, define:false require:false base_url:false console:false */
(function() {
"use strict";
var containerMgrInstance = null;
var async;
var ContainerMgr = function($,log,Widgets,SubTable) {
var enhanceDetachLinks = function() {
$('a[target=_blank]',this).each(function() {
$(this).attr("target","popup");
});
};
/**
* Loading Async directly via AMD would result in a circular dependency and return null
* @param asyncMgr
*/
this.registerAsyncMgr = function(asyncMgr) {
async = asyncMgr;
};
this.updateContainer = function(id,content,req) {
var target = id;
if (typeof id === "string") {
target = this.getContainer(id);
}
var ctrl = $('.container-controls',target);
target.html(content);
if(ctrl.length) {
this.updateControlTargets(ctrl,req);
target.append(ctrl.first());
}
target.focus();
this.initializeContainers(target);
};
this.updateControlTargets = function(ctrl, req) {
$('a',ctrl).each(function() {
$(this).attr("href",req.url);
});
};
this.initControlBehaviour = function(root) {
$('div[container-id] .container-controls',root).each(function() {
enhanceDetachLinks.apply(this);
});
};
this.initExpandables = function(root) {
$('div[container-id] .expandable',root).each(function() {
var ctr = this;
$('.expand-link',this).on("click",function() {
$(ctr).removeClass('collapsed');
});
$('.collapse-link',this).on("click",function() {
$(ctr).addClass('collapsed');
});
});
};
this.drawImplicitWidgets = function(root) {
$('.icinga-widget[type="icinga/subTable"]',root).each(function() {
new SubTable(this);
});
$('div[container-id] .inlinepie',root).each(function() {
new Widgets.inlinePie(this,32,32);
});
};
this.loadAsyncContainers = function(root) {
$('.icinga-container[icingaurl]',root).each(function() {
var el = $(this);
var url = el.attr('icingaurl');
el.attr('loaded',true);
async.loadToTarget(el,url);
});
log.debug("Loading async");
};
this.initializeContainers = function(root) {
this.initControlBehaviour(root);
this.initExpandables(root);
this.drawImplicitWidgets(root);
this.loadAsyncContainers(root);
};
this.createPopupContainer = function(content,req) {
var closeButton = $('<button type="button" class="close" data-dismiss="modal" >&times;</button>');
var container = $('<div>').addClass('modal').attr('container-id','popup-'+req.url).attr("role","dialog")
.append($("<div>").addClass('modal-header').text('Header').append(closeButton))
.append($("<div>").addClass('modal-body').html(content)).appendTo(document.body);
closeButton.on("click",function() {container.remove();});
};
this.getContainer = function(id) {
if(id == 'body') {
return $(document.body);
}
return $('div[container-id='+id+']');
};
};
define(['jquery','logging','icinga/widgets/checkIcons','icinga/widgets/subTable'], function($,log,widgets,subTable) {
if (containerMgrInstance === null) {
containerMgrInstance = new ContainerMgr($,log,widgets,subTable);
}
return containerMgrInstance;
});
})();

View File

@ -2,88 +2,25 @@
define([
'jquery',
'logging',
'icinga/module',
'icinga/util/async',
'icinga/container',
'modules/list',
'icinga/component'
], function ($, log, moduleMgr, async, containerMgr, modules,components) {
'icinga/componentLoader'
], function ($, log, async,components) {
'use strict';
$(document).ready(function(){
components.load();
});
/**
* Icinga prototype
*/
var Icinga = function() {
var internalModules = ['icinga/components/actionTable',
'icinga/components/mainDetail',
'icinga/components/datetime'];
this.modules = {};
var failedModules = [];
var initialize = function () {
registerLazyModuleLoading();
enableInternalModules();
containerMgr.registerAsyncMgr(async);
containerMgr.initializeContainers(document);
components.load();
log.debug("Initialization finished");
enableModules();
};
var registerLazyModuleLoading = function() {
async.registerHeaderListener("X-Icinga-Enable-Module", loadModuleScript, this);
};
var enableInternalModules = function() {
$.each(internalModules,function(idx,module) {
moduleMgr.enableModule(module, log.error);
});
};
var loadModuleScript = function(name) {
moduleMgr.enableModule("modules/"+name+"/"+name, function(error) {
failedModules.push({
name: name,
errorMessage: error
});
});
};
var enableModules = function(moduleList) {
moduleList = moduleList || modules;
$.each(modules,function(idx,module) {
loadModuleScript(module.name);
});
};
$(document).ready(initialize.bind(this));
return {
loadModule: function(blubb,bla) {
behaviour.registerBehaviour(blubb,bla);
},
loadIntoContainer: function(ctr) {
},
loadUrl: function(url, target, params) {
target = target || "icinga-main";
async.loadToTarget(target, url, params);
},
getFailedModules: function() {
return failedModules;
}
components: components
};
};
return new Icinga();

View File

@ -1,100 +0,0 @@
/*global Icinga:false, $: false, document: false, define:false requirejs:false base_url:false console:false */
/**
This prototype encapsulates the modules registered in the module folder
**/
(function() {
"use strict";
var loaded = {};
define(['logging'],function(log) {
var registerModuleFunctions = function(module) {
var enableFn = module.enable, disableFn = module.disable;
module.enable = (function(root) {
root = root || $('body');
for (var jqMatcher in this.eventHandler) {
for (var ev in this.eventHandler[jqMatcher]) {
log.debug("Registered module: ", "'"+ev+"'", jqMatcher);
$(root).on(ev,jqMatcher,this.eventHandler[jqMatcher][ev]);
}
}
if(enableFn) {
enableFn.apply(this,arguments);
}
}).bind(module);
module.disable = (function(root) {
root = root || $('body');
for (var jqMatcher in this.eventHandler) {
for (var ev in this.eventHandler[jqMatcher]) {
log.debug("Unregistered module: ", "'"+ev+"'", jqMatcher);
$(root).off(ev,jqMatcher,this.eventHandler[jqMatcher][ev]);
}
}
if (disableFn) {
disableFn.apply(this,arguments);
}
}).bind(module);
};
var CallInterface = function() {
/**
* Loads a module and calls successCallback with the module as the parameter on success, otherwise
* the errorCallback with the errorstring as the first parameter
*
* @param name
* @param errorCallback
* @param successCallback
*/
this.enableModule = function(name,errorCallback,successCallback) {
requirejs([name],function(module) {
if (typeof module === "undefined") {
return errorCallback(new Error("Unknown module: "+name));
}
if (typeof module.eventHandler === "object") {
registerModuleFunctions(module);
}
if (typeof module.enable === "function") {
module.enable();
}
loaded[name] = {
module: module,
active: true
};
if (typeof successCallback === "function") {
successCallback(module);
}
},function(err) {
errorCallback("Could not load module "+name+" "+err,err);
});
};
this.disableModule = function(name) {
if(loaded[name] && loaded[name].active) {
loaded[name].module.disable();
}
};
/**
* This should *ONLY* be called in testcases
**/
this.resetHard = function() {
if (typeof describe !== "function") {
return;
}
loaded = {};
};
};
return new CallInterface();
});
})();

View File

@ -3,7 +3,7 @@
"use strict";
var asyncMgrInstance = null;
define(['icinga/container','logging','jquery'],function(containerMgr,log,$) {
define(['logging','jquery'],function(log,$,containerMgr) {
var headerListeners = {};

View File

@ -31,5 +31,4 @@ describe('The async module', function() {
},this);
var test = async.createRequest();
});
});

View File

@ -19,11 +19,11 @@ var setUp = function(registry)
{
rjsmock.purgeDependencies();
requireNew('icinga/registry.js');
requireNew('icinga/componentRegistry.js');
registry = registry || rjsmock.getDefine();
rjsmock.registerDependencies({
'icinga/registry': registry,
'icinga/componentRegistry': registry,
/*
* Available components
@ -50,7 +50,7 @@ var setUp = function(registry)
$('body').empty();
requireNew('icinga/component.js');
requireNew('icinga/componentLoader.js');
component = rjsmock.getDefine();
};

View File

@ -1,283 +0,0 @@
/**
* Test cases for the module loading implementation
*
*
**/
// {{LICENSE_HEADER}}
// {{LICENSE_HEADER}}
var should = require("should");
var rjsmock = require("requiremock.js");
var asyncMock = require("asyncmock.js");
requireNew("icinga/module.js");
var module = rjsmock.getDefine();
GLOBAL.document = $('body');
/**
* Test module that only uses eventhandlers and
* no custom logic
**/
var eventOnlyModule = function() {
var thiz = this;
this.moduleLinkClick = false;
this.formFocus = false;
var onModuleLinkClick = function() {
thiz.moduleLinkClick = true;
};
var onFormFocus = function() {
thiz.formFocus = true;
};
this.eventHandler = {
'.myModule a' : {
'click': onModuleLinkClick
},
'.myModule div.test input' : {
'focus' : onFormFocus
}
};
};
/**
* Module that defines an own enable and disable function
* that is called additionally to the eventhandler setup
*
**/
var customLogicModule = function() {
var thiz = this;
this.clicked = false;
this.customEnable = false;
this.customDisable = false;
var onClick = function() {
thiz.clicked = true;
};
this.enable = function() {
thiz.customEnable = true;
};
this.disable = function() {
thiz.customDisable = true;
};
this.eventHandler = {
'.myModule a' : {
'click' : onClick
}
};
};
var setupTestDOM = function() {
tearDownTestDOM();
$('body').append($('<div class="myModule" />')
.append($('<a href="test">funkey</a>'))
.append($('<div class="test" />')
.append('<input type="button" />')));
};
var tearDownTestDOM = function() {
$('body').off();
$('body').empty();
};
describe('Module loader', function() {
var err = null;
var errorCallback = function(error) {
err = error;
};
it('Should call the errorCallback when module isn\'t found', function() {
err = null;
rjsmock.purgeDependencies();
module.resetHard();
module.enableModule("testModule", errorCallback);
should.exist(err);
});
it('Should register model event handlers when an \'eventHandler\' attribute exists', function() {
rjsmock.purgeDependencies();
var testModule = new eventOnlyModule();
rjsmock.registerDependencies({
testModule: testModule
});
err = null;
var toTest = null;
// Test event handler
setupTestDOM();
// Enable the module and asser it is recognized and enabled
module.enableModule("testModule", errorCallback, function(enabled) {
toTest = enabled;
});
should.not.exist(err, "An error occured during loading: "+err);
should.exist(toTest, "The injected test module didn't work!");
should.exist(toTest.enable, "Implicit enable method wasn't created");
$('.myModule a').click();
should.equal(toTest.moduleLinkClick, true, "Click on link should trigger handler");
$('.myModule div.test input').focus();
should.equal(toTest.formFocus, true, "Form focus should trigger handler");
tearDownTestDOM();
});
it('Should be able to deregister events handlers when disable() is called', function() {
rjsmock.purgeDependencies();
var testModule = new eventOnlyModule();
rjsmock.registerDependencies({
testModule: testModule
});
err = null;
var toTest = null;
setupTestDOM();
module.enableModule("testModule", errorCallback, function(enabled) {
toTest = enabled;
});
should.not.exist(err, "An error occured during loading: "+err);
should.exist(toTest, "The injected test module didn't work!");
should.exist(toTest.enable, "Implicit enable method wasn't created");
$('.myModule a').click();
should.equal(toTest.moduleLinkClick, true, "Click on link should trigger handler");
toTest.moduleLinkClick = false;
module.disableModule("testModule");
$('.myModule a').click();
should.equal(toTest.moduleLinkClick, false, "Click on link shouldn't trigger handler when module is disabled");
tearDownTestDOM();
$('body').unbind();
});
it('Should additionally call custom enable and disable functions', function() {
rjsmock.purgeDependencies();
var testModule = new customLogicModule();
rjsmock.registerDependencies({
testModule: testModule
});
err = null;
var toTest = null;
// Test event handler
setupTestDOM();
module.enableModule("testModule", errorCallback, function(enabled) {
toTest = enabled;
});
should.not.exist(err, "An error occured during loading: "+err);
should.exist(toTest, "The injected test module didn't work!");
should.exist(toTest.enable, "Implicit enable method wasn't created");
should.equal(toTest.customEnable, true, "Custom enable method wasn't called");
$('.myModule a').click();
should.equal(toTest.clicked, true, "Click on link should trigger handler");
toTest.clicked = false;
module.disableModule("testModule");
should.equal(toTest.customDisable, true, "Custom disable method wasn't called");
$('.myModule a').click();
should.equal(toTest.clicked, false, "Click on link shouldn't trigger handler when module is disabled");
tearDownTestDOM();
$('body').unbind();
});
});
describe('The icinga module bootstrap', function() {
it("Should automatically load all enabled modules", function() {
rjsmock.purgeDependencies();
var testClick = false;
rjsmock.registerDependencies({
"icinga/module": module,
"icinga/util/async" : {
registerHeaderListener: function() {}
},
"modules/test/test" : {
eventHandler: {
"a.test" : {
click : function() {
testClick = true;
}
}
}
},
"icinga/container" : {
registerAsyncMgr: function() {},
initializeContainers: function() {}
},
"modules/list" : [
{ name: 'test' },
{ name: 'test2'} // this one fails
]
});
tearDownTestDOM();
requireNew("icinga/icinga.js");
var icinga = rjsmock.getDefine();
$('body').append($("<a class='test'></a>"));
$('a.test').click();
should.equal(testClick, true, "Module wasn't automatically loaded!");
icinga.getFailedModules().should.have.length(1);
should.equal(icinga.getFailedModules()[0].name, "test2");
tearDownTestDOM();
});
it("Should load modules lazily when discovering a X-Icinga-Enable-Module header", function() {
rjsmock.purgeDependencies();
requireNew("icinga/util/async.js");
var async = rjsmock.getDefine();
rjsmock.registerDependencies({
"icinga/module": module,
"icinga/util/async": async,
"modules/test/test" : {
eventHandler: {
"a.test" : {
click : function() {
testClick = true;
}
}
}
},
"icinga/container" : {
registerAsyncMgr: function() {},
initializeContainers: function() {}
},
"modules/list" : [
]
});
tearDownTestDOM();
requireNew("icinga/icinga.js");
var icinga = rjsmock.getDefine();
var testClick = false;
// The module shouldn't be loaded
$('body').append($("<a class='test'></a>"));
$('a.test').click();
should.equal(testClick, false, "Unregistered module was loaded");
asyncMock.setNextAsyncResult(async,"result", false, {
"X-Icinga-Enable-Module" : "test"
});
async.createRequest();
// The module shouldn't be loaded
$('body').append($("<a class='test'></a>"));
$('a.test').click();
should.equal(testClick, true, "Module wasn't automatically loaded on header!");
tearDownTestDOM();
});
});

View File

@ -10,7 +10,7 @@ GLOBAL.document = $('body');
var registry;
var setUp = function() {
requireNew('icinga/registry.js');
requireNew('icinga/componentRegistry.js');
registry = rjsmock.getDefine();
};