Add javascript autoloading

the js/modules/%modulename%/%file% is now mapped to the module
path (if existing). To prevent name clashing, the modules folder
has been renamed to components.
This commit is contained in:
Jannis Moßhammer 2013-06-21 11:36:12 +02:00 committed by Marius Hein
parent 3a5a4bf273
commit d94d89e1cf
6 changed files with 239 additions and 12 deletions

View File

@ -9,6 +9,7 @@ use Icinga\Web\ActionController;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Zend_Controller_Action_Exception as ActionException; use Zend_Controller_Action_Exception as ActionException;
use Icinga\Application\Benchmark;
/** /**
* Class StaticController * Class StaticController
* @package Icinga\Web\Form * @package Icinga\Web\Form
@ -69,7 +70,7 @@ class StaticController extends ActionController
*/ */
public function javascriptAction() public function javascriptAction()
{ {
$module = $this->_getParam('moduleName'); $module = $this->_getParam('module_name');
$file = $this->_getParam('file'); $file = $this->_getParam('file');
$basedir = Icinga::app()->getModule($module)->getBaseDir(); $basedir = Icinga::app()->getModule($module)->getBaseDir();
@ -96,15 +97,16 @@ class StaticController extends ActionController
) . ' GMT' ) . ' GMT'
); );
$hash = md5_file($filePath);
if ($hash === $this->getRequest()->getHeader('If-None-Match')) { if ($hash === $this->getRequest()->getHeader('If-None-Match')) {
$response->setHttpResponseCode(304); $response->setHttpResponseCode(304);
return; return;
} else { } else {
readfile($filePath); readfile($filePath);
} }
// TODO: get rid of exit: return;
exit;
} }
} }
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd

View File

@ -59,6 +59,17 @@ class Web extends ApplicationBootstrap
) )
) )
); );
$this->frontController->getRouter()->addRoute(
'module_javascript',
new Route(
'js/modules/:module_name/:file',
array(
'controller' => 'static',
'action' => 'javascript'
)
)
);
return $this; return $this;
} }
@ -162,6 +173,7 @@ class Web extends ApplicationBootstrap
*/ */
protected function configurePagination() protected function configurePagination()
{ {
Paginator::addScrollingStylePrefixPath( Paginator::addScrollingStylePrefixPath(
'Icinga_Web_Paginator_ScrollingStyle', 'Icinga_Web_Paginator_ScrollingStyle',
'Icinga/Web/Paginator/ScrollingStyle' 'Icinga/Web/Paginator/ScrollingStyle'

View File

@ -0,0 +1,118 @@
/*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

@ -0,0 +1,99 @@
/*global Icinga:false, document: false, define:false require:false base_url:false console:false */
/**
* Main-Detail layout behaviour as described in
* https://wiki.icinga.org/display/cranberry/Frontend+Components#FrontendComponents-Behaviour
*
*/
define(['jquery','logging','icinga/util/async'],function($,log,async) {
"use strict";
var MainDetailBehaviour = function() {
var onOuterLinkClick = function(ev) {
var a = $(ev.currentTarget),
target = a.attr("target"),
href = a.attr("href");
ev.stopImmediatePropagation();
collapseDetailView();
async.loadToTarget("icinga-main",href);
return false;
};
var onLinkTagClick = function(ev) {
var a = $(ev.currentTarget),
target = a.attr("target"),
href = a.attr("href");
// check for protocol://
if(/^[A-Z]{2,10}\:\/\//i.test(href)) {
window.open(href);
ev.stopImmediatePropagation();
return false;
}
// check for link in table header
if(a.parents('th').length > 0) {
ev.stopImmediatePropagation();
return false;
}
if(typeof target === "undefined") {
if(a.parents("#icinga-detail").length) {
log.debug("Parent is detail, loading into detail");
async.loadToTarget("icinga-detail",href);
} else {
log.debug("Parent is not detail, loading into main");
async.loadToTarget("icinga-main",href);
}
} else {
switch(target) {
case "body":
async.loadToTarget("body", href);
break;
case "main":
async.loadToTarget("icinga-main",href);
collapseDetailView();
break;
case "detail":
log.debug("Target: detail");
async.loadToTarget("icinga-detail",href);
break;
case "popup":
log.debug("No target");
async.loadToTarget(null,href);
break;
default:
return true;
}
}
ev.stopImmediatePropagation();
return false;
};
var expandDetailView = function() {
$("#icinga-detail").parents(".collapsed").removeClass('collapsed');
};
var collapseDetailView = function(elementInDetailView) {
$("#icinga-detail").parents(".layout-main-detail").addClass('collapsed');
};
this.expandDetailView = expandDetailView;
this.collapseDetailView = collapseDetailView;
this.eventHandler = {
'.layout-main-detail * a' : {
'click' : onLinkTagClick
},
'a' : {
'click' : onOuterLinkClick
},
'.layout-main-detail .icinga-container#icinga-detail' : {
'focus' : expandDetailView
}
};
};
return new MainDetailBehaviour();
});

View File

@ -5,7 +5,7 @@
var containerMgrInstance = null; var containerMgrInstance = null;
var async; var async;
var ContainerMgr = function($,log,Widgets,SubTable,holder) { var ContainerMgr = function($,log,Widgets,SubTable) {
var enhanceDetachLinks = function() { var enhanceDetachLinks = function() {
@ -34,14 +34,12 @@
} }
target.focus(); target.focus();
this.initializeContainers(target); this.initializeContainers(target);
}; };
this.updateControlTargets = function(ctrl, req) { this.updateControlTargets = function(ctrl, req) {
$('a',ctrl).each(function() { $('a',ctrl).each(function() {
$(this).attr("href",req.url); $(this).attr("href",req.url);
}); });
}; };
this.initControlBehaviour = function(root) { this.initControlBehaviour = function(root) {
@ -86,7 +84,6 @@
this.initExpandables(root); this.initExpandables(root);
this.drawImplicitWidgets(root); this.drawImplicitWidgets(root);
this.loadAsyncContainers(root); this.loadAsyncContainers(root);
}; };
this.createPopupContainer = function(content,req) { this.createPopupContainer = function(content,req) {
@ -96,7 +93,6 @@
.append($("<div>").addClass('modal-body').html(content)).appendTo(document.body); .append($("<div>").addClass('modal-body').html(content)).appendTo(document.body);
closeButton.on("click",function() {container.remove();}); closeButton.on("click",function() {container.remove();});
}; };
this.getContainer = function(id) { this.getContainer = function(id) {
@ -107,7 +103,7 @@
}; };
}; };
define(['jquery','logging','icinga/widgets/checkIcons','icinga/widgets/subTable'], function($,log,widgets,subTable) { define(['jquery','logging','icinga/widgets/checkIcons','icinga/widgets/subTable'],function($,log,widgets,subTable) {
if (containerMgrInstance === null) { if (containerMgrInstance === null) {
containerMgrInstance = new ContainerMgr($,log,widgets,subTable); containerMgrInstance = new ContainerMgr($,log,widgets,subTable);
} }

View File

@ -13,7 +13,7 @@ define([
* Icinga prototype * Icinga prototype
*/ */
var Icinga = function() { var Icinga = function() {
var internalModules = ['icinga/modules/actionTable','icinga/modules/mainDetail']; var internalModules = ['icinga/components/actionTable','icinga/components/mainDetail'];
this.modules = {}; this.modules = {};
var failedModules = []; var failedModules = [];
@ -37,7 +37,7 @@ define([
}; };
var loadModuleScript = function(name) { var loadModuleScript = function(name) {
moduleMgr.enableModule("modules/"+name, function(error) { moduleMgr.enableModule("modules/"+name+"/"+name, function(error) {
failedModules.push({ failedModules.push({
name: name, name: name,
errorMessage: error errorMessage: error