Merge branch 'feature/main-detail-4611'

fixes #4611
This commit is contained in:
Marius Hein 2013-09-24 12:56:45 +02:00
commit 81b2a95a7b
51 changed files with 5613 additions and 735 deletions

View File

@ -33,6 +33,9 @@ use \Icinga\Application\Icinga;
use \Icinga\Application\Config as IcingaConfig;
use \Icinga\Application\Logger;
/**
* Delivery static content to clients
*/
class StaticController extends ActionController
{
/**
@ -117,13 +120,8 @@ class StaticController extends ActionController
}
$response = $this->getResponse();
$response->setHeader('Content-Type', 'text/javascript');
if (!IcingaConfig::app()->global->get('environment') == 'development') {
$this->setCacheHeader(3600);
} else {
$response->setHeader('Pragma', 'no-cache', true);
$response->setHeader('Cache-Control', 'max-age=-3600', true);
$this->setCacheHeader(3600);
}
$response->setHeader(
'Last-Modified',
gmdate(
@ -137,7 +135,7 @@ class StaticController extends ActionController
}
/**
* Set cache header for this respone
* Set cache header for this response
*
* @param integer $maxAge The maximum age to set
*/

View File

@ -7,21 +7,18 @@
</div>
<div class="col-sm-12 col-xs-12 col-md-10 col-lg-10">
<div id="icingamain" class="col-sm-12 col-xs-12 col-md-8 col-lg-8">
<?= $this->render('inline.phtml') ?>
</div>
<div id="icingadetail" class="hidden-sm hidden-xs col-md-4 col-lg-4">
Details
</div>
<?= $this->mainDetail($this->render('inline.phtml'), $this->layout()->detailContent); ?>
</div>
</div>
<br/>
<!-- Make some space at the end of the page -->
<div class="panel">
<div class="panel-body text-center">
Icinga 2 Web &copy; 2013 Icinga Team
<div class="row">
<div class="col-md-12">
<!-- Make some space at the end of the page -->
<div class="panel">
<div class="panel-body text-center">
Icinga 2 Web &copy; 2013 Icinga Team
</div>
</div>
</div>
</div>
</div>

View File

@ -1,4 +1 @@
<?= $this->layout()->moduleStart ?>
<?= $this->layout()->content ?>
<?= $this->layout()->benchmark ?>
<?= $this->layout()->moduleEnd ?>

View File

@ -17,11 +17,11 @@
<!-- Bootstrap and components -->
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap/bootstrap.min.css') ?>" media="screen">
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap/bootstrap-theme.min.css') ?>" media="screen">
<link rel="stylesheet" href="<?= $this->baseUrl('css/main.css') ?>">
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap/datetimepicker.min.css') ?>">
<!--
Not used until styling is clear (see #4550)
<link rel="stylesheet" href="<?= $this->baseUrl('css.php') ?>">
@ -35,12 +35,13 @@
<base target="_parent"/>
<? endif ?>
<script src="<?php echo $this->baseUrl('js/vendor/modernizr-2.6.2.min.js') ?>"></script>
<script src="<?= $this->baseUrl('js/vendor/modernizr-2.6.2.min.js') ?>"></script>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="./js/vendor/html5shiv.js"></script>
<script src="./js/vendor/respond.min.js"></script>
<script src="<?= $this->baseUrl('js/vendor/html5shiv.js');?>"></script>
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
<![endif]-->
</head>

View File

@ -6,7 +6,7 @@ $currentKey = isset($this->navkey) ? $this->navkey : $url;
$item = $this->navigation->keys("menu");
?>
<?php if ($this->auth()->isAuthenticated()): ?>
<ul class="nav nav-stacked" >
<ul class="nav nav-stacked" role="navigation" id="icinganavigation">
<?php
$activeSet = false;
foreach ($item as $itemName) {

View File

@ -1,4 +1,4 @@
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<nav class="navbar navbar-default navbar-fixed-top" id="icingatopbar" role="navigation">
<ul class="nav navbar-nav pull-left">
<li>
<a href="<?= $this->baseUrl('/') ?>" class="brand" style="margin-left:0px;">Icinga</a>

View File

@ -0,0 +1,92 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Helper to render main and detail contents into a container
*/
class Zend_View_Helper_MainDetail extends Zend_View_Helper_Abstract
{
/**
* HTML template
*
* @var string
*/
private static $tpl = <<<'EOT'
<div id='icingamain' class='{{MAIN_CLASS}}'>
{{MAIN_CONTENT}}
</div>
<div id='icingadetail' class='{{DETAIL_CLASS}}'>
{{DETAIL_CONTENT}}
</div>
EOT;
/**
* Css class map when detail pane is expanded
*
* @var array
*/
private static $expanded = array(
'xs' => 12,
'sm' => 12,
'md' => 12,
'lg' => 5
);
/**
* Content render function
*
* @param string $mainContent HTML for main
* @param string $detailContent HTML for detail
*
* @return string HTML all together
*/
public function mainDetail($mainContent, $detailContent = '')
{
$detailCls = 'hidden';
$mainCls = 'col-md-12 col-lg-12 col-xs-12 col-sm-12';
if ($detailContent != '') {
$detailCls = '';
$mainCls = '';
foreach (self::$expanded as $type => $size) {
$detailCls .= 'col-' . $type . '-' . ($size == 12 ? 'push-' : '') . $size . ' ';
$mainCls .= 'col-' . $type . '-' . ($size == 12 ? 'pull-' : '') . ($size < 12 ? (12-$size) : 12). ' ';
}
}
$html = str_replace('{{MAIN_CLASS}}', $mainCls, self::$tpl);
$html = str_replace('{{DETAIL_CLASS}}', $detailCls, $html);
$html = str_replace('{{MAIN_CONTENT}}', $mainContent, $html);
$html = str_replace('{{DETAIL_CONTENT}}', $detailContent, $html);
return $html;
}
}
// @codingStandardsIgnoreEnd

142
doc/container_component.md Normal file
View File

@ -0,0 +1,142 @@
# The Container Component (app/container)
The container component is the most basic building block for icingaweb. Even when displaying an empty controller,
you always have at least two containers in your viewport which are implicitly created: The main and the detail container.
Container handle the following tasks:
* Updating the url part responsible for the container
* Handling Url changes like they occur when the browser history is used by synchronizing their content with the
associated Url part
* Informing subcomponents about changes in the container
## The Container Api
You can find the sourcecode for containers along with jsdoc comments at *./public/js/icinga/components/container.js*.
Here we will discuss the most important calls and their synopsis:
### Accessing Containers:
The container component returns a 'Container' object which allows you to access responsible containers for dom nodes via
the following methods:
* using `new Container($myDomNodes)` which returns a stateless container object wrapping the container responsible for
the first node in $myDomNodes
* using `Container.getMainContainer()` or `Container.getDetailContainer()` which remove the main or detail container
(this one is stateful with a few notes, read on)
**Note:** `new Container($('#icingamain')) != Container.getMainContainer()`, but
`(new Container($('#icingamain'))).containerDom == Container.getMainContainer().containerDom`
** Example #1 getting the container responsible for a dom node **
**HTML**
<div id="icingamain">
<div class="myNode">
Some kind of node
</div>
<div id="somecontainer" data-icinga-component="app/container">
<div class="mySecondNode">
Some other kind of node
<p>
Insert your lorem ipsum here
</p>
</div>
</div>
</div>
**JS**:
require(['jquery', 'app/container'], function($, Container) {
var firstContainer = new Container($('.myNode')); // firstContainer wraps '#icingamain'
var mainContainer = Container.getMainContainer(); // also wraps '#icingamain'
var secondContainer = new Container($('.myNode p')); // #somecontainer is wrapped by secondContainer
firstContainer.someProperty = 'What a nice property!';
mainContainer.someState = 'I have some state';
console.log(firstContainer.someProperty); // return 'What a nice property'
console.log(main.someProperty); // return 'undefined'
console.log(Container.getMainContainer().someState) // return 'I have some state' when page hasn't been refreshed
});
## Containers And The Browser Url
As noted before (and indicated by the `getMainContainer()` and `getDetailContainer()` function), the main and detail
container have a special role. Considering the following Url:
http://my.monitoringhost.org/icingaweb/monitoring/list/host?page=4&detail=%2Fmonitoring%2Fshow%2Fhost%3Fhost%3Dlocalhost
This URL displays the 4th page of your host list in the main container (monitoring/list/host?page=4) and the host information
for localhost in the detail container (monitoring/show/host?host=localhost). When you split this Url up in logical pieces
it looks like this:
http://my.monitoringhost.org/icingaweb/monitoring/list/host?page=4&detail=%2Fmonitoring%2Fshow%2Fhost%3Fhost%3Dlocalhost
\___________ _______________________/\_________ ______________/ \_ ____/\________________ _______________________/
\/ \/ \/ \/
1. Base URL 2.Main container URL and Query 3.Detail param 4. Encoded detail URL and params
1. **Base URL** : I don't think this needs much explanation.
2. **Main container URL and query** : This is the *normal* part of your Url and denotes the controller route that is
being displayed in your main container
3. **Detail parameter**: This parameter will be ignored by the main container and used for rendering the detail container,
if omitted there's simple no detail view to be displayed
4 **Encoded detail URL**: The value of the "detail" parameter is the Url (without the base Url) that returns the content
of the detail area
### Updating A Container's Url
If you want your container to display content from a different Url, you can use the *replaceDomFromUrl()* on your
Container object:
**Example #2 Updating A Containers URL**
**HTML:**
<div id="icingamain">
<div id"mainSub"></div>
</div>
<div id="icingadetail">
<div id"detailSub"></div>
</div>
**JS:**
// this loads the page with the new main container
require(['jquery', 'app/container'], function($, Container) {
new Container('#mainSub').replaceDomFormUrl('/another/url');
}
// this loads the page with the new detail container
require(['jquery', 'app/container'], function($, Container) {
new Container('#detailSub').replaceDomFormUrl('/another/url');
}
// this does NOT work:
require(['jquery', 'app/container'], function($, Container) {
Container.getMainContainer().replaceDomFormUrl('/another/url');
// will never be reached due to a reload
Container.getMainContainer().replaceDomFormUrl('/another/url2');
}
// this loads the page with both main and detail changed (this is rarely needed and should be avoided)
require(['icinga', 'jquery', 'app/container'], function('Icinga', $, Container) {
// it's better to use this:
var mainContainer = Container.getMainContainer();
var detailContainer = Container.getDetailContainer();
mainContainer.updateContainerHref('/another/url'); // first update the main container href
detailContainer.updateContainerHref('/another/url2'); // update the detail href
var url = mainContainer.getContainerHref(detailContainer.getContainerHref()); // fetch the new url
Icinga.replaceBodyFromUrl(url); // and update manual
}
This results in the URL changing to './another/url?detail=%2Fanother%2Fdetail%2Furl.
The advantage of using a Container instance with the subelements (i.e. '\#mainSub') over calling getMain/DetailContainer
directly is that you don't need to know in what container your view is displayed - when you move 'mainSub' into the
detail container, the detail container would be updated afterwards.
**NOTE**: You should read the '...' section in order to understand why you shouldn't do it like in this example

View File

@ -11,10 +11,6 @@ Forms are basically Zend_Form classes with Zend_Form_Element items as controls.
To ensure common functionallity and control dependent fields Icinga 2 Web
provides sub classes to build forms on that.
![Basic form design][form1]
*(Methods and attributes are exemplary and does not reflect the full class implementation)*
### Key design
#### Build of forms
@ -28,6 +24,20 @@ In order to let icingaweb create a submit button for you (which is required for
method) you have to call the *setSubmitLabel($label)* method, which will add a
Zend_Form_Element_Submit element to your form.
#### Client side behaviour
A few methods in our Form implementation don't affect the rendering, but the behaviour of a form.
* **Automatic submission of fields via *\Icinga\Web\Form::enableAutoSubmit(array $forms)***
All form element ids passed in the $forms array will cause a submission of the form when changed. This normally
doesn't cause validation errors, as the form is not really seen as submitted by the 'isSubmittedAndValid', but allows
you to update your form when necessary. For example, when you have a select box whose selection affects which form elements
should be shown, you can use `$form->enableAutoSubmit(array('myForm'))`.
* **User confirmation when discarding forms.** When a user wants to leave the current page despite having unsaved changes,
a popup will appear and asks the user if he **really** wants to leave. This is implemented by the *app/form* componenent
and enabled by default. If you don't want this, you can call *setIgnoreChangeDiscarding($bool)* on your form.
#### Calling is *isSubmittedAndValid()*
*isSubmittedAndValid()* is used to check whether the form is ready to be processed or not.
@ -40,7 +50,6 @@ If the form has been updated, but not submitted (for example, because the a butt
some fields in the form) the form is repopulated but not validated at this time. is SubmittedAndValid() returns false
in this case, but no errors are added to the created form.
In order to be able to use isSubmittedAndValid, you have to define a submitbutton in the form.
This is done with the *setSubmitLabel(string)* function, with the first parameter being the
label set to the submit button.

View File

@ -3,7 +3,9 @@
On top of the elements provided by the Zend Framework, Icinga 2 Web ships its own to offer additional functionality.
The following is a list of these classes, as well as descriptions of the functionality they offer.
## DateTimePicker
## Elements
### DateTimePicker
`Icinga\Web\Form\Element\DateTimePicker` represents a control that allows the user to select date/time and to
display the date and time with a user specified format. Internally the element returns the input as Unix timestamp after
@ -63,4 +65,74 @@ on the timezone set by the user.
)
)
## Validators
### WritablePathValidator
This *Icinga\Web\Form\Validator\WritablePathValidator* validator tests a given (string-)input for being a valid writable
path. Normally it just tests for an existing, writable path but when setRequireExistence() is called, the path must
exist on form submission and be writable.
**Example usage of writablePathValidator
use \Icinga\Web\Form\Validator\WritablePathValidator;
$txtLogPath = new Zend_Form_Element_Text(
array(
'name' => 'logging_app_target',
'label' => 'Application Log Path',
'helptext' => 'The logfile to write the icingaweb debug logs to.'
. 'The webserver must be able to write at this location',
'required' => true,
'value' => $logging->get('target', '/var/log/icingaweb.log')
)
);
$txtLogPath->addValidator(new WritablePathValidator());
### DateTimeValidator
The *Icinga\Web\Form\Validator\DateTimeValidator* validator allows you to validate an input against a set of datetime
patterns. On successful validation, it either gives a valid pattern via getValidPattern, or null if the entered time
is a timestamp. The above DateTimePicker utilizes this validator and should be used instead of directly using the validator.
## Decorators
### ConditionalHidden Decorator
The `Icinga\Web\Form\Decorator\ConditionalHidden` allows you to hide a form element with the 'conditional' attribute for
users that don't have JavaScript enabled (the form is rendered in a \<noscript> tag when conditional is 1). Users with
javascript won't see the elements, users with javascript will see it. This is useful in a lot of cases to allow icingaweb
to be fully functional without JavaScript: Forms can show only sensible forms for most users (and, for example hide the
debug log filepath input when debugging is disabled) and automatically reload the form as soon as the forms should be
shown (e.g. when the debug checkbox is clicked), while users with text-browsers or javascript disabled see all forms,
but can only fill out the ones relative or them.
**Example use of ConditionalHidden**
use Icinga\Web\Form\Decorator\ConditionalHidden;
$textLoggingDebugPath = new Zend_Form_Element_Text(array(
'name' => 'logging_debug_target',
'label' => 'Debug Log Path',
'required' => $this->shouldDisplayDebugLog($debug),
'condition' => $this->shouldDisplayDebugLog($debug), // 1 if displayed, otherwise 0
'value' => getLogPath,
'helptext' => 'Set the path to the debug log'
))
$textLoggingDebugPath->addDecorator(new ConditionalHidden());
$form->addElement($textLoggingDebugPath);
### HelpText Decorator ###
The `Icinga\Web\Form\Decorator\HelpText` decorator allows you to use the 'helptext' property and renders this text in
a consistent ways across the application. It is automatically added by our Form implementation, so you can just use
the 'helptext' property in your form elements.
### BootstrapForm Decorator
`Icinga\Web\Form\Decorator\BoostrapForm` is the decorator we use for our forms.
It causes the forms to be rendered in a bootstrap friendly manner instead of the \<dd> \<dt> encapsulated way Zend normally
renders the forms. You usually don't have to work with this decorator as our Form implementation automatically uses it,
but it's always good to know why forms look how they look.

View File

@ -28,18 +28,20 @@
namespace Icinga\Web\Controller;
use \Exception;
use \Zend_Controller_Action;
use \Zend_Controller_Request_Abstract;
use \Zend_Controller_Front;
use \Zend_Controller_Response_Abstract;
use \Zend_Controller_Action_HelperBroker;
use \Zend_Layout;
use \Icinga\Authentication\Manager as AuthManager;
use \Icinga\Application\Benchmark;
use \Icinga\Exception;
use \Icinga\Application\Config;
use \Icinga\Web\Notification;
use \Icinga\Web\Widget\Tabs;
use \Icinga\Web\Url;
use \Icinga\Web\Request;
/**
* Base class for all core action controllers
@ -104,6 +106,21 @@ class ActionController extends Zend_Controller_Action
}
}
private function dispatchDetailView($url)
{
// strip the base URL from the detail $url
$url = substr($url, strlen($this->getRequest()->getBaseUrl()));
// the host is mandatory, but ignored in Zend
$req = new Request('http://ignoredhost/' . $url);
$router = Zend_Controller_Front::getInstance()->getRouter();
$router->route($req);
$detailHtml = $this->view->action($req->getActionName(), $req->getControllerName(), $req->getModuleName());
$this->_helper->layout->assign('detailContent', $detailHtml);
$this->_helper->layout->assign('detailClass', 'col-sm-12 col-xs-12 col-md-12 col-lg-6');
$this->_helper->layout->assign('mainClass', 'col-sm-12 col-xs-12 col-md-12 col-lg-6');
}
/**
* Check whether the controller requires a login. That is when the controller requires authentication and the
* user is currently not authenticated
@ -150,6 +167,12 @@ class ActionController extends Zend_Controller_Action
*/
private function redirectToLogin()
{
if ($this->getRequest()->isXmlHttpRequest()) {
$this->getResponse()->setHttpResponseCode(401);
$this->getResponse()->sendHeaders();
throw new Exception("You are not logged in");
}
$url = Url::fromPath('/authentication/login');
$this->redirectNow($url->getRelativeUrl());
}
@ -169,6 +192,7 @@ class ActionController extends Zend_Controller_Action
$this->_helper->Redirector->gotoUrlAndExit($url);
}
/**
* Detect whether the current request requires changes in the layout and apply them before rendering
*
@ -179,13 +203,30 @@ class ActionController extends Zend_Controller_Action
Benchmark::measure('Action::postDispatch()');
if ($this->_request->isXmlHttpRequest()) {
if ($this->replaceLayout || $this->_getParam('_render') === 'body') {
$this->_helper->layout()->setLayout('body');
header('X-Icinga-Target: body');
} else {
$this->_helper->layout()->setLayout('inline');
}
$this->_helper->layout()->setLayout('body');
}
if ($this->getParam('detail', false)) {
$detail = $this->getParam('detail');
// Zend uses the GET variables when calling getParam, therefore we have to persist the params,
// clear the $_GET array, call the detail view with the url set in $detail and afterwards recreate
// the $_GET array. If this is not done the following issues occur:
//
// - A stackoverflow issue due to infinite nested calls of buildDetailView (as the detailview has the same
// postDispatch method) when 'detail' is not set to null
//
// - Params (like filters in the URL) from the detail view would be applied on all links of the master view
// as those would be in the $_GET array after building the detail view. E.g. if you have a grid in the
// master and a detail view filtering showing one host in detail, the pagination links of the grid would
// contain the host filter of the detail view
//
$params = $_GET;
$_GET['detail'] = null;
$this->dispatchDetailView($detail);
$_GET = $params;
}
}
/**
@ -196,16 +237,20 @@ class ActionController extends Zend_Controller_Action
* using the version with "_" forces us to use deprecated code. So we try to catch this issue by looking for methods
* with the same name, but with a "_" prefix prepended.
*
* @param string $name The method name to check
* @param array $params The method parameters
* @param string $name The method name to check
* @param mixed $params The method parameters
* @return mixed Anything the method returns
*/
public function __call($name, $params)
{
$deprecatedMethod = '_'.$name;
$deprecatedMethod = '_' . $name;
if (method_exists($this, $deprecatedMethod)) {
return call_user_func_array(array($this, $deprecatedMethod), $params);
}
return parent::__call($name, $params);
parent::__call($name, $params);
return null;
}
}

View File

@ -130,6 +130,13 @@ class Form extends Zend_Form
*/
private $formDecorator;
/**
* Whether to ignore users leaving the form with unsaved changes
*
* @var bool
*/
private $ignoreChangeDiscarding = false;
/**
* Getter for the session ID
*
@ -148,6 +155,16 @@ class Form extends Zend_Form
return $this->sessionId;
}
/**
* Set whether to inform a user when he is about to discard changes (false, default) or not
*
* @param boolean $bool False to not inform users when they leave modified forms, otherwise true
*/
public function setIgnoreChangeDiscarding($bool)
{
$this->ignoreChangeDiscarding = (boolean) $bool;
}
/**
* Setter for the session ID
*
@ -292,8 +309,9 @@ class Form extends Zend_Form
}
$this->addElementDecorators();
$this->created = true;
$this->setAttrib('data-icinga-component', 'app/form');
if (!$this->ignoreChangeDiscarding) {
$this->setAttrib('data-icinga-component', 'app/form');
}
}
}
@ -384,7 +402,8 @@ class Form extends Zend_Form
foreach ($triggerElements as $elementName) {
$element = $this->getElement($elementName);
if ($element !== null) {
$element->setAttrib('data-icinga-form-autosubmit', 'true');
$element->setAttrib('onchange', 'this.form.submit()');
$element->setAttrib('data-icinga-form-autosubmit', true);
} else {
throw new ProgrammingError(
'You need to add the element "' . $elementName . '" to' .

View File

@ -35,9 +35,13 @@ use \Icinga\Web\Hook;
use \Icinga\Web\Widget\Tabextension\DashboardAction;
use \Icinga\Web\Widget\Tabextension\OutputFormat;
use \Icinga\Web\Widget\Tabs;
use Icinga\Module\Monitoring\Backend;
use \Icinga\Module\Monitoring\Backend;
use \Icinga\Web\Widget\SortBox;
use \Icinga\Application\Config as IcingaConfig;
/**
* Controller for listing views
*/
class Monitoring_ListController extends ActionController
{
/**
@ -47,6 +51,8 @@ class Monitoring_ListController extends ActionController
*/
protected $backend;
/**
* Compact layout name
*
* Set to a string containing the compact layout name to use when
* 'compact' is set as the layout parameter, otherwise null
*
@ -64,6 +70,7 @@ class Monitoring_ListController extends ActionController
$this->backend = Backend::getInstance($this->_getParam('backend'));
$this->view->grapher = Hook::get('grapher');
$this->createTabs();
$this->view->activeRowHref = $this->getParam('detail');
}
/**
@ -254,7 +261,7 @@ class Monitoring_ListController extends ActionController
->from($view, $cols)
->applyRequest($this->_request);
$this->handleFormatRequest($query);
return $query;
return $query->paginate();
}
/**
@ -268,7 +275,8 @@ class Monitoring_ListController extends ActionController
$this->_helper->viewRenderer($this->compactView);
}
if ($this->_getParam('format') === 'sql') {
if ($this->_getParam('format') === 'sql'
&& IcingaConfig::app()->global->get('environment', 'production') === 'development') {
echo '<pre>'
. htmlspecialchars(wordwrap($query->getQuery()->dump()))
. '</pre>';

View File

@ -28,14 +28,15 @@
// @codingStandardsIgnoreStart
use \Icinga\Module\Monitoring\Backend;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Hook;
use \Icinga\Web\Controller\ActionController;
use \Icinga\Web\Hook;
use \Icinga\Module\Monitoring\Object\Host;
use \Icinga\Module\Monitoring\Object\Service;
use Icinga\Application\Benchmark;
use Icinga\Web\Widget\Tabextension\OutputFormat;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\BasketAction;
use \Icinga\Application\Benchmark;
use \Icinga\Web\Widget\Tabextension\OutputFormat;
use \Icinga\Web\Widget\Tabextension\DashboardAction;
use \Icinga\Web\Widget\Tabextension\BasketAction;
use \Icinga\Web\Widget\Tabs;
/**
* Class Monitoring_ShowController
@ -207,7 +208,6 @@ class Monitoring_ShowController extends ActionController
public function hostAction()
{
$this->view->active = 'host';
if ($grapher = Hook::get('grapher')) {
if ($grapher->hasGraph($this->view->host->host_name)) {
$this->view->preview_image = $grapher->getPreviewImage(
@ -356,7 +356,7 @@ class Monitoring_ShowController extends ActionController
/**
* Creating tabs for this controller
* @return \Icinga\Web\Widget\AbstractWidget
* @return Tabs
*/
protected function createTabs()
{
@ -376,55 +376,12 @@ class Monitoring_ShowController extends ActionController
} elseif ($service = $this->_getParam('service')) {
$params['service'] = $service;
}
$tabs->add(
'host',
array(
'title' => '{{HOST_ICON}} Host',
'url' => 'monitoring/show/host',
'urlParams' => $params,
)
);
if (!isset($this->view->service)) {
$tabs->add(
'services',
array(
'title' => '{{SERVICE_ICON}} Services',
'url' => 'monitoring/show/services',
'urlParams' => $params,
)
);
}
if (isset($params['service'])) {
$tabs->add(
'service',
array(
'title' => '{{SERVICE_ICON}} Service',
'url' => 'monitoring/show/service',
'urlParams' => $params,
)
);
}
$tabs->add(
'history',
array(
'title' => '{{HISTORY_ICON}} History',
'url' => 'monitoring/show/history',
'urlParams' => $params,
)
);
$tabs->extend(new OutputFormat())
->extend(new DashboardAction())
->extend(new BasketAction);
/**
$tabs->add('contacts', array(
'title' => 'Contacts',
'icon' => 'img/classic/customer.png',
'url' => 'monitoring/detail/contacts',
'urlParams' => $params,
));**/
return $tabs;
}
}
// @codingStandardsIgnoreEnd

View File

@ -13,102 +13,105 @@ function formatDateString($self,$dateString){
$d = new DateTime($dateString);
return $self->util()->showTime($d->getTimestamp());
}
$paginator = $downtimes->paginate();
$downtimes = $downtimes->fetchAll();
?>
<?= $this->sortControl->render($this); ?>
<?=
$this->paginationControl(
$paginator,
null,
array(
'mixedPagination.phtml',
'default'
),
array('preserve' => $this->preserve)
)
?>
<div data-icinga-component="app/mainDetailGrid">
<div>
<?= $this->sortControl->render($this); ?>
</div>
<div>
<?= $this->paginationControl(
$downtimes,
null,
array(
'mixedPagination.phtml',
'default'
),
array('preserve' => $this->preserve)
);
?>
<table class="table table-condensed">
<thead>
<tr>
<th>Is In Effect</th>
<th>Object</th>
<th>Host Name</th>
<th>Service Name</th>
<th>Entry Time</th>
<th>Author</th>
<th>Comment</th>
<th>Start Time</th>
<th>End Time</th>
<th>Type</th>
<th>Trigger Time</th>
<th>Downtime ID</th>
<th>Trigger ID</th>
<th>Duration</th>
</tr>
</thead>
<table class="table table-condensed">
<thead>
<tr>
<th>Is In Effect</th>
<th>Object</th>
<th>Host Name</th>
<th>Service Name</th>
<th>Entry Time</th>
<th>Author</th>
<th>Comment</th>
<th>Start Time</th>
<th>End Time</th>
<th>Type</th>
<th>Trigger Time</th>
<th>Downtime ID</th>
<th>Trigger ID</th>
<th>Duration</th>
</tr>
</thead>
<?php foreach ($downtimes as $downtime): ?>
<?php foreach ($downtimes as $downtime): ?>
<tr>
<td>
<?= ($downtime->downtime_is_in_effect == 0 ? 'False' : '<b>True</b>'); ?>
</td>
<td>
<div>
<?php if ($downtime->object_type == 'service'): ?>
{{SERVICE_ICON}}
<?php endif; ?>
<tr>
<td>
<?= ($downtime->downtime_is_in_effect == 0 ? 'False' : '<b>True</b>'); ?>
</td>
<td>
<div>
<?php if ($downtime->object_type == 'service'): ?>
{{SERVICE_ICON}}
<?php endif; ?>
<?php if ($downtime->object_type == 'host'): ?>
{{HOST_ICON}}
<?php endif; ?>
</div>
</td>
<td>
<?= $downtime->host_name ?>
</td>
<td>
<?= $downtime->service_description ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_entry_time); ?>
</td>
<td>
<?= $downtime->downtime_author_name ?>
</td>
<td>
<?= $downtime->downtime_comment_data ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_start_time); ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_end_time); ?>
</td>
<td>
<?= $downtime->downtime_is_fixed == 1 ? 'Fixed' : 'Not Fixed' ?>
</td>
<td>
<?php
$date = formatDateString($this,$downtime->downtime_trigger_time);
echo $date != 'undef' ? $date : 'N/A';
?>
</td>
<td>
<?= $downtime->downtime_internal_downtime_id ?>
</td>
<td>
<?= $downtime->downtime_triggered_by_id == 0 ?
'N/A' : $downtime->downtime_triggered_by_id ?>
</td>
<td>
<?= $this->util()->showHourMin(intval($downtime->downtime_duration)); ?>
</td>
<td>
</td>
</tr>
<?php endforeach ?>
</table>
<?php if ($downtime->object_type == 'host'): ?>
{{HOST_ICON}}
<?php endif; ?>
</div>
</td>
<td>
<?= $downtime->host_name ?>
</td>
<td>
<?= $downtime->service_description ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_entry_time); ?>
</td>
<td>
<?= $downtime->downtime_author_name ?>
</td>
<td>
<?= $downtime->downtime_comment_data ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_start_time); ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_end_time); ?>
</td>
<td>
<?= $downtime->downtime_is_fixed == 1 ? 'Fixed' : 'Not Fixed' ?>
</td>
<td>
<?php
$date = formatDateString($this,$downtime->downtime_trigger_time);
echo $date != 'undef' ? $date : 'N/A';
?>
</td>
<td>
<?= $downtime->downtime_internal_downtime_id ?>
</td>
<td>
<?= $downtime->downtime_triggered_by_id == 0 ?
'N/A' : $downtime->downtime_triggered_by_id ?>
</td>
<td>
<?= $this->util()->showHourMin(intval($downtime->downtime_duration)); ?>
</td>
<td>
</td>
</tr>
<?php endforeach ?>
</table>
</div>
</div>

View File

@ -1,5 +1,4 @@
<?php
$hosts = $this->hosts->paginate();
$viewHelper = $this->getHelper('MonitoringState');
?>

View File

@ -1,109 +1,90 @@
<?= $this->tabs->render($this); ?>
<?php
$hosts = $this->hosts->paginate();
$viewHelper = $this->getHelper('MonitoringState');
?>
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($hosts, null, null, array('preserve' => $this->preserve)); ?>
<div data-icinga-component="app/mainDetailGrid">
<table class="table table-condensed">
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($hosts, null, null, array('preserve' => $this->preserve)); ?>
<thead>
<table class="table table-condensed">
<thead>
<tr>
<th colspan="3">Status</th>
<th colspan="2">Status</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
</thead>
<tbody>
<?php foreach($hosts as $host): ?>
<tr>
<td>
<div>
<?php if ($host->host_icon_image) : ?>
<img src="<?= $host->host_icon_image; ?>"/>
<?php endif; ?>
</div>
</td>
<?php foreach($hosts as $host): ?>
<?php $hostLink = $this->href('monitoring/show/host', array('host' => $host->host_name)); ?>
<tr <?= ($this->activeRowHref === $hostLink) ? 'class="active"' : ''; ?> >
<td>
<a style="visibility:hidden" href="<?= $hostLink; ?>"></a>
<div>
<form class="reschedule">
<td>
<div>
<?php if (!$host->host_handled && $host->host_state > 0): ?>
<a href="#" title="<?= 'Unhandled host' ?>">
<i>{{UNHANDLED_ICON}}</i>
</form>
<?php if ($host->host_icon_image) : ?>
<img src="<?= $host->host_icon_image; ?>"/>
<?php endif; ?>
</div>
</td>
<td>
<div>
<?php if (!$host->host_handled && $host->host_state > 0): ?>
<a href="#" title="<?= 'Unhandled host' ?>">
<i>{{UNHANDLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($host->host_acknowledged && !$host->host_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($host->host_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php endif; ?>
</div>
</td>
<td>
<?php if ($host->host_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($host->host_acknowledged && !$host->host_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
<a href="<?= $hostLink ?>">
<b> <?= $host->host_name ?></b><br/>
<i> <?= $host->host_address ?></i>
</a>
<?php if ($host->host_action_url != ""): ?>
<a href="<?= $host->host_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($host->host_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php if ($host->host_notes_url != ""): ?>
<a href="<?= $host->host_notes_url; ?>">Notes</a>
<?php endif; ?>
<?php if (!$host->host_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i>{{NOTIFICATIONS_DISABLED_ICON}}</i>
</a>
<?php endif; ?>
</td>
<?php if ($host->host_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i>{{IN_DOWNTIME_ICON}}</i>
</a>
<?php endif; ?>
<td>
<?= $this->escape(substr(strip_tags($host->host_output), 0, 10000)); ?>
</td>
</div>
</td>
<td title="<?= $viewHelper->getStateTitle($host, 'host'); ?>">
<div >
<b><?= ucfirst($viewHelper->monitoringState($host, 'host')); ?></b>
<div> Since&nbsp;
<?= $this->timeSince($host->host_last_state_change); ?>
<?php if ($host->host_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i>{{SOFTSTATE_ICON}}</i>
</a>
<?php endif; ?>
</div>
</div>
</td>
<td>
<?php if ($host->host_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?= $this->paginationControl($hosts, null, null, array('preserve' => $this->preserve)); ?>
<a href="<?= $this->href('monitoring/show/host', array('host' => $host->host_name)); ?>">
<b> <?= $host->host_name ?></b><br/>
<i> <?= $host->host_address ?></i>
</a>
<?php if ($host->host_action_url != ""): ?>
<a href="<?= $host->host_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($host->host_notes_url != ""): ?>
<a href="<?= $host->host_notes_url; ?>">Notes</a>
<?php endif; ?>
</td>
<td>
<?= $this->escape(substr(strip_tags($host->host_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -3,29 +3,38 @@
<?php
$formatter = $this->getHelper('MonitoringProperties');
?>
<div data-icinga-component="app/mainDetailGrid">
<?= $this->sortControl->render($this); ?>
<?= $this->sortControl->render($this); ?>
<?php
$notifications = $this->notifications->paginate();
echo $this->paginationControl($notifications, null, null, array('preserve' => $this->preserve));
?>
<table class="table table-condensed">
<thead>
<tr>
<th>Host</th>
<th>Service</th>
<th>Type</th>
<th>Time</th>
<th>Contact</th>
<th>Notification Command</th>
<th>Information</th>
</tr>
</thead>
<tbody>
<?php foreach ($notifications as $notification): ?>
<tr>
<td>
<?= $this->paginationControl($notifications, null, null, array('preserve' => $this->preserve)); ?>
<table class="table table-condensed" >
<thead>
<tr>
<th>Host</th>
<th>Service</th>
<th>Type</th>
<th>Time</th>
<th>Contact</th>
<th>Notification Command</th>
<th>Information</th>
</tr>
</thead>
<tbody>
<?php foreach ($notifications as $notification): ?>
<?php
if (empty($notification->service_description)) {
$detailLink = $this->href('monitoring/show/host', array('host' => $notification->host_name));
} else {
$detailLink = $this->href('monitoring/show/host', array(
'host' => $notification->host_name,
'service' => $notification->service_description
)
);
}
?>
<tr <?= ($this->activeRowHref === $detailLink) ? 'class="active"' : ''; ?>>
<td>
<a href="<?= $detailLink; ?>" style="visibility:hidden"></a>
<a href="<?= $this->href('monitoring/show/host', array('host' => $notification->host_name)); ?>">
<?= $notification->host_name ?>
</a>
@ -37,6 +46,7 @@ echo $this->paginationControl($notifications, null, null, array('preserve' => $t
)
); ?>">
<?= empty($notification->service_description) ? '' : $notification->service_description; ?>
</a>
</td>
<td><?= $formatter->getNotificationType($notification); ?>
</td>
@ -56,3 +66,5 @@ echo $this->paginationControl($notifications, null, null, array('preserve' => $t
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -1,5 +1,4 @@
<?php
$paginator = $services->paginate();
$viewHelper = $this->getHelper('MonitoringState');
?>
@ -9,7 +8,7 @@ $viewHelper = $this->getHelper('MonitoringState');
</div>
<?php return; endif ?>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve)) ?>
<?= $this->paginationControl($services, null, null, array('preserve' => $this->preserve)) ?>
<table class="table table-condensed">
<thead>
@ -21,7 +20,7 @@ $viewHelper = $this->getHelper('MonitoringState');
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service): ?>
<?php foreach ($services as $service): ?>
<tr>
<td>
<div>

View File

@ -1,134 +1,136 @@
<?= $this->tabs->render($this); ?>
<?php
$paginator = $services->paginate();
$viewHelper = $this->getHelper('MonitoringState');
?>
<div data-icinga-component="app/mainDetailGrid">
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve)) ?>
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($this->services, null, null, array('preserve' => $this->preserve)) ?>
<table class="table table-condensed">
<thead>
<tr>
<th colspan="3">Status</th>
<th>Service</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service): ?>
<tr>
<td>
<div>
<?php if ($service->service_icon_image) : ?>
<img src="<?= $service->service_icon_image; ?>"/>
<?php endif; ?>
</div>
</td>
<td>
<div>
<?php if (!$service->service_handled && $service->service_state > 0): ?>
<a href="#" title="<?= 'Unhandled service' ?>">
<i>{{UNHANDLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_acknowledged && !$service->service_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php endif; ?>
<?php if (!$service->service_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i>{{NOTIFICATIONS_DISABLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i>{{IN_DOWNTIME_ICON}}</i>
</a>
<?php endif; ?>
</div>
</td>
<td title="<?= $viewHelper->getStateTitle($service, 'service'); ?>">
<div>
<b><?= ucfirst($viewHelper->monitoringState($service, 'service')); ?></b>
<div> Since&nbsp;
<?= $this->timeSince($service->service_last_state_change); ?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i>{{SOFTSTATE_ICON}}</i>
</a>
<?php endif; ?>
<table class="table table-condensed">
<thead>
<tr>
<th colspan="3">Status</th>
<th>Service</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($services as $service): ?>
<?php
$serviceLink = $this->href('monitoring/show/service',array(
'host' => $service->host_name,
'service' => $service->service_description
)
);
$hostLink = $this->href('monitoring/show/host',array(
'host' => $service->host_name,
)
);
?>
<tr <?= ($this->activeRowHref === $serviceLink) ? 'class="active"' : ''; ?>>
<td>
<a style="visibility:hidden" href="<?= $serviceLink; ?>"></a>
<div>
<?php if ($service->service_icon_image) : ?>
<img src="<?= $service->service_icon_image; ?>"/>
<?php endif; ?>
</div>
</div>
</td>
</td>
<td>
<div>
<?php if (!$service->service_handled && $service->service_state > 0): ?>
<a href="#" title="<?= 'Unhandled service' ?>">
<i>{{UNHANDLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_acknowledged && !$service->service_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php endif; ?>
<?php if (!$service->service_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i>{{NOTIFICATIONS_DISABLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i>{{IN_DOWNTIME_ICON}}</i>
</a>
<?php endif; ?>
</div>
</td>
<td>
<?php if ($service->service_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
<a href="<?= $this->href(
'monitoring/show/service',
array(
'host' => $service->host_name,
'service' => $service->service_description
)
); ?>">
<b> <?= $service->service_display_name; ?></b>
</a>
<br/>
<td title="<?= $viewHelper->getStateTitle($service, 'service'); ?>">
<div>
<b><?= ucfirst($viewHelper->monitoringState($service, 'service')); ?></b>
<div> Since&nbsp;
<?= $this->timeSince($service->service_last_state_change); ?>
<?php if ($service->service_action_url != ""): ?>
<a href="<?= $service->service_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i>{{SOFTSTATE_ICON}}</i>
</a>
<?php endif; ?>
</div>
</div>
</td>
<?php if ($service->service_notes_url != ""): ?>
<a href="<?= $service->service_notes_url; ?>">Notes</a>
<?php endif; ?>
</td>
<td>
<?php if ($service->service_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
<a href="<?= $serviceLink; ?>">
<b> <?= $service->service_display_name; ?></b>
</a>
<br/>
<td title="<?= $viewHelper->getStateTitle($service, 'host'); ?>">
<a href="<?= $this->href(
'monitoring/show/host',
array(
'host' => $service->host_name,
)
); ?>">
<?= $service->host_name; ?>
</a>
<?php if ($service->service_action_url != ""): ?>
<a href="<?= $service->service_action_url; ?>">Action</a>
<?php endif; ?>
<div>
(<?= ucfirst($viewHelper->monitoringState($service, 'host')); ?>)
</div>
<span>
<?= $service->host_address ?>
</span>
</td>
<?php if ($service->service_notes_url != ""): ?>
<a href="<?= $service->service_notes_url; ?>">Notes</a>
<?php endif; ?>
<td>
<?= $this->escape(substr(strip_tags($service->service_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
<td title="<?= $viewHelper->getStateTitle($service, 'host'); ?>">
<a href="<?= $hostLink; ?>">
<?= $service->host_name; ?>
</a>
<div>
(<?= ucfirst($viewHelper->monitoringState($service, 'host')); ?>)
</div>
<span>
<?= $service->host_address ?>
</span>
</td>
<td>
<?= $this->escape(substr(strip_tags($service->service_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -65,11 +65,11 @@ if (!$this->compact) {
<td rowspan="2">
<?php if ($this->host->host_action_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->host->host_notes_url ?>'>Host actions </a>
<a target="_new" href='<?= $this->host->host_notes_url ?>'>Host actions </a>
<?php endif; ?>
<?php if ($this->host->host_notes_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->host->host_notes_url ?>'>Host notes </a>
<a target="_new" href='<?= $this->host->host_notes_url ?>'>Host notes </a>
<?php endif; ?>
</td>
</tr>
@ -103,12 +103,12 @@ if (!$this->compact) {
<td rowspan="2">
<?php if ($this->service->service_action_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->service->service_notes_url ?>'>Service actions </a>
<a target="_new" href='<?= $this->service->service_notes_url ?>'>Service actions </a>
<?php endif; ?>
<?php if ($this->service->service_notes_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->service->service_notes_url ?>'>Service notes </a>
<a target="_new" href='<?= $this->service->service_notes_url ?>'>Service notes </a>
<?php endif; ?>
</td>
</tr>

View File

@ -1,4 +1,5 @@
<?php
$hostgroupLinkList = array();
if (!empty($this->hostgroups)) {
foreach ($this->hostgroups as $name => $alias) {

View File

@ -25,22 +25,22 @@ $this->partial(
?>
<?= $this->preview_image ?>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Plugin output</span>
</div>
<div>
<div class="panel-body">
<?= $this->pluginOutput($this->service->service_output); ?>
<?= $this->pluginOutput($this->service->service_long_output); ?>
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
{{CHECK_ICON}} <span>Check Command</span>
</div>
<div>
<div class="panel-body">
<?php
$explodedCommand = explode('!', $this->service->service_check_command, 2);
echo array_shift($explodedCommand);
@ -49,16 +49,18 @@ $this->partial(
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
{{GROUP_ICON}} <span>Groups and Contacts</span>
</div>
<div class="panel-body">
<?php if (count($servicegroupLinkList)) { ?>
{{SERVICEGROUP_ICON}} <strong>Servicegroups:</strong>
<?= implode(' ', $servicegroupLinkList); ?>
<?php } ?>
<?= $this->render('show/components/contacts.phtml') ?>
</div>
</div>
<?= $this->render('show/components/comments.phtml'); ?>
@ -68,40 +70,39 @@ $this->partial(
<?= $this->render('show/components/customvars.phtml'); ?>
<?php if ($this->service->service_perfdata): ?>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Perfdata</span>
</div>
<div>
<div class="panel-body">
<?= $this->perfdata($this->service->service_perfdata); ?>
</div>
</div>
<?php endif; ?>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Flags</span>
</div>
<div>
<div class="panel-body">
<?= $this->render('show/components/flags.phtml'); ?>
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Properties</span>
</div>
<div>
<div class="panel-body">
<?= $this->render('show/components/properties.phtml'); ?>
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
{{COMMAND_ICON}} <span>Commands</span>
</div>
<div>
<div class="panel-body">
<?= $this->monitoringCommands($this->service, 'full'); ?>
</div>
</div>

View File

@ -67,10 +67,11 @@ class ListControllerHostMySQLTest extends MonitoringControllerTest
}
$controller = $this->requireController('ListController', $backend);
$controller->hostsAction();
$result = $controller->view->hosts->fetchAll();
$result = $controller->view->hosts;
$this->assertCount(5, $result, 'Testing correct result count for '.$backend);
$this->assertEquals(5, $result->getTotalItemCount(), 'Testing correct result count for '.$backend);
$result = $result->getAdapter()->getItems(0,6);
for($i=1;$i<=5;$i++) {
$this->assertEquals('host'.$i, $result[$i-1]->host_name, "Asserting correct host names for backend ".$backend);
}

View File

@ -56,9 +56,11 @@ class ListControllerServiceMySQLTest extends MonitoringControllerTest
$controller = $this->requireController('ListController', $backend);
$controller->servicesAction();
$result = $controller->view->services->fetchAll();
$this->assertEquals(9, count($result), "Testing for correct service count");
$result = $controller->view->services;
$this->assertEquals(9, $result->getTotalItemCount(), "Testing for correct service count");
$result = $result->getAdapter()->getItems(0,1);
$this->assertEquals("notes.url", $result[0]->service_notes_url, "Testing for correct notes_url");
$this->assertEquals("action.url", $result[0]->service_action_url, "Testing for correct action_url");
$this->assertEquals(0, $result[0]->service_state, "Testing for correct Service state");

View File

@ -109,7 +109,7 @@ define(['jquery', 'logging', 'icinga/componentRegistry'], function ($, log, regi
type,
el,
function(cmp) {
var id = registry.add(cmp, el.id, type);
var id = registry.add(cmp, type);
registry.markActive(id);
el.id = id;
finalize();

View File

@ -112,10 +112,8 @@ define(['jquery'], function($) {
*
* @returns {*|Array}
*/
this.add = function(cmp, id, type) {
if (!id){
id = self.getId(cmp) || createId();
}
this.add = function(cmp, type) {
var id = createId();
components[id] = {
cmp: cmp,
type: type,

View File

@ -0,0 +1,448 @@
/*global Icinga:false, Modernizr: false, document: false, History: false, define:false require:false base_url:false console:false */
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
define(['jquery', 'logging', 'icinga/componentLoader', 'URIjs/URI', 'URIjs/URITemplate'],
function($, logger, componentLoader, URI) {
"use strict";
var Icinga;
/**
* Enumeration of possible container types
*
* @type {{GENERIC: string, MAIN: string, DETAIL: string}}
*/
var CONTAINER_TYPES = {
'GENERIC' : 'generic',
'MAIN' : 'icingamain',
'DETAIL': 'icingadetail'
};
/**
* Static reference to the main container, populated on the first 'getMainContainer' call
*
* @type {Container}
*/
var mainContainer = null;
/**
* Static reference to the detail container, populated on the first getDetailContainer call
*
* @type {Container}
*/
var detailContainer = null;
/**
* A handler for accessing icinga containers, i.e. the #icingamain, #icingadetail containers and specific 'app/container'
* components.
*
* This component can be constructed with every object as the parameter and will provide access to the nearest
* container (which could be the applied object itself, if it is a container) wrapping this object.
*
* The windows url should always be modified with this implementation, so an objects context should point to a
* new URL, call new Container('#myObject').updateContainerHref('/my/url')
*
* This requirejs module also registers a global handler catching all links of the main container and rendering
* their content to the main container, in case you don't want to extend the container with additional handlers.
*
* @param {HTMLElement, jQuery, String} target A jQuery resultset, dom element or matcher string
*/
var Container = function(target) {
/**
* Set to true when no history changes should be made
*
* @type {boolean} true to disable History.js calls, false to reenable them
*/
this.freezeHistory = false;
/**
* Return the container that is at the nearest location to this element, or the element itself if it is a container
*
* Containers are either the icingamain and icingadetail ids or components tagged as app/container
*
* @param {String, jQuery, HTMLElement} target The node to use as the starting point
*
* @returns {HTMLElement|null} The nearest container found or null if target is no container
* and no container is above target
*/
var findNearestContainer = function(target) {
target = $(target);
if (target.attr('data-icinga-component') === 'app/container' ||
target.attr('id') === 'icingamain' || target.attr('id') === 'icingadetail') {
return target;
}
return target.parents('[data-icinga-component="app/container"], #icingamain, #icingadetail')[0];
};
/**
* Find the container responsible for target and determine it's type
*
* @param {HTMLElement, jQuery, String} target A jQuery resultset, dom element or matcher string
*/
this.construct = function(target) {
this.containerDom = $(findNearestContainer(target));
this.containerType = CONTAINER_TYPES.GENERIC;
if (this.containerDom.attr('id') === CONTAINER_TYPES.MAIN) {
this.containerType = CONTAINER_TYPES.MAIN;
} else if (this.containerDom.attr('id') === CONTAINER_TYPES.DETAIL) {
this.containerType = CONTAINER_TYPES.DETAIL;
} else {
this.containerType = CONTAINER_TYPES.GENERIC;
}
this.containerDom.attr('data-icinga-href', this.getContainerHref());
};
/**
* Returns the window without the hostname
*
* @returns {string} path with query, search and hash
*/
var getWindowLocationWithoutHost = function() {
return window.location.pathname + window.location.search + window.location.hash;
};
/**
* Extract and return the main container's location from the current Url
*
* This takes the window's Url and removes the detail part
*
* @returns {string} The Url of the main container
*/
var getMainContainerHrefFromUrl = function(baseUrl) {
// main has the url without the icingadetail part
var href = URI(getWindowLocationWithoutHost(baseUrl));
href.removeQuery('detail');
return href.href();
};
/**
* Return the detail container's location from the current Url
*
* This takes the detail parameter of the url and returns it or
* undefined if no location is given
*
* @returns {string|undefined} The Url of the detail container or undefined if no detail container is active
*/
var getDetailContainerHrefFromUrl = function(baseUrl) {
var location = new URI(baseUrl);
var href = URI.parseQuery(location.query()).detail;
if (!href) {
return;
}
// detail is a query param, so it is possible that (due to a bug or whatever) multiple
// detail fields are declared and returned as arrays
if (typeof href !== 'string') {
href = href[0];
}
// transform the detail parmameter to an Url
return URI(href).href();
};
/**
* Return the Url of this container
*
* This is mostly determined by the Url of the window, but for generic containers we have to rely on the
* "data-icinga-href" attribute of the container (which is also available for main and detail, but less
* reliable during history changes)
*
* @returns {String|undefined} The Url of the container or undefined if the container has no Url set
*/
this.getContainerHref = function(baseUrl) {
baseUrl = baseUrl || getWindowLocationWithoutHost();
switch (this.containerType) {
case CONTAINER_TYPES.MAIN:
return getMainContainerHrefFromUrl(baseUrl);
case CONTAINER_TYPES.DETAIL:
return getDetailContainerHrefFromUrl(baseUrl);
case CONTAINER_TYPES.GENERIC:
if (this.containerDom.attr('data-icinga-href')) {
return URI(this.containerDom.attr('data-icinga-href'));
} else {
return URI(baseUrl).href();
}
}
};
/**
* Return a href with representing the current view, but url as the main container
*
* @param {URI} url The main Url to use as an URI.js object
*
* @returns {URI} The modified URI.js containing the new main and the current detail link
*/
var setMainContainerHref = function(url, baseUrl) {
var detail = getDetailContainerHrefFromUrl(baseUrl);
if (detail) {
url.addQuery('detail', detail);
}
return url;
};
/**
* Return a complete Href string representing the current detail href and the provided main Url
*
* @param {URI} url The detail Url to use as an URI.js object
*
* @returns {URI} The modified URI.js containing the new detail and the current main link
*/
var setDetailContainerHref = function(url, baseUrl) {
var location = new URI(baseUrl);
location.removeQuery('detail');
if (typeof url !== 'undefined') { // no detail Url given
location.addQuery('detail', url);
}
return location;
};
/**
* Update the Url of this container and let the Url reflect the new changes, if required
*
* This updates the window Url and the data-icinga-href attribute of the container. The latter one is required
* to see which url is the last one the container displayed (e.g. after History changes, the url has changed
* but the containers data-icinga-href still points to the containers element).
*
* @param {String|URI} url An Url string or a URI.js object representing the new Url for this container
*
* @return {String} url The new Url of the application (main and detail)
*/
this.updateContainerHref = function(url, baseUrl) {
baseUrl = baseUrl || getWindowLocationWithoutHost();
if (typeof url === "string") {
url = URI(url);
}
var containerUrl, windowUrl;
switch (this.containerType) {
case CONTAINER_TYPES.MAIN:
windowUrl = setMainContainerHref(url, baseUrl);
containerUrl = windowUrl.clone().removeQuery('detail');
break;
case CONTAINER_TYPES.DETAIL:
windowUrl = setDetailContainerHref(url, baseUrl);
containerUrl = url;
break;
case CONTAINER_TYPES.GENERIC:
containerUrl = url;
windowUrl = baseUrl;
break;
}
if (containerUrl) {
this.containerDom.attr('data-icinga-href', containerUrl);
} else {
this.containerDom.removeAttr('data-icinga-href');
}
return windowUrl.href();
};
/**
* Load the provided url, stop all pending requests for this container and call replaceDom for the returned html
*
* This method relaods the page if a 401 (Authorization required) header is encountered
*
* @param {String, URI} url The Url to load or and URI.js object encapsulating it
*/
this.replaceDomFromUrl = function(url) {
Icinga.replaceBodyFromUrl(this.updateContainerHref(url));
};
/**
* Remove all dom nodes from this container and replace them with the ones from domNodes
*
* Triggers the custom "updated" event and causes a rescan for components on the DOM nodes
*
* If keepLayout is given, the detail panel won't be expanded if this is an update for the detail panel,
* otherwise it will be automatically shown.
*
* @param {String, jQuery, HTMLElement, Array} domNodes Any valid representation of the Dom nodes to insert
* @param {boolean} keepLayout Whether to keep the layout untouched, even if detail
* is updated end collapsed
*
* @see registerOnUpdate
*/
this.replaceDom = function(domNodes, keepLayout) {
this.containerDom.empty().append(domNodes);
this.containerDom.trigger('updated', [domNodes]);
componentLoader.load();
if (!keepLayout) {
if (this.containerType === CONTAINER_TYPES.DETAIL) {
this.showDetail();
}
}
};
/**
* Register a method to be called when this container is updated
*
* @param {function} fn The function to call when the container is updated
*/
this.registerOnUpdate = function(fn) {
this.containerDom.on('updated', fn);
};
this.construct(target);
};
/**
* Static method for detecting whether the given link is external or only browserside (hash links)
*
* @param {String} link The link to test for being site-related
*
* @returns {boolean} True when the link should be executed with the browsers normal behaviour, false
* when the link should be catched and processed internally
*/
Container.isExternalLink = function(link) {
if (link[0] === '#') {
return true;
}
return (/^\/\//).test(URI(link).relativeTo(window.location.href).href());
};
/**
* Return the page's detail container (which is always there)
*
* @returns {Container} The detail container of the page
*/
Container.getDetailContainer = function() {
detailContainer = detailContainer || new Container('#icingadetail');
if(!jQuery.contains(document.body, detailContainer)) {
detailContainer = new Container('#icingadetail');
}
return detailContainer;
};
/**
* Return the page's main container (which is always there)
*
* @returns {Container} The main container of the page
*/
Container.getMainContainer = function() {
mainContainer = mainContainer || new Container('#icingamain');
if(!jQuery.contains(document.body, mainContainer)) {
mainContainer = new Container('#icingamain');
}
return mainContainer;
};
/**
* Expand the detail container and shrinken the main container
*
* Available as a static method on the Container object or as an instance method
*/
Container.prototype.showDetail = Container.showDetail = function() {
var mainDom = Container.getMainContainer().containerDom,
detailDom = Container.getDetailContainer().containerDom;
mainDom.removeClass();
detailDom.removeClass();
mainDom.addClass('hidden-md');
detailDom.addClass('col-md-12');
mainDom.addClass('col-lg-7');
detailDom.addClass('col-lg-5');
mainDom.addClass('hidden-xs');
detailDom.addClass('col-xs-12');
mainDom.addClass('hidden-sm');
detailDom.addClass('col-sm-12');
};
/**
* Hide the detail container and expand the main container
*
* Also updates the Url by removing the detail part
*
* Available as a static method on the Container object or as an instance method
*/
Container.prototype.hideDetail = Container.hideDetail = function() {
var mainDom = Container.getMainContainer().containerDom,
detailDom = Container.getDetailContainer().containerDom;
mainDom.removeClass();
detailDom.removeClass();
mainDom.addClass('col-md-12');
detailDom.addClass('hidden-md');
mainDom.addClass('col-lg-12');
detailDom.addClass('hidden-lg');
mainDom.addClass('col-xs-12');
detailDom.addClass('hidden-xs');
mainDom.addClass('col-sm-12');
detailDom.addClass('hidden-sm');
detailDom.removeAttr('data-icinga-href');
if (typeof this.freezeHistory === 'undefined' || !this.freezeHistory) {
History.replaceState(
{},
document.title,
URI(window.location.href).removeQuery('detail').href()
);
}
};
if (Modernizr.history) {
/**
* Register the click behaviour of the main container, which means that every link, if not catched in a
* more specific handler, causes an update of the main container if it's not external or a browser behaviour link
* (those starting with '#').
*/
$('body').on('click', '#icingamain, #icingadetail', function(ev) {
var targetEl = ev.target || ev.toElement || ev.relatedTarget;
if (targetEl.tagName.toLowerCase() !== 'a') {
return true;
}
if (Container.isExternalLink($(targetEl).attr('href'))) {
return true;
} else {
// detail links render to main by default;
Icinga.replaceBodyFromUrl(
mainContainer.updateContainerHref(URI($(targetEl).attr('href')).href())
);
ev.preventDefault();
ev.stopPropagation();
return false;
}
});
}
/**
* Injects the icinga object into the Container class
*
* This can't be done via requirejs as we would end up in circular references
*
* @param {Icinga} icingaObj The Icinga object to use for reloading
*/
Container.setIcinga = function(icingaObj) {
Icinga = icingaObj;
};
return Container;
});

View File

@ -43,17 +43,7 @@ define(['jquery'], function($) {
*/
var ATTR_MODIFIED = 'data-icinga-form-modified';
/**
* Return true when the input element is a autosubmit field
*
* @param {string|DOMElement|jQuery} el The element to test for autosubmission
*
* @returns {boolean} True when the element should be automatically submitted
*/
var isAutoSubmitInput = function(el) {
return $(el).attr('data-icinga-form-autosubmit') === 'true' ||
$(el).attr('data-icinga-form-autosubmit') === '1';
};
/**
* Takes a form and returns an overloaded jQuery object
@ -105,16 +95,17 @@ define(['jquery'], function($) {
*/
var registerFormEventHandler = function(form) {
form.change(function(changed) {
if (isAutoSubmitInput(changed.target)) {
if ($(changed.target).attr('data-icinga-form-autosubmit')) {
form.clearModificationFlag();
form.submit();
} else {
form.setModificationFlag();
}
});
// submissions should clear the modification flag
form.submit(form.clearModificationFlag);
form.submit(function() {
form.clearModificationFlag();
});
};
/**
@ -137,7 +128,12 @@ define(['jquery'], function($) {
*/
return function(targetForm) {
var form = getFormObject(targetForm);
registerFormEventHandler(form);
registerLeaveConfirmationHandler(form);
// Remove DOM level onchange, we registered proper jQuery listeners for them
$('[data-icinga-form-autosubmit]').removeAttr('onchange');
};
});

View File

@ -0,0 +1,208 @@
/*global Icinga:false, document: false, define:false require:false base_url:false console:false */
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
define(['components/app/container', 'jquery', 'logging', 'URIjs/URI', 'URIjs/URITemplate'],
function(Container, $, logger, URI) {
"use strict";
/**
* Master/Detail grid component handling history, link behaviour, selection (@TODO 3788) and updates of
* grids
*
* @param {HTMLElement} The outer element to apply the behaviour on
*/
return function(gridDomNode) {
/**
* Reference to the outer container of this component
*
* @type {*|HTMLElement}
*/
gridDomNode = $(gridDomNode);
/**
* A container component to use for updating URLs and content
*
* @type {Container}
*/
this.container = null;
/**
* The node wrapping the table and pagination
*
* @type {jQuery}
*/
var contentNode;
/**
* jQuery matcher result of the form components wrapping the controls
*
* @type {jQuery}
*/
var controlForms;
/**
* Detect and select control forms for this table and return them
*
* Form controls are either all forms underneath the of the component, but not underneath the table
* or in a dom node explicitly tagged with the 'data-icinga-actiongrid-controls' attribute
*
* @param {jQuery|null} domContext The context to use as the root node for matching, if null
* the component node given in the constructor is used
*
* @returns {jQuery} A selector result with all forms modifying this grid
*/
var determineControlForms = function(domContext) {
domContext = domContext || gridDomNode;
var controls = $('[data-icinga-grid-controls]', domContext);
if (controls.length > 0) {
return $('form', controls);
} else {
return $('form', domContext).filter(function () {
return $(this).parentsUntil(domContext, 'table').length === 0;
});
}
};
/**
* Detect and select the dom of all tables displaying content for this mainDetailGrid component
*
* The table can either explicitly tagged with the 'data-icinga-grid-content' attribute, if not every table
* underneath the components root dom will be used
*
* @param {jQuery|null} domContext The context to use as the root node for matching, if null
* the component node given in the constructor is used
*
* @returns {jQuery} A selector result with all tables displaying information in the
* grid
*/
var determineContentTable = function(domContext) {
domContext = domContext || gridDomNode;
var maindetail = $('[data-icinga-grid-content]', domContext);
if (maindetail.length > 0) {
return maindetail;
} else {
return $('table', domContext);
}
};
/**
* Register the row links of tables using the first link found in the table (no matter if visible or not)
*
* Row level links can only be realized via JavaScript, so every row should provide additional links for
* Users that don't have javascript enabled
*
* @param {jQuery|null} domContext The rootnode to use for selecting rows or null to use contentNode
*/
this.registerTableLinks = function(domContext) {
domContext = domContext || contentNode;
$('tbody tr', domContext).on('click', function(ev) {
var targetEl = ev.target || ev.toElement || ev.relatedTarget;
if (targetEl.nodeName.toLowerCase() === "a") {
// test if the URL is on the current server, if not open it directly
if(Container.isExternalLink($(targetEl).attr('href'))) {
return true;
}
}
Container.getDetailContainer().replaceDomFromUrl($('a', this).attr('href'));
if (!ev.ctrlKey && !ev.metaKey) {
$('tr', $(this).parent()).removeClass('active');
}
$(this).addClass('active');
return false;
});
};
/**
* Register submit handler for the form controls (sorting, filtering, etc). Reloading happens in the
* current container
*/
this.registerControls = function() {
controlForms.on('submit', function(evt) {
var container = (new Container(this));
var form = $(this);
var url = URI(container.getContainerHref());
url.search(URI.parseQuery(form.serialize()));
container.replaceDomFromUrl(url.href());
evt.preventDefault();
evt.stopPropagation();
return false;
});
$('.pagination li a', contentNode.parent()).on('click', function(ev) {
var container = (new Container(this));
Icinga.replaceBodyFromUrl(
container.updateContainerHref($(this).attr('href'))
);
ev.preventDefault();
ev.stopPropagation();
return false;
});
};
var getSelectedRows = function() {
return $('a[href="' + Container.getDetailContainer().getContainerHref() + '"]', determineContentTable()).
parentsUntil('table', 'tr');
};
/**
* Synchronize the current selection with the url displayed in the detail box
*/
this.syncSelectionWithDetail = function() {
$('tr', contentNode).removeClass('active');
getSelectedRows().addClass('active');
};
/**
* Register listener for history changes in the detail box
*/
this.registerHistoryChanges = function() {
Container.getDetailContainer().registerOnUpdate(this.syncSelectionWithDetail.bind(this));
};
/**
* Create this component, setup listeners and behaviour
*/
this.construct = function(target) {
this.container = new Container(target);
controlForms = determineControlForms();
contentNode = determineContentTable();
this.registerControls();
this.registerTableLinks();
this.registerHistoryChanges();
};
this.construct(gridDomNode);
};
});

View File

@ -29,9 +29,10 @@
define([
'jquery',
'logging',
'icinga/util/async',
'icinga/componentLoader'
], function ($, log, async,components) {
'icinga/componentLoader',
'components/app/container',
'URIjs/URI'
], function ($, log, components, Container, URI) {
'use strict';
/**
@ -39,16 +40,78 @@ define([
*/
var Icinga = function() {
var ignoreHistoryChanges = false;
var initialize = function () {
components.load();
ignoreHistoryChanges = true;
registerGenericHistoryHandler();
ignoreHistoryChanges = false;
log.debug("Initialization finished");
};
/**
* Register handler for handling the history state generically
*
*/
var registerGenericHistoryHandler = function() {
var lastUrl = URI(window.location.href);
History.Adapter.bind(window, 'popstate', function() {
if (ignoreHistoryChanges) {
return;
}
gotoUrl(History.getState().url);
lastUrl = URI(window.location.href);
});
};
var gotoUrl = function(href) {
if (typeof document.body.pending !== 'undefined') {
document.body.pending.abort();
}
if (typeof href === 'string') {
href = URI(href);
}
document.body.pending = $.ajax({
success: function(domNodes) {
$('body').empty().append(jQuery.parseHTML(domNodes));
ignoreHistoryChanges = true;
History.pushState({}, document.title, href.href());
ignoreHistoryChanges = false;
components.load();
},
url: href.href()
});
return false;
};
if (Modernizr.history) {
$(document.body).on('click', '#icinganavigation', function(ev) {
var targetEl = ev.target || ev.toElement || ev.relatedTarget;
if (targetEl.tagName.toLowerCase() !== 'a') {
return true;
}
var href = $(targetEl).attr('href');
if (Container.isExternalLink(href)) {
return true;
}
ev.preventDefault();
ev.stopPropagation();
gotoUrl(href);
return false;
});
}
$(document).ready(initialize.bind(this));
return {
components: components
};
Container.setIcinga(this);
this.components = components;
this.replaceBodyFromUrl = gotoUrl;
};
return new Icinga();
});

View File

@ -1,199 +0,0 @@
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
/*global Icinga:false define:false require:false base_url:false console:false */
(function() {
"use strict";
var asyncMgrInstance = null;
define(['logging','jquery'],function(log,$,containerMgr) {
var headerListeners = {};
var pending = {
};
var encodeForURL = function(param) {
return encodeURIComponent(param);
};
var getCurrentGETParameters = function() {
var currentGET = window.location.search.substring(1).split("&");
var params = {};
if(currentGET.length > 0) {
$.each(currentGET, function(idx, elem) {
var keyVal = elem.split("=");
params[keyVal[0]] = encodeForURL(keyVal[1]);
});
}
return params;
}
;
var pushGet = function(param, value, url) {
url = url || (window.location.origin+window.location.pathname);
var params = getCurrentGETParameters();
params[param] = encodeForURL(value);
var search = "?";
for (var name in params) {
if (name === "" || typeof params[name] == "undefined") {
continue;
}
if (search != "?")
search += "&";
search += name+"="+params[name];
}
return url+search+"#"+window.location.hash;
};
var getDOMForDestination = function(destination) {
var target = destination;
if (typeof destination === "string") {
target = containerMgr.getContainer(destination)[0];
} else if(typeof destination.context !== "undefined") {
target = destination[0];
}
return target;
};
var applyHeaderListeners = function(headers) {
for (var header in headerListeners) {
if (headers.getResponseHeader(header) === null) {
// see if the browser/server converts headers to lowercase
if (headers.getResponseHeader(header.toLowerCase()) === null) {
continue;
}
header = header.toLowerCase();
}
var value = headers.getResponseHeader(header);
var listeners = headerListeners[header];
for (var i=0;i<listeners.length;i++) {
listeners[i].fn.apply(listeners[i].scope, [value, header, headers]);
}
}
};
var handleResponse = function(html, status, response) {
applyHeaderListeners(response);
if(this.destination) {
containerMgr.updateContainer(this.destination,html,this);
} else {
// tbd
// containerMgr.createPopupContainer(html,this);
}
};
var handleFailure = function(result,error) {
if(error === "abort") {
return;
}
log.error("Error loading resource",error,arguments);
if(this.destination) {
containerMgr.updateContainer(this.destination,result.responseText,this);
}
};
var isParent = function(dom,parentToCheck) {
while(dom.parentNode) {
dom = dom.parentNode;
if(dom === parentToCheck) {
return true;
}
}
return false;
};
var CallInterface = function() {
this.__internalXHRImplementation = $.ajax;
this.clearPendingRequestsFor = function(destination) {
if(!$.isArray(pending)) {
pending = [];
return;
}
var resultset = [];
for(var x=0;x<pending.length;x++) {
var container = pending[x].DOM;
if(isParent(container,getDOMForDestination(destination))) {
pending[x].request.abort();
} else {
resultset.push(pending[x]);
}
}
pending = resultset;
};
this.createRequest = function(url,data) {
var req = this.__internalXHRImplementation({
type : data ? 'POST' : 'GET',
url : url,
data : data,
headers: { 'X-Icinga-Accept': 'text/html' }
});
req.url = url;
req.done(handleResponse.bind(req));
req.fail(handleFailure.bind(req));
return req;
};
this.loadToTarget = function(destination,url,data) {
if(destination) {
log.debug("Laoding to container", destination, url);
this.clearPendingRequestsFor(destination);
}
var req = this.createRequest(url,data);
if(destination) {
pending.push({
request: req,
DOM: getDOMForDestination(destination)
});
req.destination = destination;
}
if (destination == "icinga-main") {
history.pushState(data, document.title, url);
} else {
url = pushGet("c["+destination+"]", url);
history.pushState(data, document.title, url);
}
return req;
};
this.loadCSS = function(name) {
};
this.registerHeaderListener = function(header, fn, scope) {
headerListeners[header] = headerListeners[header] || [];
headerListeners[header].push({fn: fn, scope:scope});
};
};
return new CallInterface();
});
})();

View File

@ -1,10 +1,13 @@
requirejs.config({
'baseUrl': window.base_url + '/js',
'urlArgs': "bust=" + (new Date()).getTime(),
'paths': {
'jquery': 'vendor/jquery-1.8.3',
'jqueryPlugins': 'vendor/jqueryPlugins/',
'bootstrap': 'vendor/bootstrap/bootstrap.min',
'history': 'vendor/history',
'logging': 'icinga/util/logging',
'URIjs': 'vendor/uri',
'datetimepicker': 'vendor/bootstrap/datetimepicker.min'
},
'shim': {
@ -18,6 +21,7 @@ requirejs.config({
});
define(['jquery', 'history'], function ($) {
requirejs(['bootstrap'], function() {
requirejs(['datetimepicker']);
});
@ -27,4 +31,5 @@ define(['jquery', 'history'], function ($) {
window.jQuery = $;
window.Icinga = Icinga;
});
});

File diff suppressed because one or more lines are too long

185
public/js/vendor/uri/IPv6.js vendored Executable file
View File

@ -0,0 +1,185 @@
/*!
* URI.js - Mutating URLs
* IPv6 Support
*
* Version: 1.11.2
*
* Author: Rodney Rehm
* Web: http://medialize.github.com/URI.js/
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
(function (root, factory) {
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(factory);
} else {
// Browser globals (root is window)
root.IPv6 = factory(root);
}
}(this, function (root) {
"use strict";
/*
var _in = "fe80:0000:0000:0000:0204:61ff:fe9d:f156";
var _out = IPv6.best(_in);
var _expected = "fe80::204:61ff:fe9d:f156";
console.log(_in, _out, _expected, _out === _expected);
*/
// save current IPv6 variable, if any
var _IPv6 = root && root.IPv6;
function best(address) {
// based on:
// Javascript to test an IPv6 address for proper format, and to
// present the "best text representation" according to IETF Draft RFC at
// http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04
// 8 Feb 2010 Rich Brown, Dartware, LLC
// Please feel free to use this code as long as you provide a link to
// http://www.intermapper.com
// http://intermapper.com/support/tools/IPV6-Validator.aspx
// http://download.dartware.com/thirdparty/ipv6validator.js
var _address = address.toLowerCase();
var segments = _address.split(':');
var length = segments.length;
var total = 8;
// trim colons (:: or ::a:b:c… or …a:b:c::)
if (segments[0] === '' && segments[1] === '' && segments[2] === '') {
// must have been ::
// remove first two items
segments.shift();
segments.shift();
} else if (segments[0] === '' && segments[1] === '') {
// must have been ::xxxx
// remove the first item
segments.shift();
} else if (segments[length - 1] === '' && segments[length - 2] === '') {
// must have been xxxx::
segments.pop();
}
length = segments.length;
// adjust total segments for IPv4 trailer
if (segments[length - 1].indexOf('.') !== -1) {
// found a "." which means IPv4
total = 7;
}
// fill empty segments them with "0000"
var pos;
for (pos = 0; pos < length; pos++) {
if (segments[pos] === '') {
break;
}
}
if (pos < total) {
segments.splice(pos, 1, '0000');
while (segments.length < total) {
segments.splice(pos, 0, '0000');
}
length = segments.length;
}
// strip leading zeros
var _segments;
for (var i = 0; i < total; i++) {
_segments = segments[i].split("");
for (var j = 0; j < 3 ; j++) {
if (_segments[0] === '0' && _segments.length > 1) {
_segments.splice(0,1);
} else {
break;
}
}
segments[i] = _segments.join("");
}
// find longest sequence of zeroes and coalesce them into one segment
var best = -1;
var _best = 0;
var _current = 0;
var current = -1;
var inzeroes = false;
// i; already declared
for (i = 0; i < total; i++) {
if (inzeroes) {
if (segments[i] === '0') {
_current += 1;
} else {
inzeroes = false;
if (_current > _best) {
best = current;
_best = _current;
}
}
} else {
if (segments[i] == '0') {
inzeroes = true;
current = i;
_current = 1;
}
}
}
if (_current > _best) {
best = current;
_best = _current;
}
if (_best > 1) {
segments.splice(best, _best, "");
}
length = segments.length;
// assemble remaining segments
var result = '';
if (segments[0] === '') {
beststr = ":";
}
for (i = 0; i < length; i++) {
result += segments[i];
if (i === length - 1) {
break;
}
result += ':';
}
if (segments[length - 1] === '') {
result += ":";
}
return result;
};
function noConflict(){
if (root.IPv6 === this) {
root.IPv6 = _IPv6;
}
return this;
};
return {
best: best,
noConflict: noConflict
};
}));

220
public/js/vendor/uri/SecondLevelDomains.js vendored Executable file
View File

@ -0,0 +1,220 @@
/*!
* URI.js - Mutating URLs
* Second Level Domain (SLD) Support
*
* Version: 1.11.2
*
* Author: Rodney Rehm
* Web: http://medialize.github.com/URI.js/
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
(function (root, factory) {
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(factory);
} else {
// Browser globals (root is window)
root.SecondLevelDomains = factory(root);
}
}(this, function (root) {
"use strict";
// save current SecondLevelDomains variable, if any
var _SecondLevelDomains = root && root.SecondLevelDomains;
var hasOwn = Object.prototype.hasOwnProperty;
var SLD = {
// list of known Second Level Domains
// converted list of SLDs from https://github.com/gavingmiller/second-level-domains
// ----
// publicsuffix.org is more current and actually used by a couple of browsers internally.
// downside is it also contains domains like "dyndns.org" - which is fine for the security
// issues browser have to deal with (SOP for cookies, etc) - but is way overboard for URI.js
// ----
list: {
"ac":"com|gov|mil|net|org",
"ae":"ac|co|gov|mil|name|net|org|pro|sch",
"af":"com|edu|gov|net|org",
"al":"com|edu|gov|mil|net|org",
"ao":"co|ed|gv|it|og|pb",
"ar":"com|edu|gob|gov|int|mil|net|org|tur",
"at":"ac|co|gv|or",
"au":"asn|com|csiro|edu|gov|id|net|org",
"ba":"co|com|edu|gov|mil|net|org|rs|unbi|unmo|unsa|untz|unze",
"bb":"biz|co|com|edu|gov|info|net|org|store|tv",
"bh":"biz|cc|com|edu|gov|info|net|org",
"bn":"com|edu|gov|net|org",
"bo":"com|edu|gob|gov|int|mil|net|org|tv",
"br":"adm|adv|agr|am|arq|art|ato|b|bio|blog|bmd|cim|cng|cnt|com|coop|ecn|edu|eng|esp|etc|eti|far|flog|fm|fnd|fot|fst|g12|ggf|gov|imb|ind|inf|jor|jus|lel|mat|med|mil|mus|net|nom|not|ntr|odo|org|ppg|pro|psc|psi|qsl|rec|slg|srv|tmp|trd|tur|tv|vet|vlog|wiki|zlg",
"bs":"com|edu|gov|net|org",
"bz":"du|et|om|ov|rg",
"ca":"ab|bc|mb|nb|nf|nl|ns|nt|nu|on|pe|qc|sk|yk",
"ck":"biz|co|edu|gen|gov|info|net|org",
"cn":"ac|ah|bj|com|cq|edu|fj|gd|gov|gs|gx|gz|ha|hb|he|hi|hl|hn|jl|js|jx|ln|mil|net|nm|nx|org|qh|sc|sd|sh|sn|sx|tj|tw|xj|xz|yn|zj",
"co":"com|edu|gov|mil|net|nom|org",
"cr":"ac|c|co|ed|fi|go|or|sa",
"cy":"ac|biz|com|ekloges|gov|ltd|name|net|org|parliament|press|pro|tm",
"do":"art|com|edu|gob|gov|mil|net|org|sld|web",
"dz":"art|asso|com|edu|gov|net|org|pol",
"ec":"com|edu|fin|gov|info|med|mil|net|org|pro",
"eg":"com|edu|eun|gov|mil|name|net|org|sci",
"er":"com|edu|gov|ind|mil|net|org|rochest|w",
"es":"com|edu|gob|nom|org",
"et":"biz|com|edu|gov|info|name|net|org",
"fj":"ac|biz|com|info|mil|name|net|org|pro",
"fk":"ac|co|gov|net|nom|org",
"fr":"asso|com|f|gouv|nom|prd|presse|tm",
"gg":"co|net|org",
"gh":"com|edu|gov|mil|org",
"gn":"ac|com|gov|net|org",
"gr":"com|edu|gov|mil|net|org",
"gt":"com|edu|gob|ind|mil|net|org",
"gu":"com|edu|gov|net|org",
"hk":"com|edu|gov|idv|net|org",
"id":"ac|co|go|mil|net|or|sch|web",
"il":"ac|co|gov|idf|k12|muni|net|org",
"in":"ac|co|edu|ernet|firm|gen|gov|i|ind|mil|net|nic|org|res",
"iq":"com|edu|gov|i|mil|net|org",
"ir":"ac|co|dnssec|gov|i|id|net|org|sch",
"it":"edu|gov",
"je":"co|net|org",
"jo":"com|edu|gov|mil|name|net|org|sch",
"jp":"ac|ad|co|ed|go|gr|lg|ne|or",
"ke":"ac|co|go|info|me|mobi|ne|or|sc",
"kh":"com|edu|gov|mil|net|org|per",
"ki":"biz|com|de|edu|gov|info|mob|net|org|tel",
"km":"asso|com|coop|edu|gouv|k|medecin|mil|nom|notaires|pharmaciens|presse|tm|veterinaire",
"kn":"edu|gov|net|org",
"kr":"ac|busan|chungbuk|chungnam|co|daegu|daejeon|es|gangwon|go|gwangju|gyeongbuk|gyeonggi|gyeongnam|hs|incheon|jeju|jeonbuk|jeonnam|k|kg|mil|ms|ne|or|pe|re|sc|seoul|ulsan",
"kw":"com|edu|gov|net|org",
"ky":"com|edu|gov|net|org",
"kz":"com|edu|gov|mil|net|org",
"lb":"com|edu|gov|net|org",
"lk":"assn|com|edu|gov|grp|hotel|int|ltd|net|ngo|org|sch|soc|web",
"lr":"com|edu|gov|net|org",
"lv":"asn|com|conf|edu|gov|id|mil|net|org",
"ly":"com|edu|gov|id|med|net|org|plc|sch",
"ma":"ac|co|gov|m|net|org|press",
"mc":"asso|tm",
"me":"ac|co|edu|gov|its|net|org|priv",
"mg":"com|edu|gov|mil|nom|org|prd|tm",
"mk":"com|edu|gov|inf|name|net|org|pro",
"ml":"com|edu|gov|net|org|presse",
"mn":"edu|gov|org",
"mo":"com|edu|gov|net|org",
"mt":"com|edu|gov|net|org",
"mv":"aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro",
"mw":"ac|co|com|coop|edu|gov|int|museum|net|org",
"mx":"com|edu|gob|net|org",
"my":"com|edu|gov|mil|name|net|org|sch",
"nf":"arts|com|firm|info|net|other|per|rec|store|web",
"ng":"biz|com|edu|gov|mil|mobi|name|net|org|sch",
"ni":"ac|co|com|edu|gob|mil|net|nom|org",
"np":"com|edu|gov|mil|net|org",
"nr":"biz|com|edu|gov|info|net|org",
"om":"ac|biz|co|com|edu|gov|med|mil|museum|net|org|pro|sch",
"pe":"com|edu|gob|mil|net|nom|org|sld",
"ph":"com|edu|gov|i|mil|net|ngo|org",
"pk":"biz|com|edu|fam|gob|gok|gon|gop|gos|gov|net|org|web",
"pl":"art|bialystok|biz|com|edu|gda|gdansk|gorzow|gov|info|katowice|krakow|lodz|lublin|mil|net|ngo|olsztyn|org|poznan|pwr|radom|slupsk|szczecin|torun|warszawa|waw|wroc|wroclaw|zgora",
"pr":"ac|biz|com|edu|est|gov|info|isla|name|net|org|pro|prof",
"ps":"com|edu|gov|net|org|plo|sec",
"pw":"belau|co|ed|go|ne|or",
"ro":"arts|com|firm|info|nom|nt|org|rec|store|tm|www",
"rs":"ac|co|edu|gov|in|org",
"sb":"com|edu|gov|net|org",
"sc":"com|edu|gov|net|org",
"sh":"co|com|edu|gov|net|nom|org",
"sl":"com|edu|gov|net|org",
"st":"co|com|consulado|edu|embaixada|gov|mil|net|org|principe|saotome|store",
"sv":"com|edu|gob|org|red",
"sz":"ac|co|org",
"tr":"av|bbs|bel|biz|com|dr|edu|gen|gov|info|k12|name|net|org|pol|tel|tsk|tv|web",
"tt":"aero|biz|cat|co|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel",
"tw":"club|com|ebiz|edu|game|gov|idv|mil|net|org",
"mu":"ac|co|com|gov|net|or|org",
"mz":"ac|co|edu|gov|org",
"na":"co|com",
"nz":"ac|co|cri|geek|gen|govt|health|iwi|maori|mil|net|org|parliament|school",
"pa":"abo|ac|com|edu|gob|ing|med|net|nom|org|sld",
"pt":"com|edu|gov|int|net|nome|org|publ",
"py":"com|edu|gov|mil|net|org",
"qa":"com|edu|gov|mil|net|org",
"re":"asso|com|nom",
"ru":"ac|adygeya|altai|amur|arkhangelsk|astrakhan|bashkiria|belgorod|bir|bryansk|buryatia|cbg|chel|chelyabinsk|chita|chukotka|chuvashia|com|dagestan|e-burg|edu|gov|grozny|int|irkutsk|ivanovo|izhevsk|jar|joshkar-ola|kalmykia|kaluga|kamchatka|karelia|kazan|kchr|kemerovo|khabarovsk|khakassia|khv|kirov|koenig|komi|kostroma|kranoyarsk|kuban|kurgan|kursk|lipetsk|magadan|mari|mari-el|marine|mil|mordovia|mosreg|msk|murmansk|nalchik|net|nnov|nov|novosibirsk|nsk|omsk|orenburg|org|oryol|penza|perm|pp|pskov|ptz|rnd|ryazan|sakhalin|samara|saratov|simbirsk|smolensk|spb|stavropol|stv|surgut|tambov|tatarstan|tom|tomsk|tsaritsyn|tsk|tula|tuva|tver|tyumen|udm|udmurtia|ulan-ude|vladikavkaz|vladimir|vladivostok|volgograd|vologda|voronezh|vrn|vyatka|yakutia|yamal|yekaterinburg|yuzhno-sakhalinsk",
"rw":"ac|co|com|edu|gouv|gov|int|mil|net",
"sa":"com|edu|gov|med|net|org|pub|sch",
"sd":"com|edu|gov|info|med|net|org|tv",
"se":"a|ac|b|bd|c|d|e|f|g|h|i|k|l|m|n|o|org|p|parti|pp|press|r|s|t|tm|u|w|x|y|z",
"sg":"com|edu|gov|idn|net|org|per",
"sn":"art|com|edu|gouv|org|perso|univ",
"sy":"com|edu|gov|mil|net|news|org",
"th":"ac|co|go|in|mi|net|or",
"tj":"ac|biz|co|com|edu|go|gov|info|int|mil|name|net|nic|org|test|web",
"tn":"agrinet|com|defense|edunet|ens|fin|gov|ind|info|intl|mincom|nat|net|org|perso|rnrt|rns|rnu|tourism",
"tz":"ac|co|go|ne|or",
"ua":"biz|cherkassy|chernigov|chernovtsy|ck|cn|co|com|crimea|cv|dn|dnepropetrovsk|donetsk|dp|edu|gov|if|in|ivano-frankivsk|kh|kharkov|kherson|khmelnitskiy|kiev|kirovograd|km|kr|ks|kv|lg|lugansk|lutsk|lviv|me|mk|net|nikolaev|od|odessa|org|pl|poltava|pp|rovno|rv|sebastopol|sumy|te|ternopil|uzhgorod|vinnica|vn|zaporizhzhe|zhitomir|zp|zt",
"ug":"ac|co|go|ne|or|org|sc",
"uk":"ac|bl|british-library|co|cym|gov|govt|icnet|jet|lea|ltd|me|mil|mod|national-library-scotland|nel|net|nhs|nic|nls|org|orgn|parliament|plc|police|sch|scot|soc",
"us":"dni|fed|isa|kids|nsn",
"uy":"com|edu|gub|mil|net|org",
"ve":"co|com|edu|gob|info|mil|net|org|web",
"vi":"co|com|k12|net|org",
"vn":"ac|biz|com|edu|gov|health|info|int|name|net|org|pro",
"ye":"co|com|gov|ltd|me|net|org|plc",
"yu":"ac|co|edu|gov|org",
"za":"ac|agric|alt|bourse|city|co|cybernet|db|edu|gov|grondar|iaccess|imt|inca|landesign|law|mil|net|ngo|nis|nom|olivetti|org|pix|school|tm|web",
"zm":"ac|co|com|edu|gov|net|org|sch"
},
// SLD expression for each TLD
//expressions: {},
// SLD expression for all TLDs
has_expression: null,
is_expression: null,
// validate domain is a known SLD
has: function(domain) {
return !!domain.match(SLD.has_expression);
},
is: function(domain) {
return !!domain.match(SLD.is_expression);
},
get: function(domain) {
var t = domain.match(SLD.has_expression);
return t && t[1] || null;
},
noConflict: function(){
if (root.SecondLevelDomains === this) {
root.SecondLevelDomains = _SecondLevelDomains;
}
return this;
},
init: function() {
var t = '';
for (var tld in SLD.list) {
if (!hasOwn.call(SLD.list, tld)) {
continue;
}
var expression = '(' + SLD.list[tld] + ')\.' + tld;
//SLD.expressions[tld] = new RegExp('\.' + expression + '$', 'i');
t += '|(' + expression + ')';
}
SLD.has_expression = new RegExp('\\.(' + t.substr(1) + ')$', 'i');
SLD.is_expression = new RegExp('^(' + t.substr(1) + ')$', 'i');
}
};
SLD.init();
return SLD;
}));

103
public/js/vendor/uri/URI.fragmentQuery.js vendored Executable file
View File

@ -0,0 +1,103 @@
/*
* Extending URI.js for fragment abuse
*/
// --------------------------------------------------------------------------------
// EXAMPLE: storing application/x-www-form-urlencoded data in the fragment
// possibly helpful for Google's hashbangs
// see http://code.google.com/web/ajaxcrawling/
// --------------------------------------------------------------------------------
// Note: make sure this is the last file loaded!
// USAGE:
// var uri = URI("http://example.org/#?foo=bar");
// uri.fragment(true) === {foo: "bar"};
// uri.fragment({bar: "foo"});
// uri.toString() === "http://example.org/#?bar=foo";
// uri.addFragment("name", "value");
// uri.toString() === "http://example.org/#?bar=foo&name=value";
// uri.removeFragment("name");
// uri.toString() === "http://example.org/#?bar=foo";
(function (root, factory) {
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory(require('./URI'));
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['./URI'], factory);
} else {
// Browser globals (root is window)
factory(root.URI);
}
}(this, function (URI) {
"use strict";
var p = URI.prototype;
// old fragment handler we need to wrap
var f = p.fragment;
// make fragmentPrefix configurable
URI.fragmentPrefix = '?';
var _parts = URI._parts;
URI._parts = function() {
var parts = _parts();
parts.fragmentPrefix = URI.fragmentPrefix;
return parts;
};
p.fragmentPrefix = function(v) {
this._parts.fragmentPrefix = v;
return this;
};
// add fragment(true) and fragment({key: value}) signatures
p.fragment = function(v, build) {
var prefix = this._parts.fragmentPrefix;
var fragment = this._parts.fragment || "";
if (v === true) {
if (fragment.substring(0, prefix.length) !== prefix) {
return {};
}
return URI.parseQuery(fragment.substring(prefix.length));
} else if (v !== undefined && typeof v !== "string") {
this._parts.fragment = prefix + URI.buildQuery(v);
this.build(!build);
return this;
} else {
return f.call(this, v, build);
}
};
p.addFragment = function(name, value, build) {
var prefix = this._parts.fragmentPrefix;
var data = URI.parseQuery((this._parts.fragment || "").substring(prefix.length));
URI.addQuery(data, name, value);
this._parts.fragment = prefix + URI.buildQuery(data);
if (typeof name !== "string") {
build = value;
}
this.build(!build);
return this;
};
p.removeFragment = function(name, value, build) {
var prefix = this._parts.fragmentPrefix;
var data = URI.parseQuery((this._parts.fragment || "").substring(prefix.length));
URI.removeQuery(data, name, value);
this._parts.fragment = prefix + URI.buildQuery(data);
if (typeof name !== "string") {
build = value;
}
this.build(!build);
return this;
};
p.addHash = p.addFragment;
p.removeHash = p.removeFragment;
// extending existing object rather than defining something new
return {};
}));

96
public/js/vendor/uri/URI.fragmentURI.js vendored Executable file
View File

@ -0,0 +1,96 @@
/*
* Extending URI.js for fragment abuse
*/
// --------------------------------------------------------------------------------
// EXAMPLE: storing a relative URL in the fragment ("FragmentURI")
// possibly helpful when working with backbone.js or sammy.js
// inspired by https://github.com/medialize/URI.js/pull/2
// --------------------------------------------------------------------------------
// Note: make sure this is the last file loaded!
// USAGE:
// var uri = URI("http://example.org/#!/foo/bar/baz.html");
// var furi = uri.fragment(true);
// furi.pathname() === '/foo/bar/baz.html';
// furi.pathname('/hello.html');
// uri.toString() === "http://example.org/#!/hello.html"
(function (root, factory) {
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory(require('./URI'));
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['./URI'], factory);
} else {
// Browser globals (root is window)
factory(root.URI);
}
}(this, function (URI) {
"use strict";
var p = URI.prototype;
// old handlers we need to wrap
var f = p.fragment;
var b = p.build;
// make fragmentPrefix configurable
URI.fragmentPrefix = '!';
var _parts = URI._parts;
URI._parts = function() {
var parts = _parts();
parts.fragmentPrefix = URI.fragmentPrefix;
return parts;
};
p.fragmentPrefix = function(v) {
this._parts.fragmentPrefix = v;
return this;
};
// add fragment(true) and fragment(URI) signatures
p.fragment = function(v, build) {
var prefix = this._parts.fragmentPrefix;
var fragment = this._parts.fragment || "";
var furi;
if (v === true) {
if (fragment.substring(0, prefix.length) !== prefix) {
furi = URI("");
} else {
furi = new URI(fragment.substring(prefix.length));
}
this._fragmentURI = furi;
furi._parentURI = this;
return furi;
} else if (v !== undefined && typeof v !== "string") {
this._fragmentURI = v;
v._parentURI = v;
this._parts.fragment = prefix + v.toString();
this.build(!build);
return this;
} else if (typeof v === "string") {
this._fragmentURI = undefined;
}
return f.call(this, v, build);
};
// make .build() of the actual URI aware of the FragmentURI
p.build = function(deferBuild) {
var t = b.call(this, deferBuild);
if (deferBuild !== false && this._parentURI) {
// update the parent
this._parentURI.fragment(this);
}
return t;
};
// extending existing object rather than defining something new
return {};
}));

1938
public/js/vendor/uri/URI.js vendored Executable file

File diff suppressed because it is too large Load Diff

81
public/js/vendor/uri/URI.min.js vendored Executable file
View File

@ -0,0 +1,81 @@
/*! URI.js v1.11.2 http://medialize.github.com/URI.js/ */
/* build contains: IPv6.js, punycode.js, SecondLevelDomains.js, URI.js, URITemplate.js */
(function(f,l){"object"===typeof exports?module.exports=l():"function"===typeof define&&define.amd?define(l):f.IPv6=l(f)})(this,function(f){var l=f&&f.IPv6;return{best:function(h){h=h.toLowerCase().split(":");var f=h.length,d=8;""===h[0]&&""===h[1]&&""===h[2]?(h.shift(),h.shift()):""===h[0]&&""===h[1]?h.shift():""===h[f-1]&&""===h[f-2]&&h.pop();f=h.length;-1!==h[f-1].indexOf(".")&&(d=7);var q;for(q=0;q<f&&""!==h[q];q++);if(q<d)for(h.splice(q,1,"0000");h.length<d;)h.splice(q,0,"0000");for(q=0;q<d;q++){for(var f=
h[q].split(""),l=0;3>l;l++)if("0"===f[0]&&1<f.length)f.splice(0,1);else break;h[q]=f.join("")}var f=-1,n=l=0,e=-1,u=!1;for(q=0;q<d;q++)u?"0"===h[q]?n+=1:(u=!1,n>l&&(f=e,l=n)):"0"==h[q]&&(u=!0,e=q,n=1);n>l&&(f=e,l=n);1<l&&h.splice(f,l,"");f=h.length;d="";""===h[0]&&(beststr=":");for(q=0;q<f;q++){d+=h[q];if(q===f-1)break;d+=":"}""===h[f-1]&&(d+=":");return d},noConflict:function(){f.IPv6===this&&(f.IPv6=l);return this}}});
(function(f){function l(a){throw RangeError(c[a]);}function h(a,b){for(var c=a.length;c--;)a[c]=b(a[c]);return a}function p(a){for(var b=[],c=0,d=a.length,k,g;c<d;)k=a.charCodeAt(c++),55296<=k&&56319>=k&&c<d?(g=a.charCodeAt(c++),56320==(g&64512)?b.push(((k&1023)<<10)+(g&1023)+65536):(b.push(k),c--)):b.push(k);return b}function d(a){return h(a,function(a){var b="";65535<a&&(a-=65536,b+=x(a>>>10&1023|55296),a=56320|a&1023);return b+=x(a)}).join("")}function q(a,b,c){var d=0;a=c?B(a/y):a>>1;for(a+=B(a/
b);a>k*m>>1;d+=g)a=B(a/k);return B(d+(k+1)*a/(a+s))}function A(a){var b=[],c=a.length,k,e=0,f=D,h=w,x,u,v,t,s;x=a.lastIndexOf(E);0>x&&(x=0);for(u=0;u<x;++u)128<=a.charCodeAt(u)&&l("not-basic"),b.push(a.charCodeAt(u));for(x=0<x?x+1:0;x<c;){u=e;k=1;for(v=g;;v+=g){x>=c&&l("invalid-input");t=a.charCodeAt(x++);t=10>t-48?t-22:26>t-65?t-65:26>t-97?t-97:g;(t>=g||t>B((C-e)/k))&&l("overflow");e+=t*k;s=v<=h?r:v>=h+m?m:v-h;if(t<s)break;t=g-s;k>B(C/t)&&l("overflow");k*=t}k=b.length+1;h=q(e-u,k,0==u);B(e/k)>C-
f&&l("overflow");f+=B(e/k);e%=k;b.splice(e++,0,f)}return d(b)}function n(a){var b,c,d,k,e,f,h,u,t,v=[],s,z,n;a=p(a);s=a.length;b=D;c=0;e=w;for(f=0;f<s;++f)t=a[f],128>t&&v.push(x(t));for((d=k=v.length)&&v.push(E);d<s;){h=C;for(f=0;f<s;++f)t=a[f],t>=b&&t<h&&(h=t);z=d+1;h-b>B((C-c)/z)&&l("overflow");c+=(h-b)*z;b=h;for(f=0;f<s;++f)if(t=a[f],t<b&&++c>C&&l("overflow"),t==b){u=c;for(h=g;;h+=g){t=h<=e?r:h>=e+m?m:h-e;if(u<t)break;n=u-t;u=g-t;v.push(x(t+n%u+22+75*(26>t+n%u)-0));u=B(n/u)}v.push(x(u+22+75*(26>
u)-0));e=q(c,z,d==k);c=0;++d}++c;++b}return v.join("")}var e="object"==typeof exports&&exports,u="object"==typeof module&&module&&module.exports==e&&module,v="object"==typeof global&&global;if(v.global===v||v.window===v)f=v;var z,C=2147483647,g=36,r=1,m=26,s=38,y=700,w=72,D=128,E="-",F=/^xn--/,a=/[^ -~]/,b=/\x2E|\u3002|\uFF0E|\uFF61/g,c={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},k=g-r,B=
Math.floor,x=String.fromCharCode,t;z={version:"1.2.3",ucs2:{decode:p,encode:d},decode:A,encode:n,toASCII:function(c){return h(c.split(b),function(b){return a.test(b)?"xn--"+n(b):b}).join(".")},toUnicode:function(a){return h(a.split(b),function(a){return F.test(a)?A(a.slice(4).toLowerCase()):a}).join(".")}};if("function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return z});else if(e&&!e.nodeType)if(u)u.exports=z;else for(t in z)z.hasOwnProperty(t)&&(e[t]=z[t]);else f.punycode=
z})(this);
(function(f,l){"object"===typeof exports?module.exports=l():"function"===typeof define&&define.amd?define(l):f.SecondLevelDomains=l(f)})(this,function(f){var l=f&&f.SecondLevelDomains,h=Object.prototype.hasOwnProperty,p={list:{ac:"com|gov|mil|net|org",ae:"ac|co|gov|mil|name|net|org|pro|sch",af:"com|edu|gov|net|org",al:"com|edu|gov|mil|net|org",ao:"co|ed|gv|it|og|pb",ar:"com|edu|gob|gov|int|mil|net|org|tur",at:"ac|co|gv|or",au:"asn|com|csiro|edu|gov|id|net|org",ba:"co|com|edu|gov|mil|net|org|rs|unbi|unmo|unsa|untz|unze",bb:"biz|co|com|edu|gov|info|net|org|store|tv",
bh:"biz|cc|com|edu|gov|info|net|org",bn:"com|edu|gov|net|org",bo:"com|edu|gob|gov|int|mil|net|org|tv",br:"adm|adv|agr|am|arq|art|ato|b|bio|blog|bmd|cim|cng|cnt|com|coop|ecn|edu|eng|esp|etc|eti|far|flog|fm|fnd|fot|fst|g12|ggf|gov|imb|ind|inf|jor|jus|lel|mat|med|mil|mus|net|nom|not|ntr|odo|org|ppg|pro|psc|psi|qsl|rec|slg|srv|tmp|trd|tur|tv|vet|vlog|wiki|zlg",bs:"com|edu|gov|net|org",bz:"du|et|om|ov|rg",ca:"ab|bc|mb|nb|nf|nl|ns|nt|nu|on|pe|qc|sk|yk",ck:"biz|co|edu|gen|gov|info|net|org",cn:"ac|ah|bj|com|cq|edu|fj|gd|gov|gs|gx|gz|ha|hb|he|hi|hl|hn|jl|js|jx|ln|mil|net|nm|nx|org|qh|sc|sd|sh|sn|sx|tj|tw|xj|xz|yn|zj",
co:"com|edu|gov|mil|net|nom|org",cr:"ac|c|co|ed|fi|go|or|sa",cy:"ac|biz|com|ekloges|gov|ltd|name|net|org|parliament|press|pro|tm","do":"art|com|edu|gob|gov|mil|net|org|sld|web",dz:"art|asso|com|edu|gov|net|org|pol",ec:"com|edu|fin|gov|info|med|mil|net|org|pro",eg:"com|edu|eun|gov|mil|name|net|org|sci",er:"com|edu|gov|ind|mil|net|org|rochest|w",es:"com|edu|gob|nom|org",et:"biz|com|edu|gov|info|name|net|org",fj:"ac|biz|com|info|mil|name|net|org|pro",fk:"ac|co|gov|net|nom|org",fr:"asso|com|f|gouv|nom|prd|presse|tm",
gg:"co|net|org",gh:"com|edu|gov|mil|org",gn:"ac|com|gov|net|org",gr:"com|edu|gov|mil|net|org",gt:"com|edu|gob|ind|mil|net|org",gu:"com|edu|gov|net|org",hk:"com|edu|gov|idv|net|org",id:"ac|co|go|mil|net|or|sch|web",il:"ac|co|gov|idf|k12|muni|net|org","in":"ac|co|edu|ernet|firm|gen|gov|i|ind|mil|net|nic|org|res",iq:"com|edu|gov|i|mil|net|org",ir:"ac|co|dnssec|gov|i|id|net|org|sch",it:"edu|gov",je:"co|net|org",jo:"com|edu|gov|mil|name|net|org|sch",jp:"ac|ad|co|ed|go|gr|lg|ne|or",ke:"ac|co|go|info|me|mobi|ne|or|sc",
kh:"com|edu|gov|mil|net|org|per",ki:"biz|com|de|edu|gov|info|mob|net|org|tel",km:"asso|com|coop|edu|gouv|k|medecin|mil|nom|notaires|pharmaciens|presse|tm|veterinaire",kn:"edu|gov|net|org",kr:"ac|busan|chungbuk|chungnam|co|daegu|daejeon|es|gangwon|go|gwangju|gyeongbuk|gyeonggi|gyeongnam|hs|incheon|jeju|jeonbuk|jeonnam|k|kg|mil|ms|ne|or|pe|re|sc|seoul|ulsan",kw:"com|edu|gov|net|org",ky:"com|edu|gov|net|org",kz:"com|edu|gov|mil|net|org",lb:"com|edu|gov|net|org",lk:"assn|com|edu|gov|grp|hotel|int|ltd|net|ngo|org|sch|soc|web",
lr:"com|edu|gov|net|org",lv:"asn|com|conf|edu|gov|id|mil|net|org",ly:"com|edu|gov|id|med|net|org|plc|sch",ma:"ac|co|gov|m|net|org|press",mc:"asso|tm",me:"ac|co|edu|gov|its|net|org|priv",mg:"com|edu|gov|mil|nom|org|prd|tm",mk:"com|edu|gov|inf|name|net|org|pro",ml:"com|edu|gov|net|org|presse",mn:"edu|gov|org",mo:"com|edu|gov|net|org",mt:"com|edu|gov|net|org",mv:"aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro",mw:"ac|co|com|coop|edu|gov|int|museum|net|org",mx:"com|edu|gob|net|org",my:"com|edu|gov|mil|name|net|org|sch",
nf:"arts|com|firm|info|net|other|per|rec|store|web",ng:"biz|com|edu|gov|mil|mobi|name|net|org|sch",ni:"ac|co|com|edu|gob|mil|net|nom|org",np:"com|edu|gov|mil|net|org",nr:"biz|com|edu|gov|info|net|org",om:"ac|biz|co|com|edu|gov|med|mil|museum|net|org|pro|sch",pe:"com|edu|gob|mil|net|nom|org|sld",ph:"com|edu|gov|i|mil|net|ngo|org",pk:"biz|com|edu|fam|gob|gok|gon|gop|gos|gov|net|org|web",pl:"art|bialystok|biz|com|edu|gda|gdansk|gorzow|gov|info|katowice|krakow|lodz|lublin|mil|net|ngo|olsztyn|org|poznan|pwr|radom|slupsk|szczecin|torun|warszawa|waw|wroc|wroclaw|zgora",
pr:"ac|biz|com|edu|est|gov|info|isla|name|net|org|pro|prof",ps:"com|edu|gov|net|org|plo|sec",pw:"belau|co|ed|go|ne|or",ro:"arts|com|firm|info|nom|nt|org|rec|store|tm|www",rs:"ac|co|edu|gov|in|org",sb:"com|edu|gov|net|org",sc:"com|edu|gov|net|org",sh:"co|com|edu|gov|net|nom|org",sl:"com|edu|gov|net|org",st:"co|com|consulado|edu|embaixada|gov|mil|net|org|principe|saotome|store",sv:"com|edu|gob|org|red",sz:"ac|co|org",tr:"av|bbs|bel|biz|com|dr|edu|gen|gov|info|k12|name|net|org|pol|tel|tsk|tv|web",tt:"aero|biz|cat|co|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel",
tw:"club|com|ebiz|edu|game|gov|idv|mil|net|org",mu:"ac|co|com|gov|net|or|org",mz:"ac|co|edu|gov|org",na:"co|com",nz:"ac|co|cri|geek|gen|govt|health|iwi|maori|mil|net|org|parliament|school",pa:"abo|ac|com|edu|gob|ing|med|net|nom|org|sld",pt:"com|edu|gov|int|net|nome|org|publ",py:"com|edu|gov|mil|net|org",qa:"com|edu|gov|mil|net|org",re:"asso|com|nom",ru:"ac|adygeya|altai|amur|arkhangelsk|astrakhan|bashkiria|belgorod|bir|bryansk|buryatia|cbg|chel|chelyabinsk|chita|chukotka|chuvashia|com|dagestan|e-burg|edu|gov|grozny|int|irkutsk|ivanovo|izhevsk|jar|joshkar-ola|kalmykia|kaluga|kamchatka|karelia|kazan|kchr|kemerovo|khabarovsk|khakassia|khv|kirov|koenig|komi|kostroma|kranoyarsk|kuban|kurgan|kursk|lipetsk|magadan|mari|mari-el|marine|mil|mordovia|mosreg|msk|murmansk|nalchik|net|nnov|nov|novosibirsk|nsk|omsk|orenburg|org|oryol|penza|perm|pp|pskov|ptz|rnd|ryazan|sakhalin|samara|saratov|simbirsk|smolensk|spb|stavropol|stv|surgut|tambov|tatarstan|tom|tomsk|tsaritsyn|tsk|tula|tuva|tver|tyumen|udm|udmurtia|ulan-ude|vladikavkaz|vladimir|vladivostok|volgograd|vologda|voronezh|vrn|vyatka|yakutia|yamal|yekaterinburg|yuzhno-sakhalinsk",
rw:"ac|co|com|edu|gouv|gov|int|mil|net",sa:"com|edu|gov|med|net|org|pub|sch",sd:"com|edu|gov|info|med|net|org|tv",se:"a|ac|b|bd|c|d|e|f|g|h|i|k|l|m|n|o|org|p|parti|pp|press|r|s|t|tm|u|w|x|y|z",sg:"com|edu|gov|idn|net|org|per",sn:"art|com|edu|gouv|org|perso|univ",sy:"com|edu|gov|mil|net|news|org",th:"ac|co|go|in|mi|net|or",tj:"ac|biz|co|com|edu|go|gov|info|int|mil|name|net|nic|org|test|web",tn:"agrinet|com|defense|edunet|ens|fin|gov|ind|info|intl|mincom|nat|net|org|perso|rnrt|rns|rnu|tourism",tz:"ac|co|go|ne|or",
ua:"biz|cherkassy|chernigov|chernovtsy|ck|cn|co|com|crimea|cv|dn|dnepropetrovsk|donetsk|dp|edu|gov|if|in|ivano-frankivsk|kh|kharkov|kherson|khmelnitskiy|kiev|kirovograd|km|kr|ks|kv|lg|lugansk|lutsk|lviv|me|mk|net|nikolaev|od|odessa|org|pl|poltava|pp|rovno|rv|sebastopol|sumy|te|ternopil|uzhgorod|vinnica|vn|zaporizhzhe|zhitomir|zp|zt",ug:"ac|co|go|ne|or|org|sc",uk:"ac|bl|british-library|co|cym|gov|govt|icnet|jet|lea|ltd|me|mil|mod|national-library-scotland|nel|net|nhs|nic|nls|org|orgn|parliament|plc|police|sch|scot|soc",
us:"dni|fed|isa|kids|nsn",uy:"com|edu|gub|mil|net|org",ve:"co|com|edu|gob|info|mil|net|org|web",vi:"co|com|k12|net|org",vn:"ac|biz|com|edu|gov|health|info|int|name|net|org|pro",ye:"co|com|gov|ltd|me|net|org|plc",yu:"ac|co|edu|gov|org",za:"ac|agric|alt|bourse|city|co|cybernet|db|edu|gov|grondar|iaccess|imt|inca|landesign|law|mil|net|ngo|nis|nom|olivetti|org|pix|school|tm|web",zm:"ac|co|com|edu|gov|net|org|sch"},has_expression:null,is_expression:null,has:function(d){return!!d.match(p.has_expression)},
is:function(d){return!!d.match(p.is_expression)},get:function(d){return(d=d.match(p.has_expression))&&d[1]||null},noConflict:function(){f.SecondLevelDomains===this&&(f.SecondLevelDomains=l);return this},init:function(){var d="",f;for(f in p.list)h.call(p.list,f)&&(d+="|("+("("+p.list[f]+")."+f)+")");p.has_expression=RegExp("\\.("+d.substr(1)+")$","i");p.is_expression=RegExp("^("+d.substr(1)+")$","i")}};p.init();return p});
(function(f,l){"object"===typeof exports?module.exports=l(require("./punycode"),require("./IPv6"),require("./SecondLevelDomains")):"function"===typeof define&&define.amd?define(["./punycode","./IPv6","./SecondLevelDomains"],l):f.URI=l(f.punycode,f.IPv6,f.SecondLevelDomains,f)})(this,function(f,l,h,p){function d(a,b){if(!(this instanceof d))return new d(a,b);void 0===a&&(a="undefined"!==typeof location?location.href+"":"");this.href(a);return void 0!==b?this.absoluteTo(b):this}function q(a){return a.replace(/([.*+?^=!:${}()|[\]\/\\])/g,
"\\$1")}function A(a){return void 0===a?"Undefined":String(Object.prototype.toString.call(a)).slice(8,-1)}function n(a){return"Array"===A(a)}function e(a,b){var c,d;if(n(b)){c=0;for(d=b.length;c<d;c++)if(!e(a,b[c]))return!1;return!0}var g=A(b);c=0;for(d=a.length;c<d;c++)if("RegExp"===g){if("string"===typeof a[c]&&a[c].match(b))return!0}else if(a[c]===b)return!0;return!1}function u(a,b){if(!n(a)||!n(b)||a.length!==b.length)return!1;a.sort();b.sort();for(var c=0,d=a.length;c<d;c++)if(a[c]!==b[c])return!1;
return!0}function v(a){return escape(a)}function z(a){return encodeURIComponent(a).replace(/[!'()*]/g,v).replace(/\*/g,"%2A")}var C=p&&p.URI,g=d.prototype,r=Object.prototype.hasOwnProperty;d._parts=function(){return{protocol:null,username:null,password:null,hostname:null,urn:null,port:null,path:null,query:null,fragment:null,duplicateQueryParameters:d.duplicateQueryParameters,escapeQuerySpace:d.escapeQuerySpace}};d.duplicateQueryParameters=!1;d.escapeQuerySpace=!0;d.protocol_expression=/^[a-z][a-z0-9-+-]*$/i;
d.idn_expression=/[^a-z0-9\.-]/i;d.punycode_expression=/(xn--)/i;d.ip4_expression=/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;d.ip6_expression=/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
d.find_uri_expression=/\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u2018\u2019]))/ig;d.defaultPorts={http:"80",https:"443",ftp:"21",gopher:"70",ws:"80",wss:"443"};d.invalid_hostname_characters=/[^a-zA-Z0-9\.-]/;d.domAttributes={a:"href",blockquote:"cite",link:"href",base:"href",script:"src",form:"action",img:"src",area:"href",
iframe:"src",embed:"src",source:"src",track:"src",input:"src"};d.getDomAttribute=function(a){if(a&&a.nodeName){var b=a.nodeName.toLowerCase();return"input"===b&&"image"!==a.type?void 0:d.domAttributes[b]}};d.encode=z;d.decode=decodeURIComponent;d.iso8859=function(){d.encode=escape;d.decode=unescape};d.unicode=function(){d.encode=z;d.decode=decodeURIComponent};d.characters={pathname:{encode:{expression:/%(24|26|2B|2C|3B|3D|3A|40)/ig,map:{"%24":"$","%26":"&","%2B":"+","%2C":",","%3B":";","%3D":"=",
"%3A":":","%40":"@"}},decode:{expression:/[\/\?#]/g,map:{"/":"%2F","?":"%3F","#":"%23"}}},reserved:{encode:{expression:/%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,map:{"%3A":":","%2F":"/","%3F":"?","%23":"#","%5B":"[","%5D":"]","%40":"@","%21":"!","%24":"$","%26":"&","%27":"'","%28":"(","%29":")","%2A":"*","%2B":"+","%2C":",","%3B":";","%3D":"="}}}};d.encodeQuery=function(a,b){var c=d.encode(a+"");return b?c.replace(/%20/g,"+"):c};d.decodeQuery=function(a,b){a+="";try{return d.decode(b?
a.replace(/\+/g,"%20"):a)}catch(c){return a}};d.recodePath=function(a){a=(a+"").split("/");for(var b=0,c=a.length;b<c;b++)a[b]=d.encodePathSegment(d.decode(a[b]));return a.join("/")};d.decodePath=function(a){a=(a+"").split("/");for(var b=0,c=a.length;b<c;b++)a[b]=d.decodePathSegment(a[b]);return a.join("/")};var m={encode:"encode",decode:"decode"},s,y=function(a,b){return function(c){return d[b](c+"").replace(d.characters[a][b].expression,function(c){return d.characters[a][b].map[c]})}};for(s in m)d[s+
"PathSegment"]=y("pathname",m[s]);d.encodeReserved=y("reserved","encode");d.parse=function(a,b){var c;b||(b={});c=a.indexOf("#");-1<c&&(b.fragment=a.substring(c+1)||null,a=a.substring(0,c));c=a.indexOf("?");-1<c&&(b.query=a.substring(c+1)||null,a=a.substring(0,c));"//"===a.substring(0,2)?(b.protocol=null,a=a.substring(2),a=d.parseAuthority(a,b)):(c=a.indexOf(":"),-1<c&&(b.protocol=a.substring(0,c)||null,b.protocol&&!b.protocol.match(d.protocol_expression)?b.protocol=void 0:"file"===b.protocol?a=a.substring(c+
3):"//"===a.substring(c+1,c+3)?(a=a.substring(c+3),a=d.parseAuthority(a,b)):(a=a.substring(c+1),b.urn=!0)));b.path=a;return b};d.parseHost=function(a,b){var c=a.indexOf("/"),d;-1===c&&(c=a.length);"["===a.charAt(0)?(d=a.indexOf("]"),b.hostname=a.substring(1,d)||null,b.port=a.substring(d+2,c)||null):a.indexOf(":")!==a.lastIndexOf(":")?(b.hostname=a.substring(0,c)||null,b.port=null):(d=a.substring(0,c).split(":"),b.hostname=d[0]||null,b.port=d[1]||null);b.hostname&&"/"!==a.substring(c).charAt(0)&&(c++,
a="/"+a);return a.substring(c)||"/"};d.parseAuthority=function(a,b){a=d.parseUserinfo(a,b);return d.parseHost(a,b)};d.parseUserinfo=function(a,b){var c=a.indexOf("/"),k=-1<c?a.lastIndexOf("@",c):a.indexOf("@");-1<k&&(-1===c||k<c)?(c=a.substring(0,k).split(":"),b.username=c[0]?d.decode(c[0]):null,c.shift(),b.password=c[0]?d.decode(c.join(":")):null,a=a.substring(k+1)):(b.username=null,b.password=null);return a};d.parseQuery=function(a,b){if(!a)return{};a=a.replace(/&+/g,"&").replace(/^\?*&*|&+$/g,
"");if(!a)return{};for(var c={},k=a.split("&"),g=k.length,e,f,h=0;h<g;h++)e=k[h].split("="),f=d.decodeQuery(e.shift(),b),e=e.length?d.decodeQuery(e.join("="),b):null,c[f]?("string"===typeof c[f]&&(c[f]=[c[f]]),c[f].push(e)):c[f]=e;return c};d.build=function(a){var b="";a.protocol&&(b+=a.protocol+":");a.urn||!b&&!a.hostname||(b+="//");b+=d.buildAuthority(a)||"";"string"===typeof a.path&&("/"!==a.path.charAt(0)&&"string"===typeof a.hostname&&(b+="/"),b+=a.path);"string"===typeof a.query&&a.query&&(b+=
"?"+a.query);"string"===typeof a.fragment&&a.fragment&&(b+="#"+a.fragment);return b};d.buildHost=function(a){var b="";if(a.hostname)d.ip6_expression.test(a.hostname)?b=a.port?b+("["+a.hostname+"]:"+a.port):b+a.hostname:(b+=a.hostname,a.port&&(b+=":"+a.port));else return"";return b};d.buildAuthority=function(a){return d.buildUserinfo(a)+d.buildHost(a)};d.buildUserinfo=function(a){var b="";a.username&&(b+=d.encode(a.username),a.password&&(b+=":"+d.encode(a.password)),b+="@");return b};d.buildQuery=
function(a,b,c){var k="",g,e,f,h;for(e in a)if(r.call(a,e)&&e)if(n(a[e]))for(g={},f=0,h=a[e].length;f<h;f++)void 0!==a[e][f]&&void 0===g[a[e][f]+""]&&(k+="&"+d.buildQueryParameter(e,a[e][f],c),!0!==b&&(g[a[e][f]+""]=!0));else void 0!==a[e]&&(k+="&"+d.buildQueryParameter(e,a[e],c));return k.substring(1)};d.buildQueryParameter=function(a,b,c){return d.encodeQuery(a,c)+(null!==b?"="+d.encodeQuery(b,c):"")};d.addQuery=function(a,b,c){if("object"===typeof b)for(var k in b)r.call(b,k)&&d.addQuery(a,k,b[k]);
else if("string"===typeof b)void 0===a[b]?a[b]=c:("string"===typeof a[b]&&(a[b]=[a[b]]),n(c)||(c=[c]),a[b]=a[b].concat(c));else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");};d.removeQuery=function(a,b,c){var k;if(n(b))for(c=0,k=b.length;c<k;c++)a[b[c]]=void 0;else if("object"===typeof b)for(k in b)r.call(b,k)&&d.removeQuery(a,k,b[k]);else if("string"===typeof b)if(void 0!==c)if(a[b]===c)a[b]=void 0;else{if(n(a[b])){k=a[b];var g={},e,f;if(n(c))for(e=0,f=c.length;e<
f;e++)g[c[e]]=!0;else g[c]=!0;e=0;for(f=k.length;e<f;e++)void 0!==g[k[e]]&&(k.splice(e,1),f--,e--);a[b]=k}}else a[b]=void 0;else throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");};d.hasQuery=function(a,b,c,k){if("object"===typeof b){for(var g in b)if(r.call(b,g)&&!d.hasQuery(a,g,b[g]))return!1;return!0}if("string"!==typeof b)throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");switch(A(c)){case "Undefined":return b in a;case "Boolean":return a=
Boolean(n(a[b])?a[b].length:a[b]),c===a;case "Function":return!!c(a[b],b,a);case "Array":return n(a[b])?(k?e:u)(a[b],c):!1;case "RegExp":return n(a[b])?k?e(a[b],c):!1:Boolean(a[b]&&a[b].match(c));case "Number":c=String(c);case "String":return n(a[b])?k?e(a[b],c):!1:a[b]===c;default:throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");}};d.commonPath=function(a,b){var c=Math.min(a.length,b.length),d;for(d=0;d<c;d++)if(a.charAt(d)!==
b.charAt(d)){d--;break}if(1>d)return a.charAt(0)===b.charAt(0)&&"/"===a.charAt(0)?"/":"";if("/"!==a.charAt(d)||"/"!==b.charAt(d))d=a.substring(0,d).lastIndexOf("/");return a.substring(0,d+1)};d.withinString=function(a,b){return a.replace(d.find_uri_expression,b)};d.ensureValidHostname=function(a){if(a.match(d.invalid_hostname_characters)){if(!f)throw new TypeError("Hostname '"+a+"' contains characters other than [A-Z0-9.-] and Punycode.js is not available");if(f.toASCII(a).match(d.invalid_hostname_characters))throw new TypeError("Hostname '"+
a+"' contains characters other than [A-Z0-9.-]");}};d.noConflict=function(a){if(a)return a={URI:this.noConflict()},URITemplate&&"function"==typeof URITemplate.noConflict&&(a.URITemplate=URITemplate.noConflict()),l&&"function"==typeof l.noConflict&&(a.IPv6=l.noConflict()),SecondLevelDomains&&"function"==typeof SecondLevelDomains.noConflict&&(a.SecondLevelDomains=SecondLevelDomains.noConflict()),a;p.URI===this&&(p.URI=C);return this};g.build=function(a){if(!0===a)this._deferred_build=!0;else if(void 0===
a||this._deferred_build)this._string=d.build(this._parts),this._deferred_build=!1;return this};g.clone=function(){return new d(this)};g.valueOf=g.toString=function(){return this.build(!1)._string};m={protocol:"protocol",username:"username",password:"password",hostname:"hostname",port:"port"};y=function(a){return function(b,c){if(void 0===b)return this._parts[a]||"";this._parts[a]=b||null;this.build(!c);return this}};for(s in m)g[s]=y(m[s]);m={query:"?",fragment:"#"};y=function(a,b){return function(c,
d){if(void 0===c)return this._parts[a]||"";null!==c&&(c+="",c.charAt(0)===b&&(c=c.substring(1)));this._parts[a]=c;this.build(!d);return this}};for(s in m)g[s]=y(s,m[s]);m={search:["?","query"],hash:["#","fragment"]};y=function(a,b){return function(c,d){var e=this[a](c,d);return"string"===typeof e&&e.length?b+e:e}};for(s in m)g[s]=y(m[s][1],m[s][0]);g.pathname=function(a,b){if(void 0===a||!0===a){var c=this._parts.path||(this._parts.hostname?"/":"");return a?d.decodePath(c):c}this._parts.path=a?d.recodePath(a):
"/";this.build(!b);return this};g.path=g.pathname;g.href=function(a,b){var c;if(void 0===a)return this.toString();this._string="";this._parts=d._parts();var k=a instanceof d,e="object"===typeof a&&(a.hostname||a.path||a.pathname);a.nodeName&&(e=d.getDomAttribute(a),a=a[e]||"",e=!1);!k&&(e&&void 0!==a.pathname)&&(a=a.toString());if("string"===typeof a)this._parts=d.parse(a,this._parts);else if(k||e)for(c in k=k?a._parts:a,k)r.call(this._parts,c)&&(this._parts[c]=k[c]);else throw new TypeError("invalid input");
this.build(!b);return this};g.is=function(a){var b=!1,c=!1,k=!1,e=!1,g=!1,f=!1,u=!1,m=!this._parts.urn;this._parts.hostname&&(m=!1,c=d.ip4_expression.test(this._parts.hostname),k=d.ip6_expression.test(this._parts.hostname),b=c||k,g=(e=!b)&&h&&h.has(this._parts.hostname),f=e&&d.idn_expression.test(this._parts.hostname),u=e&&d.punycode_expression.test(this._parts.hostname));switch(a.toLowerCase()){case "relative":return m;case "absolute":return!m;case "domain":case "name":return e;case "sld":return g;
case "ip":return b;case "ip4":case "ipv4":case "inet4":return c;case "ip6":case "ipv6":case "inet6":return k;case "idn":return f;case "url":return!this._parts.urn;case "urn":return!!this._parts.urn;case "punycode":return u}return null};var w=g.protocol,D=g.port,E=g.hostname;g.protocol=function(a,b){if(void 0!==a&&a&&(a=a.replace(/:(\/\/)?$/,""),a.match(/[^a-zA-z0-9\.+-]/)))throw new TypeError("Protocol '"+a+"' contains characters other than [A-Z0-9.+-]");return w.call(this,a,b)};g.scheme=g.protocol;
g.port=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a&&(0===a&&(a=null),a&&(a+="",":"===a.charAt(0)&&(a=a.substring(1)),a.match(/[^0-9]/))))throw new TypeError("Port '"+a+"' contains characters other than [0-9]");return D.call(this,a,b)};g.hostname=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a){var c={};d.parseHost(a,c);a=c.hostname}return E.call(this,a,b)};g.host=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a)return this._parts.hostname?
d.buildHost(this._parts):"";d.parseHost(a,this._parts);this.build(!b);return this};g.authority=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a)return this._parts.hostname?d.buildAuthority(this._parts):"";d.parseAuthority(a,this._parts);this.build(!b);return this};g.userinfo=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.username)return"";var c=d.buildUserinfo(this._parts);return c.substring(0,c.length-1)}"@"!==a[a.length-1]&&(a+=
"@");d.parseUserinfo(a,this._parts);this.build(!b);return this};g.resource=function(a,b){var c;if(void 0===a)return this.path()+this.search()+this.hash();c=d.parse(a);this._parts.path=c.path;this._parts.query=c.query;this._parts.fragment=c.fragment;this.build(!b);return this};g.subdomain=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.length-this.domain().length-1;return this._parts.hostname.substring(0,
c)||""}c=this._parts.hostname.length-this.domain().length;c=this._parts.hostname.substring(0,c);c=RegExp("^"+q(c));a&&"."!==a.charAt(a.length-1)&&(a+=".");a&&d.ensureValidHostname(a);this._parts.hostname=this._parts.hostname.replace(c,a);this.build(!b);return this};g.domain=function(a,b){if(this._parts.urn)return void 0===a?"":this;"boolean"===typeof a&&(b=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.match(/\./g);if(c&&2>c.length)return this._parts.hostname;
c=this._parts.hostname.length-this.tld(b).length-1;c=this._parts.hostname.lastIndexOf(".",c-1)+1;return this._parts.hostname.substring(c)||""}if(!a)throw new TypeError("cannot set domain empty");d.ensureValidHostname(a);!this._parts.hostname||this.is("IP")?this._parts.hostname=a:(c=RegExp(q(this.domain())+"$"),this._parts.hostname=this._parts.hostname.replace(c,a));this.build(!b);return this};g.tld=function(a,b){if(this._parts.urn)return void 0===a?"":this;"boolean"===typeof a&&(b=a,a=void 0);if(void 0===
a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.lastIndexOf("."),c=this._parts.hostname.substring(c+1);return!0!==b&&h&&h.list[c.toLowerCase()]?h.get(this._parts.hostname)||c:c}if(a)if(a.match(/[^a-zA-Z0-9-]/))if(h&&h.is(a))c=RegExp(q(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(c,a);else throw new TypeError("TLD '"+a+"' contains characters other than [A-Z0-9]");else{if(!this._parts.hostname||this.is("IP"))throw new ReferenceError("cannot set TLD on non-domain host");
c=RegExp(q(this.tld())+"$");this._parts.hostname=this._parts.hostname.replace(c,a)}else throw new TypeError("cannot set TLD empty");this.build(!b);return this};g.directory=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path&&!this._parts.hostname)return"";if("/"===this._parts.path)return"/";var c=this._parts.path.length-this.filename().length-1,c=this._parts.path.substring(0,c)||(this._parts.hostname?"/":"");return a?d.decodePath(c):c}c=this._parts.path.length-
this.filename().length;c=this._parts.path.substring(0,c);c=RegExp("^"+q(c));this.is("relative")||(a||(a="/"),"/"!==a.charAt(0)&&(a="/"+a));a&&"/"!==a.charAt(a.length-1)&&(a+="/");a=d.recodePath(a);this._parts.path=this._parts.path.replace(c,a);this.build(!b);return this};g.filename=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var c=this._parts.path.lastIndexOf("/"),c=this._parts.path.substring(c+1);return a?
d.decodePathSegment(c):c}c=!1;"/"===a.charAt(0)&&(a=a.substring(1));a.match(/\.?\//)&&(c=!0);var k=RegExp(q(this.filename())+"$");a=d.recodePath(a);this._parts.path=this._parts.path.replace(k,a);c?this.normalizePath(b):this.build(!b);return this};g.suffix=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var c=this.filename(),k=c.lastIndexOf(".");if(-1===k)return"";c=c.substring(k+1);c=/^[a-z0-9%]+$/i.test(c)?c:
"";return a?d.decodePathSegment(c):c}"."===a.charAt(0)&&(a=a.substring(1));if(c=this.suffix())k=a?RegExp(q(c)+"$"):RegExp(q("."+c)+"$");else{if(!a)return this;this._parts.path+="."+d.recodePath(a)}k&&(a=d.recodePath(a),this._parts.path=this._parts.path.replace(k,a));this.build(!b);return this};g.segment=function(a,b,c){var d=this._parts.urn?":":"/",e=this.path(),g="/"===e.substring(0,1),e=e.split(d);void 0!==a&&"number"!==typeof a&&(c=b,b=a,a=void 0);if(void 0!==a&&"number"!==typeof a)throw Error("Bad segment '"+
a+"', must be 0-based integer");g&&e.shift();0>a&&(a=Math.max(e.length+a,0));if(void 0===b)return void 0===a?e:e[a];if(null===a||void 0===e[a])if(n(b)){e=[];a=0;for(var f=b.length;a<f;a++)if(b[a].length||e.length&&e[e.length-1].length)e.length&&!e[e.length-1].length&&e.pop(),e.push(b[a])}else{if(b||"string"===typeof b)""===e[e.length-1]?e[e.length-1]=b:e.push(b)}else b||"string"===typeof b&&b.length?e[a]=b:e.splice(a,1);g&&e.unshift("");return this.path(e.join(d),c)};g.segmentCoded=function(a,b,c){var e,
g;"number"!==typeof a&&(c=b,b=a,a=void 0);if(void 0===b){a=this.segment(a,b,c);if(n(a))for(e=0,g=a.length;e<g;e++)a[e]=d.decode(a[e]);else a=void 0!==a?d.decode(a):void 0;return a}if(n(b))for(e=0,g=b.length;e<g;e++)b[e]=d.decode(b[e]);else b="string"===typeof b?d.encode(b):b;return this.segment(a,b,c)};var F=g.query;g.query=function(a,b){if(!0===a)return d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);if("function"===typeof a){var c=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace),
e=a.call(this,c);this._parts.query=d.buildQuery(e||c,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);this.build(!b);return this}return void 0!==a&&"string"!==typeof a?(this._parts.query=d.buildQuery(a,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),this.build(!b),this):F.call(this,a,b)};g.setQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);if("object"===typeof a)for(var g in a)r.call(a,g)&&(e[g]=a[g]);else if("string"===
typeof a)e[a]=void 0!==b?b:null;else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");this._parts.query=d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};g.addQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);d.addQuery(e,a,void 0===b?null:b);this._parts.query=d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);
"string"!==typeof a&&(c=b);this.build(!c);return this};g.removeQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);d.removeQuery(e,a,b);this._parts.query=d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};g.hasQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);return d.hasQuery(e,a,b,c)};g.setSearch=g.setQuery;g.addSearch=g.addQuery;g.removeSearch=
g.removeQuery;g.hasSearch=g.hasQuery;g.normalize=function(){return this._parts.urn?this.normalizeProtocol(!1).normalizeQuery(!1).normalizeFragment(!1).build():this.normalizeProtocol(!1).normalizeHostname(!1).normalizePort(!1).normalizePath(!1).normalizeQuery(!1).normalizeFragment(!1).build()};g.normalizeProtocol=function(a){"string"===typeof this._parts.protocol&&(this._parts.protocol=this._parts.protocol.toLowerCase(),this.build(!a));return this};g.normalizeHostname=function(a){this._parts.hostname&&
(this.is("IDN")&&f?this._parts.hostname=f.toASCII(this._parts.hostname):this.is("IPv6")&&l&&(this._parts.hostname=l.best(this._parts.hostname)),this._parts.hostname=this._parts.hostname.toLowerCase(),this.build(!a));return this};g.normalizePort=function(a){"string"===typeof this._parts.protocol&&this._parts.port===d.defaultPorts[this._parts.protocol]&&(this._parts.port=null,this.build(!a));return this};g.normalizePath=function(a){if(this._parts.urn||!this._parts.path||"/"===this._parts.path)return this;
var b,c=this._parts.path,e,g;"/"!==c.charAt(0)&&(b=!0,c="/"+c);for(c=c.replace(/(\/(\.\/)+)|(\/\.$)/g,"/").replace(/\/{2,}/g,"/");;){e=c.indexOf("/../");if(-1===e)break;else if(0===e){c=c.substring(3);break}g=c.substring(0,e).lastIndexOf("/");-1===g&&(g=e);c=c.substring(0,g)+c.substring(e+3)}b&&this.is("relative")&&(c=c.substring(1));c=d.recodePath(c);this._parts.path=c;this.build(!a);return this};g.normalizePathname=g.normalizePath;g.normalizeQuery=function(a){"string"===typeof this._parts.query&&
(this._parts.query.length?this.query(d.parseQuery(this._parts.query,this._parts.escapeQuerySpace)):this._parts.query=null,this.build(!a));return this};g.normalizeFragment=function(a){this._parts.fragment||(this._parts.fragment=null,this.build(!a));return this};g.normalizeSearch=g.normalizeQuery;g.normalizeHash=g.normalizeFragment;g.iso8859=function(){var a=d.encode,b=d.decode;d.encode=escape;d.decode=decodeURIComponent;this.normalize();d.encode=a;d.decode=b;return this};g.unicode=function(){var a=
d.encode,b=d.decode;d.encode=z;d.decode=unescape;this.normalize();d.encode=a;d.decode=b;return this};g.readable=function(){var a=this.clone();a.username("").password("").normalize();var b="";a._parts.protocol&&(b+=a._parts.protocol+"://");a._parts.hostname&&(a.is("punycode")&&f?(b+=f.toUnicode(a._parts.hostname),a._parts.port&&(b+=":"+a._parts.port)):b+=a.host());a._parts.hostname&&(a._parts.path&&"/"!==a._parts.path.charAt(0))&&(b+="/");b+=a.path(!0);if(a._parts.query){for(var c="",e=0,g=a._parts.query.split("&"),
h=g.length;e<h;e++){var u=(g[e]||"").split("="),c=c+("&"+d.decodeQuery(u[0],this._parts.escapeQuerySpace).replace(/&/g,"%26"));void 0!==u[1]&&(c+="="+d.decodeQuery(u[1],this._parts.escapeQuerySpace).replace(/&/g,"%26"))}b+="?"+c.substring(1)}return b+=d.decodeQuery(a.hash(),!0)};g.absoluteTo=function(a){var b=this.clone(),c=["protocol","username","password","hostname","port"],e,g;if(this._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a instanceof d||(a=new d(a));
b._parts.protocol||(b._parts.protocol=a._parts.protocol);if(this._parts.hostname)return b;for(e=0;g=c[e];e++)b._parts[g]=a._parts[g];c=["query","path"];for(e=0;g=c[e];e++)!b._parts[g]&&a._parts[g]&&(b._parts[g]=a._parts[g]);"/"!==b.path().charAt(0)&&(a=a.directory(),b._parts.path=(a?a+"/":"")+b._parts.path,b.normalizePath());b.build();return b};g.relativeTo=function(a){var b=this.clone().normalize(),c,e,g,f;if(b._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");
a=(new d(a)).normalize();c=b._parts;e=a._parts;g=b.path();f=a.path();if("/"!==g.charAt(0))throw Error("URI is already relative");if("/"!==f.charAt(0))throw Error("Cannot calculate a URI relative to another relative URI");c.protocol===e.protocol&&(c.protocol=null);if(c.username===e.username&&c.password===e.password&&null===c.protocol&&null===c.username&&null===c.password&&c.hostname===e.hostname&&c.port===e.port)c.hostname=null,c.port=null;else return b.build();if(g===f)return c.path="",b.build();
a=d.commonPath(b.path(),a.path());if(!a)return b.build();e=e.path.substring(a.length).replace(/[^\/]*$/,"").replace(/.*?\//g,"../");c.path=e+c.path.substring(a.length);return b.build()};g.equals=function(a){var b=this.clone();a=new d(a);var c={},e={},g={},f;b.normalize();a.normalize();if(b.toString()===a.toString())return!0;c=b.query();e=a.query();b.query("");a.query("");if(b.toString()!==a.toString()||c.length!==e.length)return!1;c=d.parseQuery(c,this._parts.escapeQuerySpace);e=d.parseQuery(e,this._parts.escapeQuerySpace);
for(f in c)if(r.call(c,f)){if(!n(c[f])){if(c[f]!==e[f])return!1}else if(!u(c[f],e[f]))return!1;g[f]=!0}for(f in e)if(r.call(e,f)&&!g[f])return!1;return!0};g.duplicateQueryParameters=function(a){this._parts.duplicateQueryParameters=!!a;return this};g.escapeQuerySpace=function(a){this._parts.escapeQuerySpace=!!a;return this};return d});
(function(f,l){"object"===typeof exports?module.exports=l(require("./URI")):"function"===typeof define&&define.amd?define(["./URI"],l):f.URITemplate=l(f.URI,f)})(this,function(f,l){function h(d){if(h._cache[d])return h._cache[d];if(!(this instanceof h))return new h(d);this.expression=d;h._cache[d]=this;return this}function p(d){this.data=d;this.cache={}}var d=l&&l.URITemplate,q=Object.prototype.hasOwnProperty,A=h.prototype,n={"":{prefix:"",separator:",",named:!1,empty_name_separator:!1,encode:"encode"},
"+":{prefix:"",separator:",",named:!1,empty_name_separator:!1,encode:"encodeReserved"},"#":{prefix:"#",separator:",",named:!1,empty_name_separator:!1,encode:"encodeReserved"},".":{prefix:".",separator:".",named:!1,empty_name_separator:!1,encode:"encode"},"/":{prefix:"/",separator:"/",named:!1,empty_name_separator:!1,encode:"encode"},";":{prefix:";",separator:";",named:!0,empty_name_separator:!1,encode:"encode"},"?":{prefix:"?",separator:"&",named:!0,empty_name_separator:!0,encode:"encode"},"&":{prefix:"&",
separator:"&",named:!0,empty_name_separator:!0,encode:"encode"}};h._cache={};h.EXPRESSION_PATTERN=/\{([^a-zA-Z0-9%_]?)([^\}]+)(\}|$)/g;h.VARIABLE_PATTERN=/^([^*:]+)((\*)|:(\d+))?$/;h.VARIABLE_NAME_PATTERN=/[^a-zA-Z0-9%_]/;h.expand=function(d,f){var v=n[d.operator],l=v.named?"Named":"Unnamed",q=d.variables,g=[],r,m,s;for(s=0;m=q[s];s++)r=f.get(m.name),r.val.length?g.push(h["expand"+l](r,v,m.explode,m.explode&&v.separator||",",m.maxlength,m.name)):r.type&&g.push("");return g.length?v.prefix+g.join(v.separator):
""};h.expandNamed=function(d,h,v,l,q,g){var r="",m=h.encode;h=h.empty_name_separator;var s=!d[m].length,n=2===d.type?"":f[m](g),w,p,A;p=0;for(A=d.val.length;p<A;p++)q?(w=f[m](d.val[p][1].substring(0,q)),2===d.type&&(n=f[m](d.val[p][0].substring(0,q)))):s?(w=f[m](d.val[p][1]),2===d.type?(n=f[m](d.val[p][0]),d[m].push([n,w])):d[m].push([void 0,w])):(w=d[m][p][1],2===d.type&&(n=d[m][p][0])),r&&(r+=l),v?r+=n+(h||w?"=":"")+w:(p||(r+=f[m](g)+(h||w?"=":"")),2===d.type&&(r+=n+","),r+=w);return r};h.expandUnnamed=
function(d,h,l,q,n,g){g="";var r=h.encode;h=h.empty_name_separator;var m=!d[r].length,s,p,w,A;w=0;for(A=d.val.length;w<A;w++)n?p=f[r](d.val[w][1].substring(0,n)):m?(p=f[r](d.val[w][1]),d[r].push([2===d.type?f[r](d.val[w][0]):void 0,p])):p=d[r][w][1],g&&(g+=q),2===d.type&&(s=n?f[r](d.val[w][0].substring(0,n)):d[r][w][0],g+=s,g=l?g+(h||p?"=":""):g+","),g+=p;return g};h.noConflict=function(){l.URITemplate===h&&(l.URITemplate=d);return h};A.expand=function(d){var f="";this.parts&&this.parts.length||this.parse();
d instanceof p||(d=new p(d));for(var l=0,n=this.parts.length;l<n;l++)f+="string"===typeof this.parts[l]?this.parts[l]:h.expand(this.parts[l],d);return f};A.parse=function(){var d=this.expression,f=h.EXPRESSION_PATTERN,l=h.VARIABLE_PATTERN,q=h.VARIABLE_NAME_PATTERN,p=[],g=0,r,m,s;for(f.lastIndex=0;;){m=f.exec(d);if(null===m){p.push(d.substring(g));break}else p.push(d.substring(g,m.index)),g=m.index+m[0].length;if(!n[m[1]])throw Error('Unknown Operator "'+m[1]+'" in "'+m[0]+'"');if(!m[3])throw Error('Unclosed Expression "'+
m[0]+'"');r=m[2].split(",");for(var y=0,w=r.length;y<w;y++){s=r[y].match(l);if(null===s)throw Error('Invalid Variable "'+r[y]+'" in "'+m[0]+'"');if(s[1].match(q))throw Error('Invalid Variable Name "'+s[1]+'" in "'+m[0]+'"');r[y]={name:s[1],explode:!!s[3],maxlength:s[4]&&parseInt(s[4],10)}}if(!r.length)throw Error('Expression Missing Variable(s) "'+m[0]+'"');p.push({expression:m[0],operator:m[1],variables:r})}p.length||p.push(d);this.parts=p;return this};p.prototype.get=function(d){var f=this.data,
h={type:0,val:[],encode:[],encodeReserved:[]},l;if(void 0!==this.cache[d])return this.cache[d];this.cache[d]=h;f="[object Function]"===String(Object.prototype.toString.call(f))?f(d):"[object Function]"===String(Object.prototype.toString.call(f[d]))?f[d](d):f[d];if(void 0!==f&&null!==f)if("[object Array]"===String(Object.prototype.toString.call(f))){l=0;for(d=f.length;l<d;l++)void 0!==f[l]&&null!==f[l]&&h.val.push([void 0,String(f[l])]);h.val.length&&(h.type=3)}else if("[object Object]"===String(Object.prototype.toString.call(f))){for(l in f)q.call(f,
l)&&(void 0!==f[l]&&null!==f[l])&&h.val.push([l,String(f[l])]);h.val.length&&(h.type=2)}else h.type=1,h.val.push([void 0,String(f)]);return h};f.expand=function(d,l){var n=(new h(d)).expand(l);return new f(n)};return h});

494
public/js/vendor/uri/URITemplate.js vendored Executable file
View File

@ -0,0 +1,494 @@
/*!
* URI.js - Mutating URLs
* URI Template Support - http://tools.ietf.org/html/rfc6570
*
* Version: 1.11.2
*
* Author: Rodney Rehm
* Web: http://medialize.github.com/URI.js/
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
(function (root, factory) {
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory(require('./URI'));
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['./URI'], factory);
} else {
// Browser globals (root is window)
root.URITemplate = factory(root.URI, root);
}
}(this, function (URI, root) {
"use strict";
// save current URITemplate variable, if any
var _URITemplate = root && root.URITemplate;
var hasOwn = Object.prototype.hasOwnProperty;
function URITemplate(expression) {
// serve from cache where possible
if (URITemplate._cache[expression]) {
return URITemplate._cache[expression];
}
// Allow instantiation without the 'new' keyword
if (!(this instanceof URITemplate)) {
return new URITemplate(expression);
}
this.expression = expression;
URITemplate._cache[expression] = this;
return this;
}
function Data(data) {
this.data = data;
this.cache = {};
}
var p = URITemplate.prototype;
// list of operators and their defined options
var operators = {
// Simple string expansion
'' : {
prefix: "",
separator: ",",
named: false,
empty_name_separator: false,
encode : "encode"
},
// Reserved character strings
'+' : {
prefix: "",
separator: ",",
named: false,
empty_name_separator: false,
encode : "encodeReserved"
},
// Fragment identifiers prefixed by "#"
'#' : {
prefix: "#",
separator: ",",
named: false,
empty_name_separator: false,
encode : "encodeReserved"
},
// Name labels or extensions prefixed by "."
'.' : {
prefix: ".",
separator: ".",
named: false,
empty_name_separator: false,
encode : "encode"
},
// Path segments prefixed by "/"
'/' : {
prefix: "/",
separator: "/",
named: false,
empty_name_separator: false,
encode : "encode"
},
// Path parameter name or name=value pairs prefixed by ";"
';' : {
prefix: ";",
separator: ";",
named: true,
empty_name_separator: false,
encode : "encode"
},
// Query component beginning with "?" and consisting
// of name=value pairs separated by "&"; an
'?' : {
prefix: "?",
separator: "&",
named: true,
empty_name_separator: true,
encode : "encode"
},
// Continuation of query-style &name=value pairs
// within a literal query component.
'&' : {
prefix: "&",
separator: "&",
named: true,
empty_name_separator: true,
encode : "encode"
}
// The operator characters equals ("="), comma (","), exclamation ("!"),
// at sign ("@"), and pipe ("|") are reserved for future extensions.
};
// storage for already parsed templates
URITemplate._cache = {};
// pattern to identify expressions [operator, variable-list] in template
URITemplate.EXPRESSION_PATTERN = /\{([^a-zA-Z0-9%_]?)([^\}]+)(\}|$)/g;
// pattern to identify variables [name, explode, maxlength] in variable-list
URITemplate.VARIABLE_PATTERN = /^([^*:]+)((\*)|:(\d+))?$/;
// pattern to verify variable name integrity
URITemplate.VARIABLE_NAME_PATTERN = /[^a-zA-Z0-9%_]/;
// expand parsed expression (expression, not template!)
URITemplate.expand = function(expression, data) {
// container for defined options for the given operator
var options = operators[expression.operator];
// expansion type (include keys or not)
var type = options.named ? "Named" : "Unnamed";
// list of variables within the expression
var variables = expression.variables;
// result buffer for evaluating the expression
var buffer = [];
var d, variable, i, l, value;
for (i = 0; variable = variables[i]; i++) {
// fetch simplified data source
d = data.get(variable.name);
if (!d.val.length) {
if (d.type) {
// empty variables (empty string)
// still lead to a separator being appended!
buffer.push("");
}
// no data, no action
continue;
}
// expand the given variable
buffer.push(URITemplate["expand" + type](
d,
options,
variable.explode,
variable.explode && options.separator || ",",
variable.maxlength,
variable.name
));
}
if (buffer.length) {
return options.prefix + buffer.join(options.separator);
} else {
// prefix is not prepended for empty expressions
return "";
}
};
// expand a named variable
URITemplate.expandNamed = function(d, options, explode, separator, length, name) {
// variable result buffer
var result = "";
// peformance crap
var encode = options.encode;
var empty_name_separator = options.empty_name_separator;
// flag noting if values are already encoded
var _encode = !d[encode].length;
// key for named expansion
var _name = d.type === 2 ? '': URI[encode](name);
var _value, i, l;
// for each found value
for (i = 0, l = d.val.length; i < l; i++) {
if (length) {
// maxlength must be determined before encoding can happen
_value = URI[encode](d.val[i][1].substring(0, length));
if (d.type === 2) {
// apply maxlength to keys of objects as well
_name = URI[encode](d.val[i][0].substring(0, length));
}
} else if (_encode) {
// encode value
_value = URI[encode](d.val[i][1]);
if (d.type === 2) {
// encode name and cache encoded value
_name = URI[encode](d.val[i][0]);
d[encode].push([_name, _value]);
} else {
// cache encoded value
d[encode].push([undefined, _value]);
}
} else {
// values are already encoded and can be pulled from cache
_value = d[encode][i][1];
if (d.type === 2) {
_name = d[encode][i][0];
}
}
if (result) {
// unless we're the first value, prepend the separator
result += separator;
}
if (!explode) {
if (!i) {
// first element, so prepend variable name
result += URI[encode](name) + (empty_name_separator || _value ? "=" : "");
}
if (d.type === 2) {
// without explode-modifier, keys of objects are returned comma-separated
result += _name + ",";
}
result += _value;
} else {
// only add the = if it is either default (?&) or there actually is a value (;)
result += _name + (empty_name_separator || _value ? "=" : "") + _value;
}
}
return result;
};
// expand an unnamed variable
URITemplate.expandUnnamed = function(d, options, explode, separator, length, name) {
// variable result buffer
var result = "";
// performance crap
var encode = options.encode;
var empty_name_separator = options.empty_name_separator;
// flag noting if values are already encoded
var _encode = !d[encode].length;
var _name, _value, i, l;
// for each found value
for (i = 0, l = d.val.length; i < l; i++) {
if (length) {
// maxlength must be determined before encoding can happen
_value = URI[encode](d.val[i][1].substring(0, length));
} else if (_encode) {
// encode and cache value
_value = URI[encode](d.val[i][1]);
d[encode].push([
d.type === 2 ? URI[encode](d.val[i][0]) : undefined,
_value
]);
} else {
// value already encoded, pull from cache
_value = d[encode][i][1];
}
if (result) {
// unless we're the first value, prepend the separator
result += separator;
}
if (d.type === 2) {
if (length) {
// maxlength also applies to keys of objects
_name = URI[encode](d.val[i][0].substring(0, length));
} else {
// at this point the name must already be encoded
_name = d[encode][i][0];
}
result += _name;
if (explode) {
// explode-modifier separates name and value by "="
result += (empty_name_separator || _value ? "=" : "");
} else {
// no explode-modifier separates name and value by ","
result += ",";
}
}
result += _value;
}
return result;
};
URITemplate.noConflict = function() {
if (root.URITemplate === URITemplate) {
root.URITemplate = _URITemplate;
}
return URITemplate;
};
// expand template through given data map
p.expand = function(data) {
var result = "";
if (!this.parts || !this.parts.length) {
// lazilyy parse the template
this.parse();
}
if (!(data instanceof Data)) {
// make given data available through the
// optimized data handling thingie
data = new Data(data);
}
for (var i = 0, l = this.parts.length; i < l; i++) {
result += typeof this.parts[i] === "string"
// literal string
? this.parts[i]
// expression
: URITemplate.expand(this.parts[i], data);
}
return result;
};
// parse template into action tokens
p.parse = function() {
// performance crap
var expression = this.expression;
var ePattern = URITemplate.EXPRESSION_PATTERN;
var vPattern = URITemplate.VARIABLE_PATTERN;
var nPattern = URITemplate.VARIABLE_NAME_PATTERN;
// token result buffer
var parts = [];
// position within source template
var pos = 0;
var variables, eMatch, vMatch;
// RegExp is shared accross all templates,
// which requires a manual reset
ePattern.lastIndex = 0;
// I don't like while(foo = bar()) loops,
// to make things simpler I go while(true) and break when required
while (true) {
eMatch = ePattern.exec(expression);
if (eMatch === null) {
// push trailing literal
parts.push(expression.substring(pos));
break;
} else {
// push leading literal
parts.push(expression.substring(pos, eMatch.index));
pos = eMatch.index + eMatch[0].length;
}
if (!operators[eMatch[1]]) {
throw new Error('Unknown Operator "' + eMatch[1] + '" in "' + eMatch[0] + '"');
} else if (!eMatch[3]) {
throw new Error('Unclosed Expression "' + eMatch[0] + '"');
}
// parse variable-list
variables = eMatch[2].split(',');
for (var i = 0, l = variables.length; i < l; i++) {
vMatch = variables[i].match(vPattern);
if (vMatch === null) {
throw new Error('Invalid Variable "' + variables[i] + '" in "' + eMatch[0] + '"');
} else if (vMatch[1].match(nPattern)) {
throw new Error('Invalid Variable Name "' + vMatch[1] + '" in "' + eMatch[0] + '"');
}
variables[i] = {
name: vMatch[1],
explode: !!vMatch[3],
maxlength: vMatch[4] && parseInt(vMatch[4], 10)
};
}
if (!variables.length) {
throw new Error('Expression Missing Variable(s) "' + eMatch[0] + '"');
}
parts.push({
expression: eMatch[0],
operator: eMatch[1],
variables: variables
});
}
if (!parts.length) {
// template doesn't contain any expressions
// so it is a simple literal string
// this probably should fire a warning or something?
parts.push(expression);
}
this.parts = parts;
return this;
};
// simplify data structures
Data.prototype.get = function(key) {
// performance crap
var data = this.data;
// cache for processed data-point
var d = {
// type of data 0: undefined/null, 1: string, 2: object, 3: array
type: 0,
// original values (except undefined/null)
val: [],
// cache for encoded values (only for non-maxlength expansion)
encode: [],
encodeReserved: []
};
var i, l, value;
if (this.cache[key] !== undefined) {
// we've already processed this key
return this.cache[key];
}
this.cache[key] = d;
if (String(Object.prototype.toString.call(data)) === "[object Function]") {
// data itself is a callback (global callback)
value = data(key);
} else if (String(Object.prototype.toString.call(data[key])) === "[object Function]") {
// data is a map of callbacks (local callback)
value = data[key](key);
} else {
// data is a map of data
value = data[key];
}
// generalize input into [ [name1, value1], [name2, value2], … ]
// so expansion has to deal with a single data structure only
if (value === undefined || value === null) {
// undefined and null values are to be ignored completely
return d;
} else if (String(Object.prototype.toString.call(value)) === "[object Array]") {
for (i = 0, l = value.length; i < l; i++) {
if (value[i] !== undefined && value[i] !== null) {
// arrays don't have names
d.val.push([undefined, String(value[i])]);
}
}
if (d.val.length) {
// only treat non-empty arrays as arrays
d.type = 3; // array
}
} else if (String(Object.prototype.toString.call(value)) === "[object Object]") {
for (i in value) {
if (hasOwn.call(value, i) && value[i] !== undefined && value[i] !== null) {
// objects have keys, remember them for named expansion
d.val.push([i, String(value[i])]);
}
}
if (d.val.length) {
// only treat non-empty objects as objects
d.type = 2; // object
}
} else {
d.type = 1; // primitive string (could've been string, number, boolean and objects with a toString())
// arrays don't have names
d.val.push([undefined, String(value)]);
}
return d;
};
// hook into URI for fluid access
URI.expand = function(expression, data) {
var template = new URITemplate(expression);
var expansion = template.expand(data);
return new URI(expansion);
};
return URITemplate;
}));

232
public/js/vendor/uri/jquery.URI.js vendored Executable file
View File

@ -0,0 +1,232 @@
/*!
* URI.js - Mutating URLs
* jQuery Plugin
*
* Version: 1.11.2
*
* Author: Rodney Rehm
* Web: http://medialize.github.com/URI.js/jquery-uri-plugin.html
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
(function (root, factory) {
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory(require('jquery', './URI'));
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery', './URI'], factory);
} else {
// Browser globals (root is window)
factory(root.jQuery, root.URI);
}
}(this, function ($, URI) {
"use strict";
var comparable = {};
var compare = {
// equals
'=': function(value, target) {
return value === target;
},
// ~= translates to value.match((?:^|\s)target(?:\s|$)) which is useless for URIs
// |= translates to value.match((?:\b)target(?:-|\s|$)) which is useless for URIs
// begins with
'^=': function(value, target, property) {
return !!(value + "").match(new RegExp('^' + escapeRegEx(target), 'i'));
},
// ends with
'$=': function(value, target, property) {
return !!(value + "").match(new RegExp(escapeRegEx(target) + '$', 'i'));
},
// contains
'*=': function(value, target, property) {
if (property == 'directory') {
// add trailing slash so /dir/ will match the deep-end as well
value += '/';
}
return !!(value + "").match(new RegExp(escapeRegEx(target), 'i'));
},
'equals:': function(uri, target) {
return uri.equals(target);
},
'is:': function(uri, target) {
return uri.is(target);
}
};
function escapeRegEx(string) {
// https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963
return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}
function getUriProperty(elem) {
var nodeName = elem.nodeName.toLowerCase();
var property = URI.domAttributes[nodeName];
if (nodeName === 'input' && elem.type !== 'image') {
// compensate ambiguous <input> that is not an image
return undefined;
}
// NOTE: as we use a static mapping from element to attribute,
// the HTML5 attribute issue should not come up again
// https://github.com/medialize/URI.js/issues/69
return property;
}
function generateAccessor(property) {
return {
get: function(elem) {
return $(elem).uri()[property]();
},
set: function(elem, value) {
$(elem).uri()[property](value);
return value;
}
};
};
// populate lookup table and register $.attr('uri:accessor') handlers
$.each('authority directory domain filename fragment hash host hostname href password path pathname port protocol query resource scheme search subdomain suffix tld username'.split(" "), function(k, v) {
comparable[v] = true;
$.attrHooks['uri:' + v] = generateAccessor(v);
});
// pipe $.attr('src') and $.attr('href') through URI.js
var _attrHooks = {
get: function(elem) {
return $(elem).uri();
},
set: function(elem, value) {
return $(elem).uri().href(value).toString();
}
};
$.each(['src', 'href', 'action', 'uri', 'cite'], function(k, v) {
$.attrHooks[v] = {
set: _attrHooks.set
};
});
$.attrHooks.uri.get = _attrHooks.get;
// general URI accessor
$.fn.uri = function(uri) {
var $this = this.first();
var elem = $this.get(0);
var property = getUriProperty(elem);
if (!property) {
throw new Error('Element "' + elem.nodeName + '" does not have either property: href, src, action, cite');
}
if (uri !== undefined) {
var old = $this.data('uri');
if (old) {
return old.href(uri);
}
if (!(uri instanceof URI)) {
uri = URI(uri || '');
}
} else {
uri = $this.data('uri');
if (uri) {
return uri;
} else {
uri = URI($this.attr(property) || '');
}
}
uri._dom_element = elem;
uri._dom_attribute = property;
uri.normalize();
$this.data('uri', uri);
return uri;
};
// overwrite URI.build() to update associated DOM element if necessary
URI.prototype.build = function(deferBuild) {
if (this._dom_element) {
// cannot defer building when hooked into a DOM element
this._string = URI.build(this._parts);
this._deferred_build = false;
this._dom_element.setAttribute(this._dom_attribute, this._string);
this._dom_element[this._dom_attribute] = this._string;
} else if (deferBuild === true) {
this._deferred_build = true;
} else if (deferBuild === undefined || this._deferred_build) {
this._string = URI.build(this._parts);
this._deferred_build = false;
}
return this;
};
// add :uri() pseudo class selector to sizzle
var uriSizzle;
var pseudoArgs = /^([a-zA-Z]+)\s*([\^\$*]?=|:)\s*(['"]?)(.+)\3|^\s*([a-zA-Z0-9]+)\s*$/;
function uriPseudo (elem, text) {
var match, property, uri;
// skip anything without src|href|action and bad :uri() syntax
if (!getUriProperty(elem) || !text) {
return false;
}
match = text.match(pseudoArgs);
if (!match || (!match[5] && match[2] !== ':' && !compare[match[2]])) {
// abort because the given selector cannot be executed
// filers seem to fail silently
return false;
}
uri = $(elem).uri();
if (match[5]) {
return uri.is(match[5]);
} else if (match[2] === ':') {
property = match[1].toLowerCase() + ':';
if (!compare[property]) {
// filers seem to fail silently
return false;
}
return compare[property](uri, match[4]);
} else {
property = match[1].toLowerCase();
if (!comparable[property]) {
// filers seem to fail silently
return false;
}
return compare[match[2]](uri[property](), match[4], property);
}
return false;
}
if ($.expr.createPseudo) {
// jQuery >= 1.8
uriSizzle = $.expr.createPseudo(function (text) {
return function (elem) {
return uriPseudo(elem, text);
};
});
} else {
// jQuery < 1.8
uriSizzle = function (elem, i, match) {
return uriPseudo(elem, match[3]);
};
}
$.expr[":"].uri = uriSizzle;
// extending existing object rather than defining something new
return {};
}));

7
public/js/vendor/uri/jquery.URI.min.js vendored Executable file
View File

@ -0,0 +1,7 @@
/*! URI.js v1.11.2 http://medialize.github.com/URI.js/ */
/* build contains: jquery.URI.js */
(function(d,e){"object"===typeof exports?module.exports=e(require("jquery","./URI")):"function"===typeof define&&define.amd?define(["jquery","./URI"],e):e(d.jQuery,d.URI)})(this,function(d,e){function h(a){return a.replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")}function k(a){var b=a.nodeName.toLowerCase();return"input"===b&&"image"!==a.type?void 0:e.domAttributes[b]}function p(a){return{get:function(b){return d(b).uri()[a]()},set:function(b,c){d(b).uri()[a](c);return c}}}function l(a,b){var c,e,f;if(!k(a)||
!b)return!1;c=b.match(q);if(!c||!c[5]&&":"!==c[2]&&!g[c[2]])return!1;f=d(a).uri();if(c[5])return f.is(c[5]);if(":"===c[2])return e=c[1].toLowerCase()+":",g[e]?g[e](f,c[4]):!1;e=c[1].toLowerCase();return m[e]?g[c[2]](f[e](),c[4],e):!1}var m={},g={"=":function(a,b){return a===b},"^=":function(a,b,c){return!!(a+"").match(RegExp("^"+h(b),"i"))},"$=":function(a,b,c){return!!(a+"").match(RegExp(h(b)+"$","i"))},"*=":function(a,b,c){"directory"==c&&(a+="/");return!!(a+"").match(RegExp(h(b),"i"))},"equals:":function(a,
b){return a.equals(b)},"is:":function(a,b){return a.is(b)}};d.each("authority directory domain filename fragment hash host hostname href password path pathname port protocol query resource scheme search subdomain suffix tld username".split(" "),function(a,b){m[b]=!0;d.attrHooks["uri:"+b]=p(b)});var r=function(a,b){return d(a).uri().href(b).toString()};d.each(["src","href","action","uri","cite"],function(a,b){d.attrHooks[b]={set:r}});d.attrHooks.uri.get=function(a){return d(a).uri()};d.fn.uri=function(a){var b=
this.first(),c=b.get(0),d=k(c);if(!d)throw Error('Element "'+c.nodeName+'" does not have either property: href, src, action, cite');if(void 0!==a){var f=b.data("uri");if(f)return f.href(a);a instanceof e||(a=e(a||""))}else{if(a=b.data("uri"))return a;a=e(b.attr(d)||"")}a._dom_element=c;a._dom_attribute=d;a.normalize();b.data("uri",a);return a};e.prototype.build=function(a){if(this._dom_element)this._string=e.build(this._parts),this._deferred_build=!1,this._dom_element.setAttribute(this._dom_attribute,
this._string),this._dom_element[this._dom_attribute]=this._string;else if(!0===a)this._deferred_build=!0;else if(void 0===a||this._deferred_build)this._string=e.build(this._parts),this._deferred_build=!1;return this};var n,q=/^([a-zA-Z]+)\s*([\^\$*]?=|:)\s*(['"]?)(.+)\3|^\s*([a-zA-Z0-9]+)\s*$/;n=d.expr.createPseudo?d.expr.createPseudo(function(a){return function(b){return l(b,a)}}):function(a,b,c){return l(a,c[3])};d.expr[":"].uri=n;return{}});

508
public/js/vendor/uri/punycode.js vendored Executable file
View File

@ -0,0 +1,508 @@
/*! http://mths.be/punycode v1.2.3 by @mathias */
;(function(root) {
/** Detect free variables */
var freeExports = typeof exports == 'object' && exports;
var freeModule = typeof module == 'object' && module &&
module.exports == freeExports && module;
var freeGlobal = typeof global == 'object' && global;
if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
root = freeGlobal;
}
/**
* The `punycode` object.
* @name punycode
* @type Object
*/
var punycode,
/** Highest positive signed 32-bit float value */
maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
/** Bootstring parameters */
base = 36,
tMin = 1,
tMax = 26,
skew = 38,
damp = 700,
initialBias = 72,
initialN = 128, // 0x80
delimiter = '-', // '\x2D'
/** Regular expressions */
regexPunycode = /^xn--/,
regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars
regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators
/** Error messages */
errors = {
'overflow': 'Overflow: input needs wider integers to process',
'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
'invalid-input': 'Invalid input'
},
/** Convenience shortcuts */
baseMinusTMin = base - tMin,
floor = Math.floor,
stringFromCharCode = String.fromCharCode,
/** Temporary variable */
key;
/*--------------------------------------------------------------------------*/
/**
* A generic error utility function.
* @private
* @param {String} type The error type.
* @returns {Error} Throws a `RangeError` with the applicable error message.
*/
function error(type) {
throw RangeError(errors[type]);
}
/**
* A generic `Array#map` utility function.
* @private
* @param {Array} array The array to iterate over.
* @param {Function} callback The function that gets called for every array
* item.
* @returns {Array} A new array of values returned by the callback function.
*/
function map(array, fn) {
var length = array.length;
while (length--) {
array[length] = fn(array[length]);
}
return array;
}
/**
* A simple `Array#map`-like wrapper to work with domain name strings.
* @private
* @param {String} domain The domain name.
* @param {Function} callback The function that gets called for every
* character.
* @returns {Array} A new string of characters returned by the callback
* function.
*/
function mapDomain(string, fn) {
return map(string.split(regexSeparators), fn).join('.');
}
/**
* Creates an array containing the numeric code points of each Unicode
* character in the string. While JavaScript uses UCS-2 internally,
* this function will convert a pair of surrogate halves (each of which
* UCS-2 exposes as separate characters) into a single code point,
* matching UTF-16.
* @see `punycode.ucs2.encode`
* @see <http://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode.ucs2
* @name decode
* @param {String} string The Unicode input string (UCS-2).
* @returns {Array} The new array of code points.
*/
function ucs2decode(string) {
var output = [],
counter = 0,
length = string.length,
value,
extra;
while (counter < length) {
value = string.charCodeAt(counter++);
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
// high surrogate, and there is a next character
extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) == 0xDC00) { // low surrogate
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
} else {
// unmatched surrogate; only append this code unit, in case the next
// code unit is the high surrogate of a surrogate pair
output.push(value);
counter--;
}
} else {
output.push(value);
}
}
return output;
}
/**
* Creates a string based on an array of numeric code points.
* @see `punycode.ucs2.decode`
* @memberOf punycode.ucs2
* @name encode
* @param {Array} codePoints The array of numeric code points.
* @returns {String} The new Unicode string (UCS-2).
*/
function ucs2encode(array) {
return map(array, function(value) {
var output = '';
if (value > 0xFFFF) {
value -= 0x10000;
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
value = 0xDC00 | value & 0x3FF;
}
output += stringFromCharCode(value);
return output;
}).join('');
}
/**
* Converts a basic code point into a digit/integer.
* @see `digitToBasic()`
* @private
* @param {Number} codePoint The basic numeric code point value.
* @returns {Number} The numeric value of a basic code point (for use in
* representing integers) in the range `0` to `base - 1`, or `base` if
* the code point does not represent a value.
*/
function basicToDigit(codePoint) {
if (codePoint - 48 < 10) {
return codePoint - 22;
}
if (codePoint - 65 < 26) {
return codePoint - 65;
}
if (codePoint - 97 < 26) {
return codePoint - 97;
}
return base;
}
/**
* Converts a digit/integer into a basic code point.
* @see `basicToDigit()`
* @private
* @param {Number} digit The numeric value of a basic code point.
* @returns {Number} The basic code point whose value (when used for
* representing integers) is `digit`, which needs to be in the range
* `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
* used; else, the lowercase form is used. The behavior is undefined
* if `flag` is non-zero and `digit` has no uppercase form.
*/
function digitToBasic(digit, flag) {
// 0..25 map to ASCII a..z or A..Z
// 26..35 map to ASCII 0..9
return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
}
/**
* Bias adaptation function as per section 3.4 of RFC 3492.
* http://tools.ietf.org/html/rfc3492#section-3.4
* @private
*/
function adapt(delta, numPoints, firstTime) {
var k = 0;
delta = firstTime ? floor(delta / damp) : delta >> 1;
delta += floor(delta / numPoints);
for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
delta = floor(delta / baseMinusTMin);
}
return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
}
/**
* Converts a Punycode string of ASCII-only symbols to a string of Unicode
* symbols.
* @memberOf punycode
* @param {String} input The Punycode string of ASCII-only symbols.
* @returns {String} The resulting string of Unicode symbols.
*/
function decode(input) {
// Don't use UCS-2
var output = [],
inputLength = input.length,
out,
i = 0,
n = initialN,
bias = initialBias,
basic,
j,
index,
oldi,
w,
k,
digit,
t,
length,
/** Cached calculation results */
baseMinusT;
// Handle the basic code points: let `basic` be the number of input code
// points before the last delimiter, or `0` if there is none, then copy
// the first basic code points to the output.
basic = input.lastIndexOf(delimiter);
if (basic < 0) {
basic = 0;
}
for (j = 0; j < basic; ++j) {
// if it's not a basic code point
if (input.charCodeAt(j) >= 0x80) {
error('not-basic');
}
output.push(input.charCodeAt(j));
}
// Main decoding loop: start just after the last delimiter if any basic code
// points were copied; start at the beginning otherwise.
for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
// `index` is the index of the next character to be consumed.
// Decode a generalized variable-length integer into `delta`,
// which gets added to `i`. The overflow checking is easier
// if we increase `i` as we go, then subtract off its starting
// value at the end to obtain `delta`.
for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
if (index >= inputLength) {
error('invalid-input');
}
digit = basicToDigit(input.charCodeAt(index++));
if (digit >= base || digit > floor((maxInt - i) / w)) {
error('overflow');
}
i += digit * w;
t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
if (digit < t) {
break;
}
baseMinusT = base - t;
if (w > floor(maxInt / baseMinusT)) {
error('overflow');
}
w *= baseMinusT;
}
out = output.length + 1;
bias = adapt(i - oldi, out, oldi == 0);
// `i` was supposed to wrap around from `out` to `0`,
// incrementing `n` each time, so we'll fix that now:
if (floor(i / out) > maxInt - n) {
error('overflow');
}
n += floor(i / out);
i %= out;
// Insert `n` at position `i` of the output
output.splice(i++, 0, n);
}
return ucs2encode(output);
}
/**
* Converts a string of Unicode symbols to a Punycode string of ASCII-only
* symbols.
* @memberOf punycode
* @param {String} input The string of Unicode symbols.
* @returns {String} The resulting Punycode string of ASCII-only symbols.
*/
function encode(input) {
var n,
delta,
handledCPCount,
basicLength,
bias,
j,
m,
q,
k,
t,
currentValue,
output = [],
/** `inputLength` will hold the number of code points in `input`. */
inputLength,
/** Cached calculation results */
handledCPCountPlusOne,
baseMinusT,
qMinusT;
// Convert the input in UCS-2 to Unicode
input = ucs2decode(input);
// Cache the length
inputLength = input.length;
// Initialize the state
n = initialN;
delta = 0;
bias = initialBias;
// Handle the basic code points
for (j = 0; j < inputLength; ++j) {
currentValue = input[j];
if (currentValue < 0x80) {
output.push(stringFromCharCode(currentValue));
}
}
handledCPCount = basicLength = output.length;
// `handledCPCount` is the number of code points that have been handled;
// `basicLength` is the number of basic code points.
// Finish the basic string - if it is not empty - with a delimiter
if (basicLength) {
output.push(delimiter);
}
// Main encoding loop:
while (handledCPCount < inputLength) {
// All non-basic code points < n have been handled already. Find the next
// larger one:
for (m = maxInt, j = 0; j < inputLength; ++j) {
currentValue = input[j];
if (currentValue >= n && currentValue < m) {
m = currentValue;
}
}
// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
// but guard against overflow
handledCPCountPlusOne = handledCPCount + 1;
if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
error('overflow');
}
delta += (m - n) * handledCPCountPlusOne;
n = m;
for (j = 0; j < inputLength; ++j) {
currentValue = input[j];
if (currentValue < n && ++delta > maxInt) {
error('overflow');
}
if (currentValue == n) {
// Represent delta as a generalized variable-length integer
for (q = delta, k = base; /* no condition */; k += base) {
t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
if (q < t) {
break;
}
qMinusT = q - t;
baseMinusT = base - t;
output.push(
stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
);
q = floor(qMinusT / baseMinusT);
}
output.push(stringFromCharCode(digitToBasic(q, 0)));
bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
delta = 0;
++handledCPCount;
}
}
++delta;
++n;
}
return output.join('');
}
/**
* Converts a Punycode string representing a domain name to Unicode. Only the
* Punycoded parts of the domain name will be converted, i.e. it doesn't
* matter if you call it on a string that has already been converted to
* Unicode.
* @memberOf punycode
* @param {String} domain The Punycode domain name to convert to Unicode.
* @returns {String} The Unicode representation of the given Punycode
* string.
*/
function toUnicode(domain) {
return mapDomain(domain, function(string) {
return regexPunycode.test(string)
? decode(string.slice(4).toLowerCase())
: string;
});
}
/**
* Converts a Unicode string representing a domain name to Punycode. Only the
* non-ASCII parts of the domain name will be converted, i.e. it doesn't
* matter if you call it with a domain that's already in ASCII.
* @memberOf punycode
* @param {String} domain The domain name to convert, as a Unicode string.
* @returns {String} The Punycode representation of the given domain name.
*/
function toASCII(domain) {
return mapDomain(domain, function(string) {
return regexNonASCII.test(string)
? 'xn--' + encode(string)
: string;
});
}
/*--------------------------------------------------------------------------*/
/** Define the public API */
punycode = {
/**
* A string representing the current Punycode.js version number.
* @memberOf punycode
* @type String
*/
'version': '1.2.3',
/**
* An object of methods to convert from JavaScript's internal character
* representation (UCS-2) to Unicode code points, and back.
* @see <http://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode
* @type Object
*/
'ucs2': {
'decode': ucs2decode,
'encode': ucs2encode
},
'decode': decode,
'encode': encode,
'toASCII': toASCII,
'toUnicode': toUnicode
};
/** Expose `punycode` */
// Some AMD build optimizers, like r.js, check for specific condition patterns
// like the following:
if (
typeof define == 'function' &&
typeof define.amd == 'object' &&
define.amd
) {
define(function() {
return punycode;
});
} else if (freeExports && !freeExports.nodeType) {
if (freeModule) { // in Node.js or RingoJS v0.8.0+
freeModule.exports = punycode;
} else { // in Narwhal or RingoJS v0.7.0-
for (key in punycode) {
punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
}
}
} else { // in Rhino or a web browser
root.punycode = punycode;
}
}(this));

View File

@ -38,17 +38,17 @@ icinga.performLogin();
casper.thenOpen('./config/logging', function() {
"use strict";
this.test.assertExists('form#form_config_logging', 'Assert the logging configuration form being displayed');
this.test.assertExists('form[name=form_config_logging]', 'Assert the logging configuration form being displayed');
this.test.assertEquals(
this.getElementAttribute('form#form_config_logging', 'data-icinga-form-modified'),
this.getElementAttribute('form[name=form_config_logging]', 'data-icinga-form-modified'),
"",
'Assert a form to initially have no modified flag'
);
// Wait for the component to initialize
this.wait(1000, function() {
this.sendKeys('#form_config_logging input#logging_app_target', 'somewhere');
this.sendKeys('form[name=form_config_logging] input#logging_app_target', 'somewhere');
this.test.assertEquals(
this.getElementAttribute('form#form_config_logging', 'data-icinga-form-modified'),
this.getElementAttribute('form[name=form_config_logging]', 'data-icinga-form-modified'),
"true",
'Assert a form to initially be marked as modified when changed'
);
@ -62,13 +62,13 @@ casper.thenOpen('./config/logging', function() {
casper.then(function() {
"use strict";
var checkbox = this.getElementAttribute('form#form_config_logging input#logging_debug_enable', 'value');
this.click('form#form_config_logging input#logging_debug_enable');
var checkbox = this.getElementAttribute('form[name=form_config_logging] input#logging_debug_enable', 'value');
this.click('form[name=form_config_logging] input#logging_debug_enable');
// determine whether the text input field appears after the click or not
var waitFn = (checkbox === '0' ? this.waitForSelector : this.waitWhileSelector).bind(this);
waitFn('form#form_config_logging input#logging_debug_target', function() {
waitFn('form[name=form_config_logging] input#logging_debug_target', function() {
this.test.assertEquals(
this.getElementAttribute('form#form_config_logging', 'data-icinga-form-modified'),
this.getElementAttribute('form[name=form_config_logging]', 'data-icinga-form-modified'),
"true",
'Assert modify flag to be set on the server side if using an autosubmit field'
);

View File

@ -1,34 +0,0 @@
// {{LICENSE_HEADER}}
// {{LICENSE_HEADER}}
var should = require("should");
var rjsmock = require("requiremock.js");
var asyncMock = require("asyncmock.js");
GLOBAL.document = $('body');
describe('The async module', function() {
it("Allows to react on specific headers", function(done) {
rjsmock.purgeDependencies();
rjsmock.registerDependencies({
'icinga/container' : {
updateContainer : function() {},
createPopupContainer: function() {}
}
});
requireNew("icinga/util/async.js");
var async = rjsmock.getDefine();
var headerValue = null;
asyncMock.setNextAsyncResult(async, "result", false, {
'X-Dont-Care' : 'Ignore-me',
'X-Test-Header' : 'Testme123'
});
async.registerHeaderListener("X-Test-Header", function(value, header) {
should.equal("Testme123", value);
done();
},this);
var test = async.createRequest();
});
});

View File

@ -81,7 +81,7 @@ describe('Component loader', function() {
});
});
it('Component load with user-defined id', function() {
xit('Component load with user-defined id', function() {
setUp();
addComponent('app/component2','some-id');

View File

@ -0,0 +1,127 @@
/*global requireNew:false, describe: false, it:false */
/**
* {{LICENSE_HEADER}}
* {{LICENSE_HEADER}}
*/
/**
* The assertion framework
*
* @type {should}
*/
var should = require('should');
/**
* RequireJS mocks for dynamically loading modules
*
* @type {requiremock}
*/
var rjsmock = require('requiremock.js');
/**
* URIjs object for easier URL manipulation
*
* @type {URIjs}
*/
var URI = require('URIjs');
// Setup required globals for this test
GLOBAL.document = $('body');
GLOBAL.History = require('historymock.js');
GLOBAL.Modernizr = {
history: true
};
/**
* Workaround as jQuery.contains doesn't work with the nodejs jQuery library on some test systems
*
* @returns {boolean}
*/
jQuery.contains = function() {
'use strict';
return true;
};
/**
* Create a basic dom only containing a main and detail container
*
*/
var createDOM = function() {
'use strict';
document.empty();
document.append(
$('<div>').attr('id', 'icingamain')
).append(
$('<div>').attr('id', 'icingadetail')
);
};
$.ajax = function(obj) {
obj.success("<div></div>");
};
/**
* Test case
*
*/
describe('The container component', function() {
'use strict';
/**
* Test dom selectors and instance creation
*/
it('should provide access to the main and detail component', function() {
createDOM();
rjsmock.registerDependencies({
'URIjs/URI' : URI
});
requireNew('icinga/components/container.js');
var Container = rjsmock.getDefine();
should.exist(Container.getMainContainer().containerDom, 'Assert that the main container has an DOM attached');
should.exist(Container.getDetailContainer().containerDom, 'Assert that the detail container has an DOM attached');
Container.getMainContainer().containerDom[0].should.equal(
$('#icingamain')[0], 'Assert the DOM of the main container being #icingamain');
Container.getDetailContainer().containerDom[0].should.equal(
$('#icingadetail')[0], 'Assert the DOM of the detail container being #icingadetail');
});
/**
* Test dynamic Url update
*/
it('should automatically update its part of the URL if assigning a new URL', function() {
rjsmock.registerDependencies({
'URIjs/URI' : URI
});
requireNew('icinga/components/container.js');
createDOM();
var Container = rjsmock.getDefine();
var url = Container.getMainContainer().updateContainerHref('/some/other/url?test');
window.setWindowUrl(url);
Container.getMainContainer().containerDom.attr('data-icinga-href').should.equal('/some/other/url?test');
url.should.equal(
'/some/other/url?test',
'Assert the main container updating the url correctly');
url = Container.getDetailContainer().updateContainerHref('/some/detail/url?test');
window.setWindowUrl(url);
Container.getDetailContainer().containerDom.attr('data-icinga-href').should.equal('/some/detail/url?test');
url.should.equal(
'/some/other/url?test&detail=' + encodeURIComponent('/some/detail/url?test'),
'Assert the detail container only updating the "detail" portion of the URL'
);
url = Container.getMainContainer().updateContainerHref('/some/other2/url?test=test');
window.setWindowUrl(Container.getMainContainer().getContainerHref(window.location.href));
Container.getMainContainer().containerDom.attr('data-icinga-href').should.equal('/some/other2/url?test=test');
url.should.equal(
'/some/other2/url?test=test&detail=' + encodeURIComponent('/some/detail/url?test'),
'Assert the main container keeping the detail portion untouched if being assigned a new URL'
);
});
});

View File

@ -30,7 +30,7 @@ describe('Component registry',function() {
cleanTestDom();
});
it('Existing ids are preserved', function() {
xit('Existing ids are preserved', function() {
setUp();
registry.add({}, 'user-defined-id', null).should.equal('user-defined-id');
@ -41,10 +41,6 @@ describe('Component registry',function() {
it('Components are correctly added to the library', function() {
setUp();
var cmp1 = { component: "cmp1" };
registry.add(cmp1, 'user-defined-id', null);
registry.getById('user-defined-id').should.equal(cmp1);
var cmp2 = { component: "cmp2" };
registry.add(cmp2, null, null);
registry.getById('icinga-component-0').should.equal(cmp2);
@ -52,7 +48,10 @@ describe('Component registry',function() {
cleanTestDom();
});
it('getId(component) should return the components assigned id.', function() {
/**
* Not supported anymore
*/
xit('getId(component) should return the components assigned id.', function() {
setUp();
var cmp1 = { component: "cmp1" };
@ -72,13 +71,13 @@ describe('Component registry',function() {
setUp();
var cmp1 = { component: "some/type" };
registry.add(cmp1, null, 'some/type');
registry.add(cmp1,'some/type');
var cmp2 = { component: "some/type" };
registry.add(cmp2, null, "some/type");
registry.add(cmp2, "some/type");
var cmp3 = { component: "other/type" };
registry.add(cmp3, null, "other/type");
registry.add(cmp3, "other/type");
var cmps = registry.getByType('some/type');
cmps.length.should.equal(2);

View File

@ -0,0 +1,64 @@
/**
* {{LICENSE_HEADER}}
* {{LICENSE_HEADER}}
*/
var URI = require('URIjs');
(function() {
GLOBAL.window = {
location: {
href: 'http://localhost/icinga2-web/testcase',
pathname: '/icinga2-web/testcase',
query: '',
hash: '',
host: 'localhost',
protocol: 'http'
}
};
"use strict";
var states = [];
/**
* Api for setting the window URL
*
* @param {string} url The new url to use for window.location
*/
window.setWindowUrl = function(url) {
var url = URI(url);
window.location.protocol = url.protocol();
window.location.pathname = url.pathname();
window.location.query = url.query();
window.location.search = url.search();
window.location.hash = url.hash();
window.location.href = url.href();
};
/**
* Mock for the History API
*
* @type {{pushState: Function, popState: Function, replaceState: Function, clear: Function}}
*/
module.exports = {
pushState: function(state, title, url) {
window.setWindowUrl(url);
states.push(arguments);
},
popState: function() {
return states.pop();
},
replaceState: function(state, title, url) {
states.pop();
window.setWindowUrl(url);
states.push(arguments);
},
clearState: function() {
states = [];
},
getState: function() {
return states;
}
};
})();

View File

@ -47,7 +47,7 @@ var logger = {
/**
* Mock for the 'define' function of requireJS, behaves exactly the same
* except that it looks up the dependencies in the list provided by registerDepencies()
* except that it looks up the dependencies in the list provided by registerDependencies()
* A module that hasn't been defined with a name can be fetched with getDefined() (without parameter)
*
**/
@ -88,6 +88,7 @@ var defineMock = function() {
**/
function initRequireMethods() {
GLOBAL.$ = require('jquery');
GLOBAL.jQuery = GLOBAL.$;
GLOBAL.requirejs = requireJsMock;
GLOBAL.define = defineMock;
registeredDependencies = {