aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/class/Config.php13
-rw-r--r--app/class/Controllerhome.php74
-rw-r--r--app/class/Model.php11
-rw-r--r--app/class/Modelhome.php218
-rw-r--r--app/class/Modelpage.php26
-rw-r--r--app/class/Modelrender.php7
-rw-r--r--app/fn/fn.php26
-rw-r--r--app/view/templates/home.php57
-rw-r--r--app/view/templates/homeopt.php2
-rw-r--r--assets/css/home.css11
-rw-r--r--package-lock.json170
-rw-r--r--package.json4
-rw-r--r--src/home.js3
-rw-r--r--src/map.js32
-rw-r--r--webpack.config.js1
15 files changed, 514 insertions, 141 deletions
diff --git a/app/class/Config.php b/app/class/Config.php
index fa9082f..2cfdcd3 100644
--- a/app/class/Config.php
+++ b/app/class/Config.php
@@ -3,6 +3,8 @@
namespace Wcms;
+use Http\Client\Common\Plugin\RetryPlugin;
+
abstract class Config
{
protected static $pagetable = 'mystore';
@@ -129,9 +131,16 @@ abstract class Config
return self::$fontsize;
}
- public static function basepath()
+ /**
+ * @param bool $trailingslash If not empty basepath, add a trailing slash after the basepath
+ */
+ public static function basepath(bool $trailingslash = false) : string
{
- return self::$basepath;
+ if($trailingslash && !empty(self::$basepath)) {
+ return self::$basepath . '/';
+ } else {
+ return self::$basepath;
+ }
}
public static function route404()
diff --git a/app/class/Controllerhome.php b/app/class/Controllerhome.php
index 5a9110a..71f110e 100644
--- a/app/class/Controllerhome.php
+++ b/app/class/Controllerhome.php
@@ -27,23 +27,15 @@ class Controllerhome extends Controllerpage
} else {
- $table = $this->modelhome->getlister();
- $this->opt = $this->modelhome->optinit($table);
+ $pagelist = $this->modelhome->getlister();
+ $this->opt = $this->modelhome->optinit($pagelist);
$vars['colors'] = new Colors($this->opt->taglist());
- if (!isset($_GET['search'])) {
- $searchopt = ['title' => 1, 'description' => 1, 'content' => 1, 'other' => 0, 'casesensitive' => 0];
- } else {
- $searchopt['title'] = $_GET['title'] ?? 0;
- $searchopt['description'] = $_GET['description'] ?? 0;
- $searchopt['content'] = $_GET['content'] ?? 0;
- $searchopt['other'] = $_GET['other'] ?? 0;
- $searchopt['casesensitive'] = $_GET['case'] ?? 0;
- }
- $regex = $_GET['search'] ?? '';
+ $deepsearch = $this->deepsearch();
+
+ $vars['pagelistopt'] = $this->modelhome->pagetable($pagelist, $this->opt, $deepsearch['regex'], $deepsearch['searchopt']);
- $vars['table2'] = $this->modelhome->table2($table, $this->opt, $regex , $searchopt);
$vars['columns'] = $this->modelhome->setcolumns($this->user->columns());
@@ -52,22 +44,58 @@ class Controllerhome extends Controllerpage
$vars['editorlist'] = $this->usermanager->getlisterbylevel(2, '>=');
$vars['user'] = $this->user;
$vars['opt'] = $this->opt;
- $vars['deepsearch'] = $regex;
- $vars['searchopt'] = $searchopt;
+ $vars['deepsearch'] = $deepsearch['regex'];
+ $vars['searchopt'] = $deepsearch['searchopt'];
+ $vars['display'] = $_GET['display'] ?? 'list';
+
+ if($vars['display'] === 'map') {
+ $vars['layout'] = $_GET['layout'] ?? 'cose-bilkent';
+ $vars['showorphans'] = boolval($_GET['showorphans'] ?? false);
+ $vars['showredirection'] = boolval($_GET['showredirection'] ?? false);
+ $datas = $this->modelhome->cytodata($vars['pagelistopt'], $vars['layout'], $vars['showorphans'], $vars['showredirection']);
+ $vars['json'] = json_encode($datas, JSON_PRETTY_PRINT);
+ }
- $vars['footer'] = ['version' => getversion(), 'total' => count($table), 'database' => Config::pagetable()];
+ $vars['footer'] = ['version' => getversion(), 'total' => count($pagelist), 'database' => Config::pagetable()];
- if (isset($_POST['query']) && $this->user->iseditor()) {
- $datas = array_merge($_POST, $_SESSION['opt']);
- $this->optlist = $this->modelhome->Optlistinit($table);
- $this->optlist->hydrate($datas);
- $vars['optlist'] = $this->optlist;
- }
+ $this->listquery($pagelist);
+
+ $vars['optlist'] = $this->optlist ?? null;
$this->showtemplate('home', $vars);
}
}
+ /**
+ * Look for GET deepsearch datas and transform it an array
+ *
+ * @return array containing `string $regex` and `array $searchopt`
+ */
+ public function deepsearch() : array
+ {
+ if (!isset($_GET['search'])) {
+ $searchopt = ['title' => 1, 'description' => 1, 'content' => 1, 'other' => 0, 'casesensitive' => 0];
+ } else {
+ $searchopt['title'] = $_GET['title'] ?? 0;
+ $searchopt['description'] = $_GET['description'] ?? 0;
+ $searchopt['content'] = $_GET['content'] ?? 0;
+ $searchopt['other'] = $_GET['other'] ?? 0;
+ $searchopt['casesensitive'] = $_GET['case'] ?? 0;
+ }
+ $regex = $_GET['search'] ?? '';
+ return ['regex' => $regex, 'searchopt' => $searchopt];
+ }
+
+ public function listquery(array $pagelist)
+ {
+ if (isset($_POST['query']) && $this->user->iseditor()) {
+ $datas = array_merge($_POST, $_SESSION['opt']);
+ $this->optlist = $this->modelhome->Optlistinit($pagelist);
+ $this->optlist->hydrate($datas);
+ $vars['optlist'] = $this->optlist;
+ }
+ }
+
public function columns()
{
if (isset($_POST['columns']) && $this->user->iseditor()) {
@@ -109,7 +137,7 @@ class Controllerhome extends Controllerpage
$this->routedirect('home');
}
}
-
+
/**
* Render every pages in the database
*/
diff --git a/app/class/Model.php b/app/class/Model.php
index 5847f74..9e66af4 100644
--- a/app/class/Model.php
+++ b/app/class/Model.php
@@ -20,6 +20,17 @@ abstract class Model
const GLOBAL_DIR = 'assets'. DIRECTORY_SEPARATOR . 'global' . DIRECTORY_SEPARATOR;
const DATABASE_DIR = '.' . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR;
const PAGES_DIR = self::DATABASE_DIR . 'pages' . DIRECTORY_SEPARATOR;
+
+ const MAP_LAYOUTS = [
+ 'cose' => 'cose',
+ 'cose-bilkent' => 'cose-bilkent',
+ 'circle' => 'circle',
+ 'breadthfirst' => 'breadthfirst',
+ 'concentric' => 'concentric',
+ 'grid' => 'grid',
+ 'random' => 'random',
+ ];
+
const MEDIA_EXT = [
'jpg' => 'image',
diff --git a/app/class/Modelhome.php b/app/class/Modelhome.php
index 6352282..949c4f7 100644
--- a/app/class/Modelhome.php
+++ b/app/class/Modelhome.php
@@ -39,64 +39,87 @@ class Modelhome extends Modelpage
}
+ /**
+ * @param array $pagelist of Pages objects as `id => Page`
+ * @param Opt $opt
+ *
+ * @param string $regex Regex to match.
+ * @param array $options Option search, could be `content` `title` `description`.
+ *
+ * @return array associative array of `Page` objects *
+ */
+ public function pagetable(array $pagelist, Opt $opt, $regex = '', $searchopt = []) : array
+ {
+ $pagelist = $this->filter($pagelist, $opt);
+ if(!empty($regex)) {
+ $pagelist = $this->deepsearch($pagelist, $regex , $searchopt);
+ }
+ $pagelist = $this->sort($pagelist, $opt);
+
+ return $pagelist;
+ }
+
/**
- * @param array $table
+ * Filter the pages list acording to the options and invert
+ *
+ * @param array $pagelist of `Page` objects
* @param Opt $opt
- * @param string $regex
+ *
+ * @return array of `string` pages id
*/
- public function table2(array $table, Opt $opt, string $regex = "", array $searchopt = [])
- {
+ public function filter(array $pagelist, Opt $opt) : array
+ {
- $filtertagfilter = $this->filtertagfilter($table, $opt->tagfilter(), $opt->tagcompare());
- $filterauthorfilter = $this->filterauthorfilter($table, $opt->authorfilter(), $opt->authorcompare());
- $filtersecure = $this->filtersecure($table, $opt->secure());
- $filterlinkto = $this->filterlinkto($table, $opt->linkto());
+ $filtertagfilter = $this->filtertagfilter($pagelist, $opt->tagfilter(), $opt->tagcompare());
+ $filterauthorfilter = $this->filterauthorfilter($pagelist, $opt->authorfilter(), $opt->authorcompare());
+ $filtersecure = $this->filtersecure($pagelist, $opt->secure());
+ $filterlinkto = $this->filterlinkto($pagelist, $opt->linkto());
$filter = array_intersect($filtertagfilter, $filtersecure, $filterauthorfilter, $filterlinkto);
- $table2 = [];
- $table2invert = [];
- foreach ($table as $page) {
- if (in_array($page->id(), $filter)) {
- $table2[] = $page;
- } else {
- $table2invert[] = $page;
- }
-
+ if($opt->invert()) {
+ $idlist = array_keys($pagelist);
+ $filter = array_diff($idlist, $filter);
}
- if (!empty($opt->invert())) {
- $table2 = $table2invert;
- }
+ return array_intersect_key($pagelist, array_flip($filter));
+ }
- if(!empty($regex)) {
- $table2 = $this->deepsearch($regex, $searchopt, $table2);
- }
- $this->pagelistsort($table2, $opt->sortby(), $opt->order());
+
+ /**
+ * Sort and limit an array of Pages
+ *
+ * @param array $pagelist of `Page` objects
+ * @param Opt $opt
+ *
+ * @return array associative array of `Page` objects
+ */
+ public function sort(array $pagelist, Opt $opt) : array
+ {
+ $this->pagelistsort($pagelist, $opt->sortby(), $opt->order());
if($opt->limit() !== 0) {
- $table2 = array_slice($table2, 0, $opt->limit());
+ $pagelist = array_slice($pagelist, 0, $opt->limit());
}
-
- return $table2;
+ return $pagelist;
}
/**
* Search for regex and count occurences
*
+ * @param array $page list Array of Pages.
* @param string $regex Regex to match.
* @param array $options Option search, could be `content` `title` `description`.
- * @param array $page list Array of Pages.
*
- * @return array associative array of Pages
+ * @return array associative array of `Page` objects
*/
- public function deepsearch(string $regex, array $options, array $pagelist) : array
+ public function deepsearch(array $pagelist, string $regex, array $options) : array
{
if($options['casesensitive']) {
$case = '';
@@ -126,11 +149,142 @@ class Modelhome extends Modelpage
$count += preg_match($regex, $page->description());
}
if ($count !== 0) {
- $pageselected[] = $page;
+ $pageselected[$page->id()] = $page;
}
}
return $pageselected;
- }
+ }
+
+
+
+ /**
+ * Transform list of page into list of nodes and edges
+ *
+ * @param array $pagelist associative array of pages as `id => Page`
+ * @param string $layout
+ * @param bool $showorphans if `false`, remove orphans pages
+ * @param bool $showredirection if `true`, add redirections
+ *
+ * @return array
+ */
+ public function cytodata(array $pagelist, string $layout = 'random', bool $showorphans = false, bool $showredirection = false) : array
+ {
+ $datas['elements'] = $this->mapdata($pagelist, $showorphans, $showredirection);
+
+ $datas['layout'] = [
+ 'name' => $layout,
+ 'quality' => 'proof',
+ 'fit' => true,
+ 'randomize' => true,
+ 'nodeDimensionsIncludeLabels' => true,
+ 'tile' => false,
+ 'edgeElasticity' => 0.45,
+ 'gravity' => 0.25,
+ 'idealEdgeLength' => 60,
+ 'numIter' => 10000
+ ];
+ $datas['style'] = [
+ [
+ 'selector' => 'node',
+ 'style' => [
+ 'label' => 'data(id)',
+ 'background-image' => 'data(favicon)',
+ 'background-fit' => 'contain',
+ 'border-width' => 3,
+ 'border-color' => '#80b97b'
+ ],
+ ],
+ [
+ 'selector' => 'node.not_published',
+ 'style' => [
+ 'shape' => 'round-hexagon',
+ 'border-color' => '#b97b7b'
+ ],
+ ],
+ [
+ 'selector' => 'node.private',
+ 'style' => [
+ 'shape' => 'round-triangle',
+ 'border-color' => '#b9b67b'
+ ],
+ ],
+ [
+ 'selector' => 'edge',
+ 'style' => [
+ 'curve-style' => 'bezier',
+ 'target-arrow-shape' => 'triangle',
+ 'arrow-scale' => 1.5
+ ],
+ ],
+ [
+ 'selector' => 'edge.redirect',
+ 'style' => [
+ 'line-style' => 'dashed',
+ 'label' => 'data(refresh)'
+ ],
+ ],
+ ];
+ return $datas;
+ }
+
+ /**
+ * Transform list of Pages into cytoscape nodes and edge datas
+ *
+ * @param array $pagelist associative array of pages as `id => Page`
+ * @param bool $showorphans if `false`, remove orphans pages
+ * @param bool $showredirection if `true`, add redirections
+ *
+ * @return array of cytoscape datas
+ */
+ public function mapdata(array $pagelist, bool $showorphans = true, bool $showredirection = false) : array
+ {
+ $idlist = array_keys($pagelist);
+
+ $edges = [];
+ $notorphans = [];
+ foreach ($pagelist as $page) {
+ foreach ($page->linkto() as $linkto) {
+ if(in_array($linkto, $idlist)) {
+ $edge['group'] = 'edges';
+ $edge['data']['id'] = $page->id() . '>' . $linkto;
+ $edge['data']['source'] = $page->id();
+ $edge['data']['target'] = $linkto;
+ $edges[] = $edge;
+ $notorphans[] = $linkto;
+ $notorphans[] = $page->id();
+ }
+ }
+ // add redirection edge
+ if($showredirection && key_exists($page->redirection(), $pagelist)) {
+ $edger['group'] = 'edges';
+ $edger['data']['id'] = $page->id() . '>' . $page->redirection();
+ $edger['data']['refresh'] = $page->refresh();
+ $edger['data']['source'] = $page->id();
+ $edger['data']['target'] = $page->redirection();
+ $edger['classes'] = 'redirect';
+ $edges[] = $edger;
+ $notorphans[] = $page->redirection();
+ $notorphans[] = $page->id();
+ }
+ }
+
+ $notorphans = array_unique($notorphans);
+
+ $nodes = [];
+ foreach ($pagelist as $id => $page) {
+ if($showorphans || (!$showorphans && in_array($id, $notorphans))) {
+ $node['group'] = 'nodes';
+ $node['data']['id'] = $page->id();
+ $node['data']['edit'] = $page->id() . DIRECTORY_SEPARATOR . 'edit';
+ $node['data']['favicon'] = Model::faviconpath() . $page->favicon();
+ $node['classes'] = [$page->secure('string')];
+ $nodes[] = $node;
+ }
+ }
+
+ return array_merge($nodes, $edges);
+
+ }
/**
diff --git a/app/class/Modelpage.php b/app/class/Modelpage.php
index 9344089..43ba117 100644
--- a/app/class/Modelpage.php
+++ b/app/class/Modelpage.php
@@ -27,7 +27,7 @@ class Modelpage extends Modeldb
/**
* Scan library for all pages as objects
*
- * @return array of Pages objects
+ * @return array of Pages objects as `id => Page`
*/
public function getlister()
{
@@ -232,7 +232,7 @@ class Modelpage extends Modeldb
public function pagelistsort(&$pagelist, $sortby, $order = 1)
{
- return usort($pagelist, $this->buildsorter($sortby, $order));
+ return uasort($pagelist, $this->buildsorter($sortby, $order));
}
@@ -241,7 +241,7 @@ class Modelpage extends Modeldb
* @param array $tagchecked list of tags
* @param string $tagcompare string, can be 'OR' or 'AND', set the tag filter method
*
- * @return array $array
+ * @return array $array of `string` page id
*/
public function filtertagfilter(array $pagelist, array $tagchecked, $tagcompare = 'OR')
@@ -267,6 +267,16 @@ class Modelpage extends Modeldb
return $filteredlist;
}
+
+
+ /**
+ * @param array $pagelist List of Page
+ * @param array $authorchecked list of authors
+ * @param string $authorcompare, can be 'OR' or 'AND', set the author filter method
+ *
+ * @return array $array of `string` page id
+ */
+
public function filterauthorfilter(array $pagelist, array $authorchecked, $authorcompare = 'OR')
{
@@ -290,7 +300,15 @@ class Modelpage extends Modeldb
return $filteredlist;
}
- public function filtersecure(array $pagelist, $secure) : array
+ /**
+ * @param array $pagelist List of Page
+ * @param int $secure secure level
+ * @param string $authorcompare, can be 'OR' or 'AND', set the author filter method
+ *
+ * @return array $array of `string` page id
+ */
+
+ public function filtersecure(array $pagelist, int $secure) : array
{
$filteredlist = [];
foreach ($pagelist as $page) {
diff --git a/app/class/Modelrender.php b/app/class/Modelrender.php
index c3103b6..bb6cf03 100644
--- a/app/class/Modelrender.php
+++ b/app/class/Modelrender.php
@@ -7,6 +7,7 @@ use Michelf\MarkdownExtra;
class Modelrender extends Modelpage
{
+ /** @var \AltoRouter */
protected $router;
/** @var Page */
protected $page;
@@ -21,7 +22,7 @@ class Modelrender extends Modelpage
const RENDER_VERBOSE = 1;
- public function __construct($router)
+ public function __construct(\AltoRouter $router)
{
parent::__construct();
@@ -657,10 +658,10 @@ class Modelrender extends Modelpage
foreach ($matches as $match) {
$optlist = $modelhome->Optlistinit($pagelist);
$optlist->parsehydrate($match['options']);
- $table2 = $modelhome->table2($pagelist, $optlist, '', []);
+ $pagetable = $modelhome->pagetable($pagelist, $optlist, '', []);
$content = '<ul class="pagelist">' . PHP_EOL ;
- foreach ($table2 as $page ) {
+ foreach ($pagetable as $page ) {
$content .= '<li>' . PHP_EOL;
$content .= '<a href="' . $this->upage($page->id()) . '">' . $page->title() . '</a>' . PHP_EOL;
if($optlist->description()) {
diff --git a/app/fn/fn.php b/app/fn/fn.php
index 6dfeb2c..60e4722 100644
--- a/app/fn/fn.php
+++ b/app/fn/fn.php
@@ -2,6 +2,8 @@
use Wcms\Medialist;
+use function Clue\StreamFilter\fun;
+
function readablesize($bytes)
{
$format = ' %d %s';
@@ -292,7 +294,29 @@ function recurse_copy($src,$dst) {
}
}
closedir($dir);
-}
+}
+
+/**
+ * Generate a list of <options> html drop down list
+ *
+ * @param array $options as `value => title`
+ * @param string|int $selected value of actualy selected option
+ *
+ * @return string HTML list of options
+ */
+function options(array $options, $selected = null) : string
+{
+ $html = '';
+ foreach ($options as $value => $title) {
+ if($value == $selected) {
+ $attribute = 'selected';
+ } else {
+ $attribute = '';
+ }
+ $html .= '<option value="' . $value . '" ' . $attribute . '>' . $title . '</option>' . PHP_EOL;
+ }
+ return $html;
+}
diff --git a/app/view/templates/home.php b/app/view/templates/home.php
index 8078d4a..4784b08 100644
--- a/app/view/templates/home.php
+++ b/app/view/templates/home.php
@@ -24,13 +24,62 @@
<main class="home">
- <?php $this->insert('homeopt', ['opt' => $opt, 'user' => $user]) ?>
+ <?php $this->insert('homeopt', ['opt' => $opt, 'user' => $user, 'display' => $display]) ?>
<section class="pages">
<div class="block">
- <h2 class="hidephone">Pages (<?= count($table2) ?>)</h2>
+ <h2 class="hidephone">Pages (<?= count($pagelistopt) ?>) <span class="right"><a href="?display=list" <?= $display === 'list' ? 'style="color: white"' : '' ?> >list</a> / <a href="?display=map" <?= $display === 'map' ? 'style="color: white"' : '' ?> >map</a></span> </h2>
+
+ <?php if($display === 'map') { ?>
+
+ <!-- ___________________ M A P _________________________ -->
+
+ <div id="deepsearchbar">
+ <form action="" method="get">
+ <input type="hidden" name="display" value="map">
+ <input type="checkbox" name="showorphans" value="1" id="showorphans" <?= $showorphans ? 'checked' : '' ?>>
+ <label for="showorphans">show orphans pages</label>
+ <input type="checkbox" name="showredirection" value="1" id="showredirection" <?= $showredirection ? 'checked' : '' ?>>
+ <label for="showredirection">show redirections</label>
+ <select name="layout" id="layout">
+ <?= options(Wcms\Model::MAP_LAYOUTS, $layout) ?>
+ </select>
+ <label for="layout">graph layout</label>
+ <input type="submit" value="update">
+ </form>
+ </div>
+
+ <div id="graph"></div>
+
+ <script>
+ var data = <?= $json ?>;
+ console.log(data);
+ </script>
+
+ <script src="<?= Wcms\Model::jspath() ?>map.bundle.js"></script>
+
+ <?php } else { ?>
+
+ <!-- ___________________ L I S T _________________________ -->
+
+ <div id="deepsearchbar" class="hidephone">
+ <form action="<?= $this->url('home') ?>" method="get">
+ <input type="text" name="search" value="<?= $deepsearch ?>" id="deepsearch" placeholder="deep search">
+ <input type="checkbox" name="title" id="deeptitle" value="1" <?= $searchopt['title'] ? 'checked' : '' ?>>
+ <label for="deeptitle">title</label>
+ <input type="checkbox" name="description" id="deepdescription" value="1" <?= $searchopt['description'] ? 'checked' : '' ?>>
+ <label for="deepdescription">description</label>
+ <input type="checkbox" name="content" id="deepcontent" value="1" <?= $searchopt['content'] ? 'checked' : '' ?>>
+ <label for="deepcontent" title="Markdown content : MAIN, HEADER, NAV, ASIDE, FOOTER">content</label>
+ <input type="checkbox" name="other" id="deepother" value="1" <?= $searchopt['other'] ? 'checked' : '' ?>>
+ <label for="deepother" title="Structure content : BODY, CSS, Javascript">other</label>
+ <input type="checkbox" name="case" id="deepcase" value="1" <?= $searchopt['casesensitive'] ? 'checked' : '' ?>>
+ <label for="deepcase" title="Case sensitive or not">case sensitive</label>
+ <input type="submit" value="search">
+ </form>
+ </div>
<div id="deepsearchbar" class="hidephone">
@@ -105,7 +154,7 @@
</tr>
</thead>
<tbody>
- <?php foreach ($table2 as $item) { ?>
+ <?php foreach ($pagelistopt as $item) { ?>
<tr>
<?php if($user->issupereditor()) { ?><td class="hidephone"><input type="checkbox" name="pagesid[]" value="<?= $item->id() ?>" id="id_<?= $item->id() ?>" form="multi"></td><?php } ?>
<?php if($columns['favicon']) { ?>
@@ -161,6 +210,8 @@
</table>
</div>
+ <?php } ?>
+
</div>
</section>
diff --git a/app/view/templates/homeopt.php b/app/view/templates/homeopt.php
index d1f70da..ed7315d 100644
--- a/app/view/templates/homeopt.php
+++ b/app/view/templates/homeopt.php
@@ -159,6 +159,8 @@
</div>
+ <input type="hidden" name="display" value="<?= $display ?>">
+
<input type="submit" name="submit" value="filter">
⬅<input type="submit" name="submit" value="reset">
diff --git a/assets/css/home.css b/assets/css/home.css
index c026b27..ef59a53 100644
--- a/assets/css/home.css
+++ b/assets/css/home.css
@@ -108,6 +108,7 @@ div#deepsearchbar {
+
aside .submenu code {
overflow: auto;
display: block;
@@ -128,6 +129,12 @@ main.home div#main {
flex-direction: column;
}
+
+main.home div#graph {
+ height: 1000px;
+ width: 800px;
+}
+
main.home table .id {
font-family: monospace;
font-size: medium;
@@ -218,6 +225,9 @@ h1, h2 {
font-size: larger;
}
+h2 .right {
+ float: right;
+}
p {
@@ -640,3 +650,4 @@ footer {
+
diff --git a/package-lock.json b/package-lock.json
index 6827641..5302d80 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -434,9 +434,9 @@
"dev": true
},
"acorn": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
- "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+ "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
"dev": true
},
"agent-base": {
@@ -1336,6 +1336,14 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
+ "cose-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.1.tgz",
+ "integrity": "sha512-LErvsHUOzYseXGFKWGCAQBTePO1iYZ9JL+YZlmoyqZ7EDcBzrEMRSouOGszQl72J6VK0AVrJbnNCf3eciqy7SA==",
+ "requires": {
+ "layout-base": "^1.0.0"
+ }
+ },
"cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
@@ -1467,6 +1475,23 @@
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true
},
+ "cytoscape": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.14.1.tgz",
+ "integrity": "sha512-s8TaaDzXZ5dMkaoZXrfNRdR053L6MsF+P/Yb4AXpF8Y3VNHZXbuydMpaJTlDG/0HKqs51qHSejTUxV1fyQfU6g==",
+ "requires": {
+ "heap": "^0.2.6",
+ "lodash.debounce": "^4.0.8"
+ }
+ },
+ "cytoscape-cose-bilkent": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
+ "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
+ "requires": {
+ "cose-base": "^1.0.0"
+ }
+ },
"date-now": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
@@ -2223,14 +2248,13 @@
"dev": true
},
"fsevents": {
- "version": "1.2.9",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
- "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
+ "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
"dev": true,
"optional": true,
"requires": {
- "nan": "^2.12.1",
- "node-pre-gyp": "^0.12.0"
+ "node-pre-gyp": "*"
},
"dependencies": {
"abbrev": {
@@ -2278,7 +2302,7 @@
}
},
"chownr": {
- "version": "1.1.1",
+ "version": "1.1.4",
"bundled": true,
"dev": true,
"optional": true
@@ -2308,7 +2332,7 @@
"optional": true
},
"debug": {
- "version": "4.1.1",
+ "version": "3.2.6",
"bundled": true,
"dev": true,
"optional": true,
@@ -2335,12 +2359,12 @@
"optional": true
},
"fs-minipass": {
- "version": "1.2.5",
+ "version": "1.2.7",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "minipass": "^2.2.1"
+ "minipass": "^2.6.0"
}
},
"fs.realpath": {
@@ -2366,7 +2390,7 @@
}
},
"glob": {
- "version": "7.1.3",
+ "version": "7.1.6",
"bundled": true,
"dev": true,
"optional": true,
@@ -2395,7 +2419,7 @@
}
},
"ignore-walk": {
- "version": "3.0.1",
+ "version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true,
@@ -2414,7 +2438,7 @@
}
},
"inherits": {
- "version": "2.0.3",
+ "version": "2.0.4",
"bundled": true,
"dev": true,
"optional": true
@@ -2450,13 +2474,13 @@
}
},
"minimist": {
- "version": "0.0.8",
+ "version": "1.2.5",
"bundled": true,
"dev": true,
"optional": true
},
"minipass": {
- "version": "2.3.5",
+ "version": "2.9.0",
"bundled": true,
"dev": true,
"optional": true,
@@ -2466,42 +2490,42 @@
}
},
"minizlib": {
- "version": "1.2.1",
+ "version": "1.3.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "minipass": "^2.2.1"
+ "minipass": "^2.9.0"
}
},
"mkdirp": {
- "version": "0.5.1",
+ "version": "0.5.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "minimist": "0.0.8"
+ "minimist": "^1.2.5"
}
},
"ms": {
- "version": "2.1.1",
+ "version": "2.1.2",
"bundled": true,
"dev": true,
"optional": true
},
"needle": {
- "version": "2.3.0",
+ "version": "2.3.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "debug": "^4.1.0",
+ "debug": "^3.2.6",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
}
},
"node-pre-gyp": {
- "version": "0.12.0",
+ "version": "0.14.0",
"bundled": true,
"dev": true,
"optional": true,
@@ -2515,11 +2539,11 @@
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
- "tar": "^4"
+ "tar": "^4.4.2"
}
},
"nopt": {
- "version": "4.0.1",
+ "version": "4.0.3",
"bundled": true,
"dev": true,
"optional": true,
@@ -2529,19 +2553,29 @@
}
},
"npm-bundled": {
- "version": "1.0.6",
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"npm-packlist": {
- "version": "1.4.1",
+ "version": "1.4.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1"
+ "npm-bundled": "^1.0.1",
+ "npm-normalize-package-bin": "^1.0.1"
}
},
"npmlog": {
@@ -2606,7 +2640,7 @@
"optional": true
},
"process-nextick-args": {
- "version": "2.0.0",
+ "version": "2.0.1",
"bundled": true,
"dev": true,
"optional": true
@@ -2621,18 +2655,10 @@
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
}
},
"readable-stream": {
- "version": "2.3.6",
+ "version": "2.3.7",
"bundled": true,
"dev": true,
"optional": true,
@@ -2647,7 +2673,7 @@
}
},
"rimraf": {
- "version": "2.6.3",
+ "version": "2.7.1",
"bundled": true,
"dev": true,
"optional": true,
@@ -2674,7 +2700,7 @@
"optional": true
},
"semver": {
- "version": "5.7.0",
+ "version": "5.7.1",
"bundled": true,
"dev": true,
"optional": true
@@ -2727,18 +2753,18 @@
"optional": true
},
"tar": {
- "version": "4.4.8",
+ "version": "4.4.13",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
- "minipass": "^2.3.4",
- "minizlib": "^1.1.1",
+ "minipass": "^2.8.6",
+ "minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
- "yallist": "^3.0.2"
+ "yallist": "^3.0.3"
}
},
"util-deprecate": {
@@ -2763,7 +2789,7 @@
"optional": true
},
"yallist": {
- "version": "3.0.3",
+ "version": "3.1.1",
"bundled": true,
"dev": true,
"optional": true
@@ -2994,6 +3020,11 @@
"minimalistic-assert": "^1.0.1"
}
},
+ "heap": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz",
+ "integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw="
+ },
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -3537,9 +3568,9 @@
}
},
"kind-of": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
- "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true
},
"latest-version": {
@@ -3551,6 +3582,11 @@
"package-json": "^6.3.0"
}
},
+ "layout-base": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
+ "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="
+ },
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
@@ -3593,6 +3629,11 @@
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+ },
"lodash.find": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz",
@@ -3805,9 +3846,9 @@
}
},
"minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mississippi": {
@@ -3850,20 +3891,12 @@
}
},
"mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
+ "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
"dev": true,
"requires": {
- "minimist": "0.0.8"
- },
- "dependencies": {
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- }
+ "minimist": "^1.2.5"
}
},
"move-concurrently": {
@@ -3892,13 +3925,6 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true
},
- "nan": {
- "version": "2.14.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
- "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
- "dev": true,
- "optional": true
- },
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
diff --git a/package.json b/package.json
index ea3dc0e..0448346 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,9 @@
"repository": "github:vincent-peugnet/wcms",
"license": "MIT",
"dependencies": {
- "codemirror": "^5.49.0"
+ "codemirror": "^5.49.0",
+ "cytoscape": "^3.14.1",
+ "cytoscape-cose-bilkent": "^4.1.0"
},
"devDependencies": {
"@sentry/browser": "^5.9.0",
diff --git a/src/home.js b/src/home.js
index 8906079..3dc79ef 100644
--- a/src/home.js
+++ b/src/home.js
@@ -3,6 +3,9 @@ import { checkallHandler, closeSubmenus } from './fn/fn';
window.addEventListener('load', () => {
let checkboxes = document.getElementsByName('pagesid[]');
let checkall = document.getElementById('checkall');
+ if (!checkall) {
+ return;
+ }
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.addEventListener('input', checkallHandler.bind({ checkboxes }));
diff --git a/src/map.js b/src/map.js
new file mode 100644
index 0000000..4723318
--- /dev/null
+++ b/src/map.js
@@ -0,0 +1,32 @@
+import cytoscape from 'cytoscape';
+import coseBilkent from 'cytoscape-cose-bilkent';
+
+cytoscape.use(coseBilkent);
+
+let options = {
+ container: document.getElementById('graph'),
+};
+
+Object.assign(options, data);
+
+let cy = cytoscape(options);
+
+cy.on('tap', 'node', function() {
+ try {
+ // your browser may block popups
+ window.open(this.data('id'));
+ } catch (e) {
+ // fall back on url change
+ window.location.href = this.data('id');
+ }
+});
+
+cy.on('cxttap', 'node', function() {
+ try {
+ // your browser may block popups
+ window.open(this.data('edit'));
+ } catch (e) {
+ // fall back on url change
+ window.location.href = this.data('edit');
+ }
+});
diff --git a/webpack.config.js b/webpack.config.js
index 1c38b9b..f6af5a3 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -16,6 +16,7 @@ module.exports = (env) => {
entry: {
edit: './src/edit.js',
home: './src/home.js',
+ map: './src/map.js',
media: './src/media.js',
sentry: './src/sentry.js',
},