1
0
mirror of https://github.com/typemill/typemill.git synced 2025-10-18 08:07:31 +02:00

Version 1.2.16

This commit is contained in:
trendschau
2019-10-20 12:09:45 +02:00
parent c03a27c10a
commit 62c7650f55
165 changed files with 19328 additions and 17815 deletions

View File

@@ -1,47 +1,47 @@
<?php
namespace Plugins\Analytics;
use \Typemill\Plugin;
class Analytics extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onSettingsLoaded',
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
public function onTwigLoaded()
{
/* get Twig Instance and add the cookieconsent template-folder to the path */
$twig = $this->getTwig();
$loader = $twig->getLoader();
$loader->addPath(__DIR__ . '/templates');
$analyticSettings = $this->settings['settings']['plugins']['analytics'];
if(isset($analyticSettings['tool']))
{
/* fetch the template, render it with twig and add javascript with settings */
if($analyticSettings['tool'] == 'piwik')
{
$this->addInlineJS($twig->fetch('/piwikanalytics.twig', $this->settings));
}
elseif($analyticSettings['tool'] == 'google')
{
$this->addJS('https://www.googletagmanager.com/gtag/js?id=' . $analyticSettings['google_id']);
$this->addInlineJS($twig->fetch('/googleanalytics.twig', $analyticSettings));
}
}
}
<?php
namespace Plugins\Analytics;
use \Typemill\Plugin;
class Analytics extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onSettingsLoaded',
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
public function onTwigLoaded()
{
/* get Twig Instance and add the cookieconsent template-folder to the path */
$twig = $this->getTwig();
$loader = $twig->getLoader();
$loader->addPath(__DIR__ . '/templates');
$analyticSettings = $this->settings['settings']['plugins']['analytics'];
if(isset($analyticSettings['tool']))
{
/* fetch the template, render it with twig and add javascript with settings */
if($analyticSettings['tool'] == 'piwik')
{
$this->addInlineJS($twig->fetch('/piwikanalytics.twig', $this->settings));
}
elseif($analyticSettings['tool'] == 'google')
{
$this->addJS('https://www.googletagmanager.com/gtag/js?id=' . $analyticSettings['google_id']);
$this->addInlineJS($twig->fetch('/googleanalytics.twig', $analyticSettings));
}
}
}
}

View File

@@ -1,38 +1,38 @@
name: Analytics
version: 1.0.0
description: Integrate Piwik or Google Analytics Script
author: Sebastian Schürmanns
homepage: http://typemill.net
licence: MIT
settings:
tool: none
forms:
fields:
tool:
type: radio
label: Choose Your Tool
options:
none: None
piwik: Piwik
google: Google Analytics
piwik_url:
type: text
label: Piwik URL
help: Add the URL to your piwik installation without protocol like this: my-site.com.
placeholder: 'url like my-piwik-installation.com'
piwik_id:
type: number
label: Piwik Site-ID
help: You can find the id in Piwik under configuration and tracking code.
placeholder: 'simple number like 8'
google_id:
type: text
label: Google Tracking ID
help: You can find the tracking id in google under property. It starts with UA-
name: Analytics
version: 1.0.0
description: Integrate Piwik or Google Analytics Script
author: Sebastian Schürmanns
homepage: http://typemill.net
licence: MIT
settings:
tool: none
forms:
fields:
tool:
type: radio
label: Choose Your Tool
options:
none: None
piwik: Piwik
google: Google Analytics
piwik_url:
type: text
label: Piwik URL
help: Add the URL to your piwik installation without protocol like this: my-site.com.
placeholder: 'url like my-piwik-installation.com'
piwik_id:
type: number
label: Piwik Site-ID
help: You can find the id in Piwik under configuration and tracking code.
placeholder: 'simple number like 8'
google_id:
type: text
label: Google Tracking ID
help: You can find the tracking id in google under property. It starts with UA-
placeholder: 'UA-12345-6'

View File

@@ -1,5 +1,5 @@
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ google_id }}');

View File

@@ -1,11 +1,11 @@
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function(){
var u="//{{ settings.plugins.analytics.piwik_url }}/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', '{{ settings.plugins.analytics.piwik_id }}']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function(){
var u="//{{ settings.plugins.analytics.piwik_url }}/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', '{{ settings.plugins.analytics.piwik_id }}']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})();

View File

@@ -1,38 +1,38 @@
<?php
namespace Plugins\CookieConsent;
use \Typemill\Plugin;
class CookieConsent extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onSettingsLoaded',
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
public function onTwigLoaded()
{
/* add external CSS and JavaScript */
$this->addCSS('/cookieconsent/public/cookieconsent.min.css');
$this->addJS('/cookieconsent/public/cookieconsent.min.js');
/* get Twig Instance and add the cookieconsent template-folder to the path */
$twig = $this->getTwig();
$loader = $twig->getLoader();
$loader->addPath(__DIR__ . '/templates');
/* fetch the template, render it with twig and add it as inline-javascript */
$this->addInlineJS($twig->fetch('/cookieconsent.twig', $this->settings));
}
<?php
namespace Plugins\CookieConsent;
use \Typemill\Plugin;
class CookieConsent extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onSettingsLoaded',
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
public function onTwigLoaded()
{
/* add external CSS and JavaScript */
$this->addCSS('/cookieconsent/public/cookieconsent.min.css');
$this->addJS('/cookieconsent/public/cookieconsent.min.js');
/* get Twig Instance and add the cookieconsent template-folder to the path */
$twig = $this->getTwig();
$loader = $twig->getLoader();
$loader->addPath(__DIR__ . '/templates');
/* fetch the template, render it with twig and add it as inline-javascript */
$this->addInlineJS($twig->fetch('/cookieconsent.twig', $this->settings));
}
}

View File

@@ -1,88 +1,88 @@
name: Cookie Consent
version: 1.0.1
description: Enables a cookie consent for websites
author: Sebastian Schürmanns
homepage: https://cookieconsent.insites.com/
licence: MIT
settings:
popup_background: '#70c1b3'
popup_text: '#ffffff'
button_background: '#66b0a3'
button_text: '#ffffff'
theme: 'edgeless'
position: 'bottom'
message: 'This website uses cookies to ensure you get the best experience on our website.'
link: 'Learn More'
href: 'https://cookiesandyou.com/'
dismiss: 'Got It'
forms:
fields:
theme:
type: select
label: Theme
placeholder: 'Add name of theme'
required: true
options:
edgeless: Edgeless
block: Block
classic: Classic
position:
type: select
label: Position of Cookie Banner
options:
bottom: Bottom
top: Top
bottom-left: Bottom left
bottom-right: Bottom right
message:
type: textarea
label: Message
placeholder: 'Message for cookie-popup'
required: true
href:
type: url
label: Link to more informations
placeholder: 'https://cookiesandyou.com/'
required: true
link:
type: text
label: Label for Link
placeholder: 'Link-Lable like More infos'
required: true
dismiss:
type: text
label: Label for Button
placeholder: 'Got it'
required: true
popup_background:
type: color
label: Background Color of Popup
placeholder: 'Add hex color value like #ffffff'
required: true
popup_text:
type: color
label: Text Color of Popup
placeholder: 'Add hex color value like #ffffff'
required: true
button_background:
type: color
label: Background Color of Button
placeholder: 'Add hex color value like #ffffff'
required: true
button_text:
type: color
label: Text Color of Button
placeholder: 'Add hex color value like #ffffff'
name: Cookie Consent
version: 1.0.1
description: Enables a cookie consent for websites
author: Sebastian Schürmanns
homepage: https://cookieconsent.insites.com/
licence: MIT
settings:
popup_background: '#70c1b3'
popup_text: '#ffffff'
button_background: '#66b0a3'
button_text: '#ffffff'
theme: 'edgeless'
position: 'bottom'
message: 'This website uses cookies to ensure you get the best experience on our website.'
link: 'Learn More'
href: 'https://cookiesandyou.com/'
dismiss: 'Got It'
forms:
fields:
theme:
type: select
label: Theme
placeholder: 'Add name of theme'
required: true
options:
edgeless: Edgeless
block: Block
classic: Classic
position:
type: select
label: Position of Cookie Banner
options:
bottom: Bottom
top: Top
bottom-left: Bottom left
bottom-right: Bottom right
message:
type: textarea
label: Message
placeholder: 'Message for cookie-popup'
required: true
href:
type: url
label: Link to more informations
placeholder: 'https://cookiesandyou.com/'
required: true
link:
type: text
label: Label for Link
placeholder: 'Link-Lable like More infos'
required: true
dismiss:
type: text
label: Label for Button
placeholder: 'Got it'
required: true
popup_background:
type: color
label: Background Color of Popup
placeholder: 'Add hex color value like #ffffff'
required: true
popup_text:
type: color
label: Text Color of Popup
placeholder: 'Add hex color value like #ffffff'
required: true
button_background:
type: color
label: Background Color of Button
placeholder: 'Add hex color value like #ffffff'
required: true
button_text:
type: color
label: Text Color of Button
placeholder: 'Add hex color value like #ffffff'
required: true

View File

@@ -1,6 +1,6 @@
.cc-window{opacity:1;transition:opacity 1s ease}.cc-window.cc-invisible{opacity:0}.cc-animate.cc-revoke{transition:transform 1s ease}.cc-animate.cc-revoke.cc-top{transform:translateY(-2em)}.cc-animate.cc-revoke.cc-bottom{transform:translateY(2em)}.cc-animate.cc-revoke.cc-active.cc-bottom,.cc-animate.cc-revoke.cc-active.cc-top,.cc-revoke:hover{transform:translateY(0)}.cc-grower{max-height:0;overflow:hidden;transition:max-height 1s}
.cc-link,.cc-revoke:hover{text-decoration:underline}.cc-revoke,.cc-window{position:fixed;overflow:hidden;box-sizing:border-box;font-family:Helvetica,Calibri,Arial,sans-serif;font-size:16px;line-height:1.5em;display:-ms-flexbox;display:flex;-ms-flex-wrap:nowrap;flex-wrap:nowrap;z-index:9999}.cc-window.cc-static{position:static}.cc-window.cc-floating{padding:2em;max-width:24em;-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner{padding:1em 1.8em;width:100%;-ms-flex-direction:row;flex-direction:row}.cc-revoke{padding:.5em}.cc-header{font-size:18px;font-weight:700}.cc-btn,.cc-close,.cc-link,.cc-revoke{cursor:pointer}.cc-link{opacity:.8;display:inline-block;padding:.2em}.cc-link:hover{opacity:1}.cc-link:active,.cc-link:visited{color:initial}.cc-btn{display:block;padding:.4em .8em;font-size:.9em;font-weight:700;border-width:2px;border-style:solid;text-align:center;white-space:nowrap}.cc-highlight .cc-btn:first-child{background-color:transparent;border-color:transparent}.cc-highlight .cc-btn:first-child:focus,.cc-highlight .cc-btn:first-child:hover{background-color:transparent;text-decoration:underline}.cc-close{display:block;position:absolute;top:.5em;right:.5em;font-size:1.6em;opacity:.9;line-height:.75}.cc-close:focus,.cc-close:hover{opacity:1}
.cc-revoke.cc-top{top:0;left:3em;border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}.cc-revoke.cc-bottom{bottom:0;left:3em;border-top-left-radius:.5em;border-top-right-radius:.5em}.cc-revoke.cc-left{left:3em;right:unset}.cc-revoke.cc-right{right:3em;left:unset}.cc-top{top:1em}.cc-left{left:1em}.cc-right{right:1em}.cc-bottom{bottom:1em}.cc-floating>.cc-link{margin-bottom:1em}.cc-floating .cc-message{display:block;margin-bottom:1em}.cc-window.cc-floating .cc-compliance{-ms-flex:1 0 auto;flex:1 0 auto}.cc-window.cc-banner{-ms-flex-align:center;align-items:center}.cc-banner.cc-top{left:0;right:0;top:0}.cc-banner.cc-bottom{left:0;right:0;bottom:0}.cc-banner .cc-message{display:block;-ms-flex:1 1 auto;flex:1 1 auto;max-width:100%;margin-right:1em}.cc-compliance{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:justify;align-content:space-between}.cc-floating .cc-compliance>.cc-btn{-ms-flex:1;flex:1}.cc-btn+.cc-btn{margin-left:.5em}
@media print{.cc-revoke,.cc-window{display:none}}@media screen and (max-width:900px){.cc-btn{white-space:normal}}@media screen and (max-width:414px) and (orientation:portrait),screen and (max-width:736px) and (orientation:landscape){.cc-window.cc-top{top:0}.cc-window.cc-bottom{bottom:0}.cc-window.cc-banner,.cc-window.cc-floating,.cc-window.cc-left,.cc-window.cc-right{left:0;right:0}.cc-window.cc-banner{-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner .cc-compliance{-ms-flex:1 1 auto;flex:1 1 auto}.cc-window.cc-floating{max-width:none}.cc-window .cc-message{margin-bottom:1em}.cc-window.cc-banner{-ms-flex-align:unset;align-items:unset}.cc-window.cc-banner .cc-message{margin-right:0}}
.cc-floating.cc-theme-classic{padding:1.2em;border-radius:5px}.cc-floating.cc-type-info.cc-theme-classic .cc-compliance{text-align:center;display:inline;-ms-flex:none;flex:none}.cc-theme-classic .cc-btn{border-radius:5px}.cc-theme-classic .cc-btn:last-child{min-width:140px}.cc-floating.cc-type-info.cc-theme-classic .cc-btn{display:inline-block}
.cc-window{opacity:1;transition:opacity 1s ease}.cc-window.cc-invisible{opacity:0}.cc-animate.cc-revoke{transition:transform 1s ease}.cc-animate.cc-revoke.cc-top{transform:translateY(-2em)}.cc-animate.cc-revoke.cc-bottom{transform:translateY(2em)}.cc-animate.cc-revoke.cc-active.cc-bottom,.cc-animate.cc-revoke.cc-active.cc-top,.cc-revoke:hover{transform:translateY(0)}.cc-grower{max-height:0;overflow:hidden;transition:max-height 1s}
.cc-link,.cc-revoke:hover{text-decoration:underline}.cc-revoke,.cc-window{position:fixed;overflow:hidden;box-sizing:border-box;font-family:Helvetica,Calibri,Arial,sans-serif;font-size:16px;line-height:1.5em;display:-ms-flexbox;display:flex;-ms-flex-wrap:nowrap;flex-wrap:nowrap;z-index:9999}.cc-window.cc-static{position:static}.cc-window.cc-floating{padding:2em;max-width:24em;-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner{padding:1em 1.8em;width:100%;-ms-flex-direction:row;flex-direction:row}.cc-revoke{padding:.5em}.cc-header{font-size:18px;font-weight:700}.cc-btn,.cc-close,.cc-link,.cc-revoke{cursor:pointer}.cc-link{opacity:.8;display:inline-block;padding:.2em}.cc-link:hover{opacity:1}.cc-link:active,.cc-link:visited{color:initial}.cc-btn{display:block;padding:.4em .8em;font-size:.9em;font-weight:700;border-width:2px;border-style:solid;text-align:center;white-space:nowrap}.cc-highlight .cc-btn:first-child{background-color:transparent;border-color:transparent}.cc-highlight .cc-btn:first-child:focus,.cc-highlight .cc-btn:first-child:hover{background-color:transparent;text-decoration:underline}.cc-close{display:block;position:absolute;top:.5em;right:.5em;font-size:1.6em;opacity:.9;line-height:.75}.cc-close:focus,.cc-close:hover{opacity:1}
.cc-revoke.cc-top{top:0;left:3em;border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}.cc-revoke.cc-bottom{bottom:0;left:3em;border-top-left-radius:.5em;border-top-right-radius:.5em}.cc-revoke.cc-left{left:3em;right:unset}.cc-revoke.cc-right{right:3em;left:unset}.cc-top{top:1em}.cc-left{left:1em}.cc-right{right:1em}.cc-bottom{bottom:1em}.cc-floating>.cc-link{margin-bottom:1em}.cc-floating .cc-message{display:block;margin-bottom:1em}.cc-window.cc-floating .cc-compliance{-ms-flex:1 0 auto;flex:1 0 auto}.cc-window.cc-banner{-ms-flex-align:center;align-items:center}.cc-banner.cc-top{left:0;right:0;top:0}.cc-banner.cc-bottom{left:0;right:0;bottom:0}.cc-banner .cc-message{display:block;-ms-flex:1 1 auto;flex:1 1 auto;max-width:100%;margin-right:1em}.cc-compliance{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:justify;align-content:space-between}.cc-floating .cc-compliance>.cc-btn{-ms-flex:1;flex:1}.cc-btn+.cc-btn{margin-left:.5em}
@media print{.cc-revoke,.cc-window{display:none}}@media screen and (max-width:900px){.cc-btn{white-space:normal}}@media screen and (max-width:414px) and (orientation:portrait),screen and (max-width:736px) and (orientation:landscape){.cc-window.cc-top{top:0}.cc-window.cc-bottom{bottom:0}.cc-window.cc-banner,.cc-window.cc-floating,.cc-window.cc-left,.cc-window.cc-right{left:0;right:0}.cc-window.cc-banner{-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner .cc-compliance{-ms-flex:1 1 auto;flex:1 1 auto}.cc-window.cc-floating{max-width:none}.cc-window .cc-message{margin-bottom:1em}.cc-window.cc-banner{-ms-flex-align:unset;align-items:unset}.cc-window.cc-banner .cc-message{margin-right:0}}
.cc-floating.cc-theme-classic{padding:1.2em;border-radius:5px}.cc-floating.cc-type-info.cc-theme-classic .cc-compliance{text-align:center;display:inline;-ms-flex:none;flex:none}.cc-theme-classic .cc-btn{border-radius:5px}.cc-theme-classic .cc-btn:last-child{min-width:140px}.cc-floating.cc-type-info.cc-theme-classic .cc-btn{display:inline-block}
.cc-theme-edgeless.cc-window{padding:0}.cc-floating.cc-theme-edgeless .cc-message{margin:2em 2em 1.5em}.cc-banner.cc-theme-edgeless .cc-btn{margin:0;padding:.8em 1.8em;height:100%}.cc-banner.cc-theme-edgeless .cc-message{margin-left:1em}.cc-floating.cc-theme-edgeless .cc-btn+.cc-btn{margin-left:0}

View File

@@ -1,22 +1,22 @@
window.addEventListener("load", function(){
window.cookieconsent.initialise({
"palette": {
"popup": {
"background": "{{ settings.plugins.cookieconsent.popup_background }}",
"text": "{{ settings.plugins.cookieconsent.popup_text }}"
},
"button": {
"background": "{{ settings.plugins.cookieconsent.button_background }}",
"text": "{{ settings.plugins.cookieconsent.button_text }}"
}
},
"theme": "{{ settings.plugins.cookieconsent.theme }}",
"position": "{{ settings.plugins.cookieconsent.position }}",
"content": {
"message": "{{ settings.plugins.cookieconsent.message }}",
"dismiss": "{{ settings.plugins.cookieconsent.dismiss }}",
"link": "{{ settings.plugins.cookieconsent.link }}",
"href": "{{ settings.plugins.cookieconsent.href }}"
}
})
});
window.addEventListener("load", function(){
window.cookieconsent.initialise({
"palette": {
"popup": {
"background": "{{ settings.plugins.cookieconsent.popup_background }}",
"text": "{{ settings.plugins.cookieconsent.popup_text }}"
},
"button": {
"background": "{{ settings.plugins.cookieconsent.button_background }}",
"text": "{{ settings.plugins.cookieconsent.button_text }}"
}
},
"theme": "{{ settings.plugins.cookieconsent.theme }}",
"position": "{{ settings.plugins.cookieconsent.position }}",
"content": {
"message": "{{ settings.plugins.cookieconsent.message }}",
"dismiss": "{{ settings.plugins.cookieconsent.dismiss }}",
"link": "{{ settings.plugins.cookieconsent.link }}",
"href": "{{ settings.plugins.cookieconsent.href }}"
}
})
});

View File

@@ -1,28 +1,28 @@
<?php
namespace Plugins\Highlight;
use \Typemill\Plugin;
class Highlight extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onTwigLoaded()
{
/* add external CSS and JavaScript */
$this->addCSS('/highlight/public/default.css');
$this->addJS('/highlight/public/highlight.pack.js');
/* initialize the script */
$this->addInlineJS('hljs.initHighlightingOnLoad();');
}
<?php
namespace Plugins\Highlight;
use \Typemill\Plugin;
class Highlight extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onTwigLoaded()
{
/* add external CSS and JavaScript */
$this->addCSS('/highlight/public/default.css');
$this->addJS('/highlight/public/highlight.pack.js');
/* initialize the script */
$this->addInlineJS('hljs.initHighlightingOnLoad();');
}
}

View File

@@ -1,6 +1,6 @@
name: Highlight
version: 1.0.0
description: Adds the famous javascript syntax highlighter.
author: Sebastian Schürmanns
homepage: https://highlightjs.org/
name: Highlight
version: 1.0.0
description: Adds the famous javascript syntax highlighter.
author: Sebastian Schürmanns
homepage: https://highlightjs.org/
licence: BSD

View File

@@ -1,24 +1,24 @@
Copyright (c) 2006, Ivan Sagalaev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of highlight.js nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright (c) 2006, Ivan Sagalaev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of highlight.js nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,99 +1,99 @@
/*
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #F0F0F0;
}
/* Base color: saturation 0; */
.hljs,
.hljs-subst {
color: #444;
}
.hljs-comment {
color: #888888;
}
.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}
/* User color: hue: 0 */
.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #880000;
}
.hljs-title,
.hljs-section {
color: #880000;
font-weight: bold;
}
.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #BC6060;
}
/* Language color: hue: 90; */
.hljs-literal {
color: #78A960;
}
.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #397300;
}
/* Meta color: hue: 200 */
.hljs-meta {
color: #1f7199;
}
.hljs-meta-string {
color: #4d99bf;
}
/* Misc effects */
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
/*
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #F0F0F0;
}
/* Base color: saturation 0; */
.hljs,
.hljs-subst {
color: #444;
}
.hljs-comment {
color: #888888;
}
.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}
/* User color: hue: 0 */
.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #880000;
}
.hljs-title,
.hljs-section {
color: #880000;
font-weight: bold;
}
.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #BC6060;
}
/* Language color: hue: 90; */
.hljs-literal {
color: #78A960;
}
.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #397300;
}
/* Meta color: hue: 200 */
.hljs-meta {
color: #1f7199;
}
.hljs-meta-string {
color: #4d99bf;
}
/* Misc effects */
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

File diff suppressed because one or more lines are too long

204
plugins/hyer/getads.php Normal file
View File

@@ -0,0 +1,204 @@
<?php
namespace Plugins\hyer;
use \Typemill\Plugin;
class getads extends Plugin
{
public static function getSubscribedEvents(){}
# get only pro ads for product
public function pro()
{
# get referrer url
if(!isset($_SERVER['HTTP_REFERER']))
{
return $this->returnJsonError(['message' => 'Referrer is missing']);
}
# get the url parameter
$input = $this->getParams();
# create secret
$secret = time();
$secret = substr($secret,0,-1);
$oldsecret = $secret -1;
$secret = md5($secret . $this->getSalt());
$oldsecret = md5($oldsecret . $this->getSalt());
if(!isset($input['access']) OR ($input['access'] != $secret AND $input['access'] != $oldsecret))
{
return $this->returnJsonError(['message' => 'Secret is incorrect']);
}
# get the last segment of url
$referrer = $_SERVER['HTTP_REFERER'];
$segment = basename($referrer);
# get the settings
$settings = \Typemill\Settings::loadSettings();
# get url-map to find the correct cms for requesting page
$urlmap = json_decode($settings['settings']['plugins']['hyer']['urlmap'],true);
# map the request with the map in settings
if(!isset($urlmap[$segment]))
{
return $this->returnJsonError(['message' => 'We could not map that url']);
}
# make call
# $hyerApi = 'http://localhost/typemillService/public/publicapi/proads';
$hyerApi = 'https://service.cmsstash.de/publicapi/proads';
$hyerRequest = ['product' => $urlmap[$segment]];
$result = $this->makeApiCall($hyerRequest, $hyerApi);
return $this->returnJson($result);
}
# get 10 latest pro ads
public function latest()
{
# get the url parameter
$input = $this->getParams();
# create secret
$secret = time();
$secret = substr($secret,0,-1);
$oldsecret = $secret -1;
$secret = md5($secret . $this->getSalt());
$oldsecret = md5($oldsecret . $this->getSalt());
if(!isset($input['access']) OR ($input['access'] != $secret AND $input['access'] != $oldsecret))
{
return $this->returnJsonError(['message' => 'Secret is incorrect']);
}
# $hyerApi = 'http://localhost/typemillService/public/publicapi/latestproads';
$hyerApi = 'https://service.cmsstash.de/publicapi/latestproads';
$hyerRequest = [];
$result = $this->makeApiCall($hyerRequest, $hyerApi);
return $this->returnJson($result);
}
# search for ads from directory
public function search()
{
session_start();
# get the url parameter
$input = $this->getParams();
# validate input here
# read session data for simple csrf check
$token = isset($_SESSION['hyer']) ? $_SESSION['hyer'] : false;
$time = isset($_SESSION['hyer-expire']) ? $_SESSION['hyer-expire'] : false;
# perform simple security or csrf check
if(!isset($input['token']) OR !$token OR !$time OR ($input['token'] != $token ) OR (time() >= $time))
{
return $this->returnJsonError(['message' => 'Die Sitzung ist abgelaufen, bitte laden Sie die Seite neu.']);
}
# $hyerApi = 'http://localhost/typemillService/public/publicapi/searchads';
$hyerApi = 'https://service.cmsstash.de/publicapi/searchads';
$hyerRequest = [];
if(isset($input['product']) && $input['product'] != '')
{
if(!$this->validateLengthBetween($input['product'], 2, 50) OR !$this->validateHtml($input['product']))
{
return $this->returnJsonError(['message' => 'Product is invalid']);
}
$hyerRequest['product'] = $input['product'];
}
if(isset($input['region']) && $input['region'] != '')
{
if(!$this->validateLengthBetween($input['region'], 2, 50) OR !$this->validateHtml($input['region']))
{
return $this->returnJsonError(['message' => 'Product is invalid']);
}
$hyerRequest['region'] = $input['region'];
}
$result = $this->makeApiCall($hyerRequest, $hyerApi);
if($result)
{
return $this->returnJson($result);
}
return $this->returnJsonError(['message' => 'Kein Ergebnis']);
}
private function makeApiCall($hyerRequest, $hyerApi)
{
# get the settings
$settings = \Typemill\Settings::loadSettings();
# get api key from settings
$apikey = $settings['settings']['plugins']['hyer']['apikey'];
$siteid = $settings['settings']['plugins']['hyer']['siteid'];
# use key 'http' even if you send the request to https://...
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Authorization: Basic " . base64_encode($siteid . ":". $apikey),
'method' => 'GET',
'content' => http_build_query($hyerRequest),
'timeout' => 5
)
);
ini_set('allow_url_fopen', '1');
$context = stream_context_create($options);
$result = file_get_contents($hyerApi, false, $context);
$result = json_decode($result);
ini_set('allow_url_fopen', '0');
return $result;
}
private function validateHtml($value)
{
if($value == strip_tags($value))
{
return true;
}
return false;
}
private function validateLengthBetween($value, $min, $max)
{
$length = $this->stringLength($value);
return ($length !== false) && $length >= $min && $length <= $max;
}
private function stringLength($value)
{
if (!is_string($value)) {
return false;
} elseif (function_exists('mb_strlen')) {
return mb_strlen($value);
}
return strlen($value);
}
private function getSalt()
{
return "asPx9Derf2";
}
}

221
plugins/hyer/hyer.php Normal file
View File

@@ -0,0 +1,221 @@
<?php
namespace Plugins\hyer;
use \Typemill\Plugin;
class Hyer extends Plugin
{
protected $item;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onsettingsLoaded',
'onItemLoaded' => 'onItemLoaded',
'onContentArrayLoaded' => 'onContentArrayLoaded',
);
}
public static function addNewRoutes()
{
# the route for the api calls
return array(
array(
'httpMethod' => 'get',
'route' => '/latestadsfe30s8edw4wdkp',
'class' => 'Plugins\hyer\getads:latest'
),
array(
'httpMethod' => 'get',
'route' => '/proadsfe30s8edw4wdkp',
'class' => 'Plugins\hyer\getads:pro'
),
array(
'httpMethod' => 'get',
'route' => '/searchadsfe30s8edw4wdkp',
'class' => 'Plugins\hyer\getads:search'
)
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
public function onItemLoaded($item)
{
$this->item = $item->getData();
}
public function onContentArrayLoaded($contentArray)
{
# get content array
$content = $contentArray->getData();
$settings = $this->getPluginSettings('hyer');
$path = $this->getPath();
$segment = basename($path); # get the last url segment
$urlmap = json_decode($settings['urlmap'],true); # to find the correct page to include app
$directory = trim($settings['directory'],"/"); # the path for the directory
$salt = "asPx9Derf2";
# if we are on the directory page
if(trim($path,"/") == trim($settings['directory'],"/"))
{
# activate axios and vue in frontend
$this->activateAxios();
$this->activateVue();
$this->activateTachyons();
# add the css and vue application
$this->addCSS('/hyer/public/hyer.css');
$this->addJS('/hyer/public/hyer.js');
$twig = $this->getTwig(); // get the twig-object
$loader = $twig->getLoader(); // get the twig-template-loader
$loader->addPath(__DIR__ . '/templates');
$svg = $twig->fetch('/hyer.twig');
# simple security for first request
$secret = time();
$secret = substr($secret,0,-1);
$secret = md5($secret . $salt);
# simple csrf protection with a session for long following requests
session_start();
$length = 32;
$token = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
$_SESSION['hyer'] = $token;
$_SESSION['hyer-expire'] = time() + 1300; # 60 seconds * 30 minutes
# use this to secure the api long term
# $token = time();
# $token = substr($token,0,-4);
# $token = md5($token . $salt);
$products = '';
foreach($urlmap as $product)
{
$products .= $product . ',';
}
$products = trim($products, ",");
# create div for vue app
$textHyer = '<div data-access="' . $secret . '" data-token="' . $token . '" data-products="' . $products . '" id="hyerapp"><directory></directory></div>';
# create content type
$textHyer = Array
(
'rawHtml' => $svg . $textHyer,
'allowRawHtmlInSafeMode' => true,
'autobreak' => 1
);
$content[] = $textHyer;
}
# map the url with the map in settings
elseif(isset($urlmap[$segment]))
{
# activate axios and vue in frontend
$this->activateAxios();
$this->activateVue();
$this->activateTachyons();
# add the css and vue application
$this->addCSS('/hyer/public/hyer.css');
$this->addJS('/hyer/public/hyer.js');
$twig = $this->getTwig(); // get the twig-object
$loader = $twig->getLoader(); // get the twig-template-loader
$loader->addPath(__DIR__ . '/templates');
$svg = $twig->fetch('/hyer.twig');
# use this to secure the api a bit
$secret = time();
$secret = substr($secret,0,-1);
$secret = md5($secret . $salt);
# create div for vue app
$textHyer = '<div data-access="' . $secret . '" id="hyerapp"><premiumads></premiumads></div>';
$textHeadline = (isset($settings['headline']) && !empty($settings['headline'])) ? $settings['headline'] : false;
$textTeaser = (isset($settings['teaser']) && !empty($settings['teaser'])) ? $settings['teaser'] : false;
# create content type
$textHyer = Array
(
'rawHtml' => $svg . $textHyer,
'allowRawHtmlInSafeMode' => true,
'autobreak' => 1
);
if($textHeadline)
{
$textHeadline = array(
'name' => 'h2',
'text' => $textHeadline,
'handler' => 'line',
'attributes' => Array
(
'id' => $textHeadline
)
);
}
if($textTeaser)
{
$textTeaser = array(
'name' => 'p',
'handler' => Array
(
'function' => 'lineElements',
'argument' => $textTeaser,
'destination' => 'elements'
)
);
}
$length = count($content);
$thisElement = 0;
$pos = false;
$i = 0;
$position = isset($settings['position']) ? $settings['position'] : 3;
while($i < $length)
{
if($content[$i]['name'] == 'h2')
{
$thisElement = $thisElement + 1;
}
# place hyer app before the 3rd headline h2-level
if($thisElement == $position)
{
$pos = $i;
break;
}
$i++;
}
if($pos)
{
$start = array_slice($content, 0, $pos);
$end = array_slice($content, $pos);
if($textHeadline){ $start[] = $textHeadline; }
if($textTeaser){ $start[] = $textTeaser; }
$content = array_merge( $start, array($textHyer), $end );
}
else
{
if($textHeadline){ $content[] = $textHeadline; }
if($textTeaser){ $content[] = $textTeaser; }
$content[] = $textHyer;
}
}
$contentArray->setData($content);
}
}

44
plugins/hyer/hyer.yaml Normal file
View File

@@ -0,0 +1,44 @@
name: HYER SPACE
version: 1.0.0
description: Integrate HYER Text-Adds into typemill.
author: Sebastian Schürmanns
homepage: https://typemill.net
licence: MIT
forms:
fields:
apikey:
type: text
label: API KEY
placeholder:
required: true
siteid:
type: text
label: Site ID
placeholder:
required: true
urlmap:
type: textarea
label: URL Map
placeholder: '180 characters'
required: true
directory:
type: text
label: Path to directory
placeholder: 'path/to/directory'
headline:
type: text
label: Headline before ad-block on review page
teaser:
type: textarea
label: Teaser before ad-block on review page
position:
type: number
label: Position for the ad-block (before xyz h2 headlines)

View File

@@ -0,0 +1,64 @@
/**********************
** ICONS **
**********************/
.icon {
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 0;
stroke: currentColor;
fill: currentColor;
}
.icon-users {
width: 1.125em;
}
.bg-customized{
background: #f9f8f6;
}
.bg-customized-2{
background: #ff4136;
}
.b--customized-2{
border-color: #ff4136;
}
.customized-2{
color: #ff4136;
}
.custombutton{
line-height: 1.55rem;
}
.link, .link:active, .link:focus, .link:hover, .link:link, .link:visited {
transition: color .15s ease-in;
color: #000;
text-decoration: none;
}
.link-white, .link-white.active, .link-white:focus, .link-white:hover, .link-white:link, .link-white:visited{
color: #fff;
}
select{
background-color: #f9f8f6;
}
select{
/* reset */
-webkit-appearance: none;
-moz-appearance: none;
/* style */
background-image:
linear-gradient(45deg, transparent 50%, #444 50%),
linear-gradient(135deg, #444 50%, transparent 50%),
linear-gradient(to right, transparent, transparent);
background-position:
calc(100% - 20px) calc(1em + 6px),
calc(100% - 15px) calc(1em + 6px),
100% 0;
background-size:
5px 5px,
5px 5px,
2.8em 2.8em;
background-repeat: no-repeat;
cursor: pointer;
}
.shadow-hover:hover{
box-shadow: 0px 0px 2px 0px rgba(158,158,158,1);
}

290
plugins/hyer/public/hyer.js Normal file
View File

@@ -0,0 +1,290 @@
const myaxios = axios.create({
baseURL: document.getElementsByTagName("base")[0].href
});
Vue.component('directory', {
data: function () {
return {
ads: [],
products: [],
selectedProduct: "",
selectedRegion: "",
token: "",
message: false,
messageType: '',
}
},
template: '<div>' +
'<div>' +
'<form action="#">' +
'<fieldset class="bn ma0 pa0">' +
'<div class="flex-ns items-end justify-around">' +
'<div class="w-100 mr3-ns">' +
'<label for="country" class="f6 b db mb2 mt4 tl">CMS</label>' +
'<select v-model="selectedProduct" class="input-reset outline-focus ba b--black-20 ph2 pv3 mb2 db w-100" name="location[index][country]" v-model="location.country" required>' +
'<option value="">-</option>' +
'<option v-for="product in products">{{ product }}</option>' +
'</select>' +
'</div>' +
'<div class="w-100 mr3-ns">' +
'<label for="region" class="f6 b db mb2 mt4 tl">Region</label>' +
'<select v-model="selectedRegion" class="input-reset outline-focus ba b--black-20 ph2 pv3 mb2 db w-100" name="location[index][region]" v-model="location.region" required>' +
'<option value="">-</option>' +
'<option>Baden-Würtemberg</option>' +
'<option>Bayern</option>' +
'<option>Berlin</option>' +
'<option>Brandenburg</option>' +
'<option>Bremen</option>' +
'<option>Hamburg</option>' +
'<option>Hessen</option>' +
'<option>Mecklenburg-Vorpommern</option>' +
'<option>Niedersachsen</option>' +
'<option>Nordrhein-Westfalen</option>' +
'<option>Rheinland-Pfalz</option>' +
'<option>Saarland</option>' +
'<option>Sachsen</option>' +
'<option>Sachsen-Anhalt</option>' +
'<option>Schleswig-Holstein</option>' +
'<option>Thüringen</option>' +
'</select>' +
'</div>' +
'<button @click.prevent="searchProducts" class="w-100 w-30-ns link bg-customized-2 custombutton link-white dim bn mb2 pa3 mt3 mt0-ns pointer">Suchen</button>' +
'</div>' +
'</fieldset>' +
'</form>' +
'</div>' +
'<div v-if="message" class="w-100 pa3 tc mv3 customized-2 ba b--customized-2">{{ message }}</div>' +
'<div v-for="ad in ads">' +
'<div class="w-100 bg-customized dark-gray flex dib tl pa0 mv3 f5 relative">' +
'<div class="w-100 ba b--light-gray pa0 shadow-hover">' +
'<a :href="getLink(ad)" rel="ugc sponsored" target="_blank" class="link dark-gray pa0 mt3 mb0">' +
'<div v-if="ad.pro" class="absolute-ns tc top-0 right-0 ba b--dark-gray pa1 f6 ma3">Premium</div>' +
'<h3 class="mt4 mb1 pv0 ph3 underline-hover">{{ getTitle(ad) }}</h3>' +
'<small class="pv0 ph3 ma0 underline-hover">{{ getLink(ad) }}</small>' +
'<p class="mt1 mb3 pv0 ph3 underline-hover">{{ getTeaser(ad) }}</p>' +
'</a>' +
'<div class="f5 pv2 ph3 bt dark-gray b--moon-gray w-100 flex">' +
'<div class="tl w-50">' +
'<svg class="icon icon-location"><use xlink:href="#icon-location"></use></svg> {{ ad.city }} ' +
'<span class="pl2"><svg class="icon icon-location"><use xlink:href="#icon-users"></use></svg> {{ ad.size}}</span>' +
'</div>' +
'<div class="tr w-50">Web:' +
'<a class="link dark-gray ph2" rel="ugc sponsored" :href="ad.company_link" target="_blank"><svg class="icon icon-location"><use xlink:href="#icon-home"></use></svg></a>' +
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_1" :href="ad.sm_link_1" target="_blank"><svg :class="getSocialIcon(ad.sm_link_1)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_1, true)"></use></svg></a>' +
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_2" :href="ad.sm_link_2" target="_blank"><svg :class="getSocialIcon(ad.sm_link_2)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_2, true)"></use></svg></a>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'<div class="w-100 bg-white dark-gray dib tl pa0 mv3 f5">' +
'<div class="w-100 ba b--light-gray pa0 tc">' +
'<h3 class="mt4 mb0 pv0 ph3">Web-Agentur oder Freelancer?</h3>' +
'<p class="mt3 mb3 pv0 ph3">Dann ist genau hier Ihre Expertise gefragt!<br/> Mit einem Dienstleister-Eintrag auf cmsstash.de</p>' +
'<a href="https://service.cmsstash.de" class="link bg-customized-2 link-white dim ba1 b--red mt0 mb4 ph3 pv2 dib br1">Eintrag erstellen</a>' +
'</div>' +
'</div>' +
'</div>',
mounted: function(){
var products = document.getElementById("hyerapp").dataset.products;
this.products = products.split(',');
this.token = document.getElementById("hyerapp").dataset.token;
self = this;
var access = document.getElementById("hyerapp").dataset.access;
myaxios.get('/latestadsfe30s8edw4wdkp?access='+access)
.then(function (response) {
self.ads = response.data.ads;
/* if matomo is on, check dom for new links */
if(_paq)
{
Vue.nextTick(function () {
_paq.push(['enableLinkTracking']);
})
}
})
.catch(function (error) {});
},
methods: {
searchProducts: function()
{
self = this;
myaxios.get('/searchadsfe30s8edw4wdkp',{
params: {
token: this.token,
product: this.selectedProduct,
region: this.selectedRegion
}
})
.then(function (response) {
self.ads = response.data.ads;
/* if matomo is on, check dom for new links */
if(_paq)
{
Vue.nextTick(function () {
_paq.push(['enableLinkTracking']);
})
}
})
.catch(function (error)
{
if(error.response)
{
self.message = error.response.data.message;
self.messageType = 'error';
}
});
},
getTitle: function(product)
{
if(product.ad_title)
{
return product.ad_title;
}
return product.company_name;
},
getTeaser: function(product)
{
if(product.ad_teaser)
{
return product.ad_teaser;
}
return product.company_description;
},
getLink: function(product)
{
if(product.ad_link)
{
return product.ad_link;
}
return product.company_link;
},
getSocialIcon: function(icon,id)
{
var prefix = id ? '#' : '';
if(icon.indexOf("twitter") > -1){ return prefix+'icon-twitter' }
else if(icon.indexOf("facebook") > -1){ return prefix+'icon-facebook' }
else if(icon.indexOf("xing") > -1){ return prefix+'icon-xing2' }
else if(icon.indexOf("linkedin") > -1){ return prefix+'icon-linkedin2' }
return prefix+'icon-link';
},
}
});
Vue.component('premiumads', {
data: function () {
return {
ads: [],
fill: [],
}
},
template: '<div>' +
'<div v-for="ad in ads">' +
'<div class="w-100 bg-customized dark-gray flex dib tl pa0 mv3 f5 relative">' +
'<div class="w-100 ba b--light-gray pa0 shadow-hover">' +
'<a rel="ugc sponsored" :href="getLink(ad)" target="_blank" class="link dark-gray pa0 mt3 mb0">' +
'<div v-if="ad.pro" class="absolute-ns tc top-0 right-0 ba b--dark-gray pa1 f6 ma3">Premium</div>' +
'<h3 class="mt4 mb1 pv0 ph3 underline-hover">{{ getTitle(ad) }}</h3>' +
'<small class="pv0 ph3 ma0 underline-hover">{{ getLink(ad) }}</small>' +
'<p class="mt1 mb3 pv0 ph3 underline-hover">{{ getTeaser(ad) }}</p>' +
'</a>' +
'<div class="f5 pv2 ph3 bt dark-gray b--moon-gray w-100 flex">' +
'<div class="tl w-50">' +
'<svg class="icon icon-location"><use xlink:href="#icon-location"></use></svg> {{ ad.city }} ' +
'<span class="pl2"><svg class="icon icon-location"><use xlink:href="#icon-users"></use></svg> {{ ad.size}}</span>' +
'</div>' +
'<div class="tr w-50">Web:' +
'<a class="link dark-gray ph2" rel="ugc sponsored" :href="ad.company_link" target="_blank"><svg class="icon icon-location"><use xlink:href="#icon-home"></use></svg></a>' +
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_1" :href="ad.sm_link_1" target="_blank"><svg :class="getSocialIcon(ad.sm_link_1)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_1, true)"></use></svg></a>' +
'<a class="link dark-gray ph2" rel="ugc sponsored" v-if="ad.sm_link_2" :href="ad.sm_link_2" target="_blank"><svg :class="getSocialIcon(ad.sm_link_2)" class="icon"><use :xlink:href="getSocialIcon(ad.sm_link_2, true)"></use></svg></a>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'<div v-if="ads.length < 3">' +
'<div class="w-100 bg-white dark-gray dib tl pa0 mv3 f5">' +
'<div class="w-100 ba b--light-gray pa0 tc">' +
'<h3 class="mt4 mb0 pv0 ph3">Web-Agentur oder Freelancer?</h3>' +
'<p class="mt3 mb3 pv0 ph3">Dann ist genau hier Ihre Expertise gefragt!<br/> Mit einem Dienstleister-Eintrag auf cmsstash.de</p>' +
'<a href="https://service.cmsstash.de" class="link bg-customized-2 link-white dim ba1 b--red mt0 mb4 ph3 pv2 dib br1">Eintrag erstellen</a>' +
'</div>' +
'</div>' +
'</div>' +
'</div>',
mounted: function(){
self = this;
var access = document.getElementById("hyerapp").dataset.access;
myaxios.get('/proadsfe30s8edw4wdkp?access='+access)
.then(function (response) {
self.ads = response.data.ads;
var restads = 3 - self.ads.length;
for(var i = 0; i < restads; i++)
{
self.fill.push(1);
}
/* if matomo is on, check dom for new links */
if(_paq)
{
Vue.nextTick(function () {
_paq.push(['enableLinkTracking']);
})
}
})
.catch(function (error) {
});
},
methods: {
getTitle: function(product)
{
if(product.ad_title)
{
return product.ad_title;
}
return product.company_name;
},
getTeaser: function(product)
{
if(product.ad_teaser)
{
return product.ad_teaser;
}
return product.company_description;
},
getLink: function(product)
{
if(product.ad_link)
{
return product.ad_link;
}
return product.company_link;
},
getSocialIcon: function(icon,id)
{
var prefix = id ? '#' : '';
if(icon.indexOf("twitter") > -1){ return prefix+'icon-twitter' }
else if(icon.indexOf("facebook") > -1){ return prefix+'icon-facebook' }
else if(icon.indexOf("xing") > -1){ return prefix+'icon-xing2' }
else if(icon.indexOf("linkedin") > -1){ return prefix+'icon-linkedin2' }
return prefix+'icon-link';
},
}
});
var app = new Vue({
el: "#hyerapp",
data: {
disabled: false,
message: '',
messageType: '',
},
});

View File

@@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" style="display:none">
<symbol id="icon-facebook" viewBox="0 0 16 16">
<title>facebook</title>
<path d="M9.5 3h2.5v-3h-2.5c-1.93 0-3.5 1.57-3.5 3.5v1.5h-2v3h2v8h3v-8h2.5l0.5-3h-3v-1.5c0-0.271 0.229-0.5 0.5-0.5z"></path>
</symbol>
<symbol id="icon-home" viewBox="0 0 16 16">
<title>home</title>
<path d="M16 9.226l-8-6.21-8 6.21v-2.532l8-6.21 8 6.21zM14 9v6h-4v-4h-4v4h-4v-6l6-4.5z"></path>
</symbol>
<symbol id="icon-location" viewBox="0 0 16 16">
<title>location</title>
<path d="M8 0c-2.761 0-5 2.239-5 5 0 5 5 11 5 11s5-6 5-11c0-2.761-2.239-5-5-5zM8 8c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"></path>
</symbol>
<symbol id="icon-users" viewBox="0 0 18 16">
<title>users</title>
<path d="M12 12.041v-0.825c1.102-0.621 2-2.168 2-3.716 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h14c0-2.015-2.608-3.682-6-3.959z"></path>
<path d="M5.112 12.427c0.864-0.565 1.939-0.994 3.122-1.256-0.235-0.278-0.449-0.588-0.633-0.922-0.475-0.863-0.726-1.813-0.726-2.748 0-1.344 0-2.614 0.478-3.653 0.464-1.008 1.299-1.633 2.488-1.867-0.264-1.195-0.968-1.98-2.841-1.98-3 0-3 2.015-3 4.5 0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h4.359c0.227-0.202 0.478-0.393 0.753-0.573z"></path>
</symbol>
<symbol id="icon-twitter" viewBox="0 0 16 16">
<title>twitter</title>
<path d="M16 3.538c-0.588 0.263-1.222 0.438-1.884 0.516 0.678-0.406 1.197-1.050 1.444-1.816-0.634 0.375-1.338 0.65-2.084 0.797-0.6-0.638-1.453-1.034-2.397-1.034-1.813 0-3.281 1.469-3.281 3.281 0 0.256 0.028 0.506 0.084 0.747-2.728-0.138-5.147-1.444-6.766-3.431-0.281 0.484-0.444 1.050-0.444 1.65 0 1.138 0.578 2.144 1.459 2.731-0.538-0.016-1.044-0.166-1.488-0.409 0 0.013 0 0.028 0 0.041 0 1.591 1.131 2.919 2.634 3.219-0.275 0.075-0.566 0.116-0.866 0.116-0.212 0-0.416-0.022-0.619-0.059 0.419 1.303 1.631 2.253 3.066 2.281-1.125 0.881-2.538 1.406-4.078 1.406-0.266 0-0.525-0.016-0.784-0.047 1.456 0.934 3.181 1.475 5.034 1.475 6.037 0 9.341-5.003 9.341-9.341 0-0.144-0.003-0.284-0.009-0.425 0.641-0.459 1.197-1.038 1.637-1.697z"></path>
</symbol>
<symbol id="icon-linkedin2" viewBox="0 0 16 16">
<title>linkedin2</title>
<path d="M6 6h2.767v1.418h0.040c0.385-0.691 1.327-1.418 2.732-1.418 2.921 0 3.461 1.818 3.461 4.183v4.817h-2.885v-4.27c0-1.018-0.021-2.329-1.5-2.329-1.502 0-1.732 1.109-1.732 2.255v4.344h-2.883v-9z"></path>
<path d="M1 6h3v9h-3v-9z"></path>
<path d="M4 3.5c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.672-1.5-1.5c0-0.828 0.672-1.5 1.5-1.5s1.5 0.672 1.5 1.5z"></path>
</symbol>
<symbol id="icon-xing2" viewBox="0 0 16 16">
<title>xing2</title>
<path d="M2.431 3.159c-0.138 0-0.256 0.050-0.316 0.144-0.059 0.1-0.050 0.225 0.013 0.353l1.559 2.7c0.003 0.006 0.003 0.009 0 0.013l-2.45 4.331c-0.063 0.128-0.059 0.256 0 0.353 0.059 0.094 0.163 0.156 0.3 0.156h2.306c0.344 0 0.513-0.234 0.628-0.447 0 0 2.397-4.241 2.491-4.406-0.009-0.016-1.588-2.766-1.588-2.766-0.116-0.203-0.287-0.431-0.644-0.431h-2.3z"></path>
<path d="M12.125 0c-0.344 0-0.494 0.216-0.619 0.441 0 0-4.972 8.816-5.134 9.106 0.009 0.016 3.278 6.016 3.278 6.016 0.116 0.203 0.291 0.441 0.644 0.441h2.306c0.137 0 0.247-0.053 0.306-0.147 0.063-0.1 0.059-0.228-0.006-0.356l-3.25-5.947c-0.003-0.006-0.003-0.009 0-0.016l5.109-9.034c0.063-0.128 0.066-0.256 0.006-0.356-0.059-0.094-0.169-0.147-0.306-0.147h-2.334z"></path>
</symbol>
<symbol id="icon-cart" viewBox="0 0 16 16">
<title>cart</title>
<path d="M6 14.5c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.672-1.5-1.5c0-0.828 0.672-1.5 1.5-1.5s1.5 0.672 1.5 1.5z"></path>
<path d="M16 14.5c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.672-1.5-1.5c0-0.828 0.672-1.5 1.5-1.5s1.5 0.672 1.5 1.5z"></path>
<path d="M16 8v-6h-12c0-0.552-0.448-1-1-1h-3v1h2l0.751 6.438c-0.458 0.367-0.751 0.93-0.751 1.562 0 1.105 0.895 2 2 2h12v-1h-12c-0.552 0-1-0.448-1-1 0-0.003 0-0.007 0-0.010l13-1.99z"></path>
</symbol>
<symbol id="icon-warning" viewBox="0 0 16 16">
<title>warning</title>
<path d="M8 1.45l6.705 13.363h-13.409l6.705-13.363zM8 0c-0.345 0-0.69 0.233-0.951 0.698l-6.829 13.611c-0.523 0.93-0.078 1.691 0.989 1.691h13.583c1.067 0 1.512-0.761 0.989-1.691h0l-6.829-13.611c-0.262-0.465-0.606-0.698-0.951-0.698v0z"></path>
<path d="M9 13c0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.552 0.448-1 1-1s1 0.448 1 1z"></path>
<path d="M8 11c-0.552 0-1-0.448-1-1v-3c0-0.552 0.448-1 1-1s1 0.448 1 1v3c0 0.552-0.448 1-1 1z"></path>
</symbol>
<symbol id="icon-coin-euro" viewBox="0 0 16 16">
<title>coin-euro</title>
<path d="M7.5 1c-4.142 0-7.5 3.358-7.5 7.5s3.358 7.5 7.5 7.5c4.142 0 7.5-3.358 7.5-7.5s-3.358-7.5-7.5-7.5zM7.5 14.5c-3.314 0-6-2.686-6-6s2.686-6 6-6c3.314 0 6 2.686 6 6s-2.686 6-6 6z"></path>
<path d="M10.482 10.068c-0.239-0.139-0.545-0.058-0.684 0.181-0.27 0.463-0.767 0.751-1.298 0.751h-2c-0.652 0-1.208-0.418-1.414-1h2.414c0.276 0 0.5-0.224 0.5-0.5s-0.224-0.5-0.5-0.5h-2.5v-1h2.5c0.276 0 0.5-0.224 0.5-0.5s-0.224-0.5-0.5-0.5h-2.414c0.206-0.582 0.762-1 1.414-1h2c0.531 0 1.028 0.288 1.298 0.751 0.139 0.239 0.445 0.32 0.684 0.181s0.32-0.445 0.181-0.684c-0.448-0.77-1.277-1.249-2.162-1.249h-2c-1.207 0-2.217 0.86-2.45 2h-0.55c-0.276 0-0.5 0.224-0.5 0.5s0.224 0.5 0.5 0.5h0.5v1h-0.5c-0.276 0-0.5 0.224-0.5 0.5s0.224 0.5 0.5 0.5h0.55c0.232 1.14 1.242 2 2.45 2h2c0.886 0 1.714-0.478 2.162-1.249 0.139-0.239 0.058-0.545-0.181-0.684z"></path>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -1,47 +1,47 @@
<?php
namespace Plugins\Math;
use \Typemill\Plugin;
class Math extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onSettingsLoaded',
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
public function onTwigLoaded()
{
$mathSettings = $this->settings['settings']['plugins']['math'];
if($mathSettings['tool'] == 'mathjax')
{
/* add external CSS and JavaScript */
$this->addJS('https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/latest.js?config=TeX-MML-AM_CHTML');
}
if($mathSettings['tool'] == 'katex')
{
$this->addJS('/math/public/katex.min.js');
$this->addJS('/math/public/auto-render.min.js');
$this->addCSS('/math/public/katex.min.css');
/* initialize autorendering of page only in frontend */
if (strpos($this->getPath(), 'tm/content') === false)
{
$this->addInlineJs('renderMathInElement(document.body);');
}
}
}
<?php
namespace Plugins\Math;
use \Typemill\Plugin;
class Math extends Plugin
{
protected $settings;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onSettingsLoaded',
'onTwigLoaded' => 'onTwigLoaded'
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
public function onTwigLoaded()
{
$mathSettings = $this->settings['settings']['plugins']['math'];
if($mathSettings['tool'] == 'mathjax')
{
/* add external CSS and JavaScript */
$this->addJS('https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/latest.js?config=TeX-MML-AM_CHTML');
}
if($mathSettings['tool'] == 'katex')
{
$this->addJS('/math/public/katex.min.js');
$this->addJS('/math/public/auto-render.min.js');
$this->addCSS('/math/public/katex.min.css');
/* initialize autorendering of page only in frontend */
if (strpos($this->getPath(), 'tm/content') === false)
{
$this->addInlineJs('renderMathInElement(document.body);');
}
}
}
}

View File

@@ -1,20 +1,20 @@
name: Math
version: 1.0.2
description: Adds support for katex and mathjax.
author: Sebastian Schürmanns
homepage: https://mathjax.org/
licence: Apache 2.0 / MIT
settings:
tool: none
forms:
fields:
tool:
type: radio
label: Choose Your Tool
options:
none: None
katex: KaTex
name: Math
version: 1.0.2
description: Adds support for katex and mathjax.
author: Sebastian Schürmanns
homepage: https://mathjax.org/
licence: Apache 2.0 / MIT
settings:
tool: none
forms:
fields:
tool:
type: radio
label: Choose Your Tool
options:
none: None
katex: KaTex
mathjax: MathJax

View File

@@ -1,140 +1,140 @@
# [<img src="https://khan.github.io/KaTeX/katex-logo.svg" width="130" alt="KaTeX">](https://khan.github.io/KaTeX/)
[![Build Status](https://travis-ci.org/Khan/KaTeX.svg?branch=master)](https://travis-ci.org/Khan/KaTeX)
[![codecov](https://codecov.io/gh/Khan/KaTeX/branch/master/graph/badge.svg)](https://codecov.io/gh/Khan/KaTeX)
[![Join the chat at https://gitter.im/Khan/KaTeX](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Khan/KaTeX?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](http://www.intmath.com/cg5/katex-mathjax-comparison.php).
* **Print quality:** KaTeXs layout is based on Donald Knuths TeX, the gold standard for math typesetting.
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
KaTeX supports all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 9 - IE 11. More information can be found on the [list of supported commands](https://khan.github.io/KaTeX/function-support.html) and on the [wiki](https://github.com/khan/katex/wiki).
## Usage
You can [download KaTeX](https://github.com/khan/katex/releases) and host it on your server or include the `katex.min.js` and `katex.min.css` files on your page directly from a CDN:
```html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.css" integrity="sha384-VEnyslhHLHiYPca9KFkBB3CMeslnM9CzwjxsEbZTeA21JBm7tdLwKoZmCt3cZTYD" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.js" integrity="sha384-O4hpKqcplNCe+jLuBVEXC10Rn1QEqAmX98lKAIFBEDxZI0a+6Z2w2n8AEtQbR4CD" crossorigin="anonymous"></script>
```
#### In-browser rendering
Call `katex.render` with a TeX expression and a DOM element to render into:
```js
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element);
```
If KaTeX can't parse the expression, it throws a `katex.ParseError` error.
#### Server side rendering or rendering to a string
To generate HTML on the server or to generate an HTML string of the rendered math, you can use `katex.renderToString`:
```js
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}");
// '<span class="katex">...</span>'
```
Make sure to include the CSS and font files, but there is no need to include the JavaScript. Like `render`, `renderToString` throws if it can't parse the expression.
#### Security
Any HTML generated by KaTeX *should* be safe from `<script>` or other code
injection attacks.
(See `maxSize` below for preventing large width/height visual affronts.)
Of course, it is always a good idea to sanitize the HTML, though you will need
a rather generous whitelist (including some of SVG and MathML) to support
all of KaTeX.
#### Handling errors
If KaTeX encounters an error (invalid or unsupported LaTeX), then it will
throw an exception of type `katex.ParseError`. The message in this error
includes some of the LaTeX source code, so needs to be escaped if you want
to render it to HTML. In particular, you should convert `&`, `<`, `>`
characters to `&amp;`, `&lt;`, `&gt;` (e.g., using `_.escape`)
before including either LaTeX source code or exception messages in your
HTML/DOM. (Failure to escape in this way makes a `<script>` injection
attack possible if your LaTeX source is untrusted.)
#### Rendering options
You can provide an object of options as the last argument to `katex.render` and `katex.renderToString`. Available options are:
- `displayMode`: `boolean`. If `true` the math will be rendered in display mode, which will put the math in display style (so `\int` and `\sum` are large, for example), and will center the math on the page on its own line. If `false` the math will be rendered in inline mode. (default: `false`)
- `throwOnError`: `boolean`. If `true`, KaTeX will throw a `ParseError` when it encounters an unsupported command. If `false`, KaTeX will render the unsupported command as text in the color given by `errorColor`. (default: `true`)
- `errorColor`: `string`. A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option determines the color which unsupported commands are rendered in. (default: `#cc0000`)
- `macros`: `object`. A collection of custom macros. Each macro is a property with a name like `\name` (written `"\\name"` in JavaScript) which maps to a string that describes the expansion of the macro. Single-character keys can also be included in which case the character will be redefined as the given macro (similar to TeX active characters).
- `colorIsTextColor`: `boolean`. If `true`, `\color` will work like LaTeX's `\textcolor`, and take two arguments (e.g., `\color{blue}{hello}`), which restores the old behavior of KaTeX (pre-0.8.0). If `false` (the default), `\color` will work like LaTeX's `\color`, and take one argument (e.g., `\color{blue}hello`). In both cases, `\textcolor` works as in LaTeX (e.g., `\textcolor{blue}{hello}`).
- `maxSize`: `number`. If non-zero, all user-specified sizes, e.g. in `\rule{500em}{500em}`, will be capped to `maxSize` ems. Otherwise, users can make elements and spaces arbitrarily large (the default behavior).
For example:
```js
katex.render("c = \\pm\\sqrt{a^2 + b^2}\\in\\RR", element, {
displayMode: true,
macros: {
"\\RR": "\\mathbb{R}"
}
});
```
#### Automatic rendering of math on a page
Math on the page can be automatically rendered using the auto-render extension. See [the Auto-render README](contrib/auto-render/README.md) for more information.
#### Font size and lengths
By default, KaTeX math is rendered in a 1.21× larger font than the surrounding
context, which makes super- and subscripts easier to read. You can control
this using CSS, for example:
```css
.katex { font-size: 1.1em; }
```
KaTeX supports all TeX units, including absolute units like `cm` and `in`.
Absolute units are currently scaled relative to the default TeX font size of
10pt, so that `\kern1cm` produces the same results as `\kern2.845275em`.
As a result, relative and absolute units are both uniformly scaled relative
to LaTeX with a 10pt font; for example, the rectangle `\rule{1cm}{1em}` has
the same aspect ratio in KaTeX as in LaTeX. However, because most browsers
default to a larger font size, this typically means that a 1cm kern in KaTeX
will appear larger than 1cm in browser units.
### Common Issues
- Many Markdown preprocessors, such as the one that Jekyll and GitHub Pages use,
have a "smart quotes" feature. This changes `'` to `` which is an issue for
math containing primes, e.g. `f'`. This can be worked around by defining a
single character macro which changes them back, e.g. `{"", "'"}`.
- KaTeX follows LaTeX's rendering of `aligned` and `matrix` environments unlike
MathJax. When displaying fractions one above another in these vertical
layouts there may not be enough space between rows for people who are used to
MathJax's rendering. The distance between rows can be adjusted by using
`\\[0.1em]` instead of the standard line separator distance.
- KaTeX does not support the `align` environment because LaTeX doesn't support
`align` in math mode. The `aligned` environment offers the same functionality
but in math mode, so use that instead or define a macro that maps `align` to
`aligned`.
## Libraries
### Angular2+
- [ng-katex](https://github.com/garciparedes/ng-katex) Angular module to write beautiful math expressions with TeX syntax boosted by KaTeX library
### Ruby
- [katex-ruby](https://github.com/glebm/katex-ruby) Provides server-side rendering and integration with popular Ruby web frameworks (Rails, Hanami, and anything that uses Sprockets).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
## License
KaTeX is licensed under the [MIT License](http://opensource.org/licenses/MIT).
# [<img src="https://khan.github.io/KaTeX/katex-logo.svg" width="130" alt="KaTeX">](https://khan.github.io/KaTeX/)
[![Build Status](https://travis-ci.org/Khan/KaTeX.svg?branch=master)](https://travis-ci.org/Khan/KaTeX)
[![codecov](https://codecov.io/gh/Khan/KaTeX/branch/master/graph/badge.svg)](https://codecov.io/gh/Khan/KaTeX)
[![Join the chat at https://gitter.im/Khan/KaTeX](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Khan/KaTeX?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](http://www.intmath.com/cg5/katex-mathjax-comparison.php).
* **Print quality:** KaTeXs layout is based on Donald Knuths TeX, the gold standard for math typesetting.
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
KaTeX supports all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 9 - IE 11. More information can be found on the [list of supported commands](https://khan.github.io/KaTeX/function-support.html) and on the [wiki](https://github.com/khan/katex/wiki).
## Usage
You can [download KaTeX](https://github.com/khan/katex/releases) and host it on your server or include the `katex.min.js` and `katex.min.css` files on your page directly from a CDN:
```html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.css" integrity="sha384-VEnyslhHLHiYPca9KFkBB3CMeslnM9CzwjxsEbZTeA21JBm7tdLwKoZmCt3cZTYD" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/katex.min.js" integrity="sha384-O4hpKqcplNCe+jLuBVEXC10Rn1QEqAmX98lKAIFBEDxZI0a+6Z2w2n8AEtQbR4CD" crossorigin="anonymous"></script>
```
#### In-browser rendering
Call `katex.render` with a TeX expression and a DOM element to render into:
```js
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element);
```
If KaTeX can't parse the expression, it throws a `katex.ParseError` error.
#### Server side rendering or rendering to a string
To generate HTML on the server or to generate an HTML string of the rendered math, you can use `katex.renderToString`:
```js
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}");
// '<span class="katex">...</span>'
```
Make sure to include the CSS and font files, but there is no need to include the JavaScript. Like `render`, `renderToString` throws if it can't parse the expression.
#### Security
Any HTML generated by KaTeX *should* be safe from `<script>` or other code
injection attacks.
(See `maxSize` below for preventing large width/height visual affronts.)
Of course, it is always a good idea to sanitize the HTML, though you will need
a rather generous whitelist (including some of SVG and MathML) to support
all of KaTeX.
#### Handling errors
If KaTeX encounters an error (invalid or unsupported LaTeX), then it will
throw an exception of type `katex.ParseError`. The message in this error
includes some of the LaTeX source code, so needs to be escaped if you want
to render it to HTML. In particular, you should convert `&`, `<`, `>`
characters to `&amp;`, `&lt;`, `&gt;` (e.g., using `_.escape`)
before including either LaTeX source code or exception messages in your
HTML/DOM. (Failure to escape in this way makes a `<script>` injection
attack possible if your LaTeX source is untrusted.)
#### Rendering options
You can provide an object of options as the last argument to `katex.render` and `katex.renderToString`. Available options are:
- `displayMode`: `boolean`. If `true` the math will be rendered in display mode, which will put the math in display style (so `\int` and `\sum` are large, for example), and will center the math on the page on its own line. If `false` the math will be rendered in inline mode. (default: `false`)
- `throwOnError`: `boolean`. If `true`, KaTeX will throw a `ParseError` when it encounters an unsupported command. If `false`, KaTeX will render the unsupported command as text in the color given by `errorColor`. (default: `true`)
- `errorColor`: `string`. A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option determines the color which unsupported commands are rendered in. (default: `#cc0000`)
- `macros`: `object`. A collection of custom macros. Each macro is a property with a name like `\name` (written `"\\name"` in JavaScript) which maps to a string that describes the expansion of the macro. Single-character keys can also be included in which case the character will be redefined as the given macro (similar to TeX active characters).
- `colorIsTextColor`: `boolean`. If `true`, `\color` will work like LaTeX's `\textcolor`, and take two arguments (e.g., `\color{blue}{hello}`), which restores the old behavior of KaTeX (pre-0.8.0). If `false` (the default), `\color` will work like LaTeX's `\color`, and take one argument (e.g., `\color{blue}hello`). In both cases, `\textcolor` works as in LaTeX (e.g., `\textcolor{blue}{hello}`).
- `maxSize`: `number`. If non-zero, all user-specified sizes, e.g. in `\rule{500em}{500em}`, will be capped to `maxSize` ems. Otherwise, users can make elements and spaces arbitrarily large (the default behavior).
For example:
```js
katex.render("c = \\pm\\sqrt{a^2 + b^2}\\in\\RR", element, {
displayMode: true,
macros: {
"\\RR": "\\mathbb{R}"
}
});
```
#### Automatic rendering of math on a page
Math on the page can be automatically rendered using the auto-render extension. See [the Auto-render README](contrib/auto-render/README.md) for more information.
#### Font size and lengths
By default, KaTeX math is rendered in a 1.21× larger font than the surrounding
context, which makes super- and subscripts easier to read. You can control
this using CSS, for example:
```css
.katex { font-size: 1.1em; }
```
KaTeX supports all TeX units, including absolute units like `cm` and `in`.
Absolute units are currently scaled relative to the default TeX font size of
10pt, so that `\kern1cm` produces the same results as `\kern2.845275em`.
As a result, relative and absolute units are both uniformly scaled relative
to LaTeX with a 10pt font; for example, the rectangle `\rule{1cm}{1em}` has
the same aspect ratio in KaTeX as in LaTeX. However, because most browsers
default to a larger font size, this typically means that a 1cm kern in KaTeX
will appear larger than 1cm in browser units.
### Common Issues
- Many Markdown preprocessors, such as the one that Jekyll and GitHub Pages use,
have a "smart quotes" feature. This changes `'` to `` which is an issue for
math containing primes, e.g. `f'`. This can be worked around by defining a
single character macro which changes them back, e.g. `{"", "'"}`.
- KaTeX follows LaTeX's rendering of `aligned` and `matrix` environments unlike
MathJax. When displaying fractions one above another in these vertical
layouts there may not be enough space between rows for people who are used to
MathJax's rendering. The distance between rows can be adjusted by using
`\\[0.1em]` instead of the standard line separator distance.
- KaTeX does not support the `align` environment because LaTeX doesn't support
`align` in math mode. The `aligned` environment offers the same functionality
but in math mode, so use that instead or define a macro that maps `align` to
`aligned`.
## Libraries
### Angular2+
- [ng-katex](https://github.com/garciparedes/ng-katex) Angular module to write beautiful math expressions with TeX syntax boosted by KaTeX library
### Ruby
- [katex-ruby](https://github.com/glebm/katex-ruby) Provides server-side rendering and integration with popular Ruby web frameworks (Rails, Hanami, and anything that uses Sprockets).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
## License
KaTeX is licensed under the [MIT License](http://opensource.org/licenses/MIT).

87
plugins/search/index.php Normal file
View File

@@ -0,0 +1,87 @@
<?php
namespace Plugins\search;
use \Typemill\Plugin;
use \Typemill\Models\Write;
use \Typemill\Models\WriteCache;
class Index extends Plugin
{
public static function getSubscribedEvents(){}
public function index()
{
$write = new Write();
$index = $write->getFile('cache', 'index.json');
if(!$index)
{
$this->createIndex();
$index = $write->getFile('cache', 'index.json');
}
return $this->returnJson($index);
}
private function createIndex()
{
$write = new WriteCache();
# get content structure
$structure = $write->getCache('cache', 'structure.txt');
# get data for search-index
$index = $this->getAllContent($structure, $write);
# store the index file here
$write->writeFile('cache', 'index.json', json_encode($index, JSON_UNESCAPED_SLASHES));
}
private function getAllContent($structure, $write, $index = NULL)
{
foreach($structure as $item)
{
if($item->elementType == "folder")
{
if($item->fileType == 'md')
{
$page = $write->getFileWithPath('content' . $item->path . DIRECTORY_SEPARATOR . 'index.md');
$pageArray = $this->getPageContentArray($page, $item->urlAbs);
$index[$pageArray['url']] = $pageArray;
}
$index = $this->getAllContent($item->folderContent, $write, $index);
}
else
{
$page = $write->getFileWithPath('content' . $item->path);
$pageArray = $this->getPageContentArray($page, $item->urlAbs);
$index[$pageArray['url']] = $pageArray;
}
}
return $index;
}
private function getPageContentArray($page, $url)
{
$parts = explode("\n", $page, 2);
# get the title / headline
$title = trim($parts[0], '# ');
$title = str_replace(["\r\n", "\n", "\r"],' ', $title);
# get and cleanup the content
$content = $parts[1];
$content = strip_tags($content);
$content = str_replace(["\r\n", "\n", "\r"],' ', $content);
$pageContent = [
'title' => $title,
'content' => $content,
'url' => $url
];
return $pageContent;
}
}

View File

@@ -0,0 +1,19 @@
Copyright (C) 2013 by Oliver Nightingale
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

6
plugins/search/public/lunr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,64 @@
.searchContainer{
overflow: hidden;
width: 100%;
vertical-align: middle;
white-space: nowrap;
}
.searchContainer input{
width: 100%;
height: 50px;
border: 1px solid #ddd;
font-size: 1rem;
float: left;
padding-left: 15px;
border-radius: 2px;
box-sizing:border-box;
}
.searchContainer button{
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border: none;
background: #232833;
height: 50px;
width: 50px;
color: #fff;
font-size: 10pt;
margin-left: -50px;
}
.searchContainer button:hover,.searchContainer button:focus, .searchContainer button:active{
cursor: pointer;
}
#searchresult{
}
.resultwrapper{
}
button#closeSearchResult{
position: absolute;
right: 0px;
top: 0px;
margin: 10px;
border: none;
border-radius: 2px;
font-size: 1rem;
color: #fff;
background: #000;
padding: 15px;
}
button#closeSearchResult:hover,#closeSearchResult:focus{
cursor: pointer;
}
.resultlist{
margin: 0px;
padding: 0px;
list-style:none;
}
.resultitem{
}
.resultheader{
}
.resultsnippet{
}

View File

@@ -0,0 +1,115 @@
var searchField = document.getElementById("searchField");
var searchButton = document.getElementById("searchButton");
if(searchField && searchButton)
{
var searchIndex = false;
var documents = false;
var holdcontent = false;
var contentwrapper = false;
searchField.addEventListener("focus", function(event){
if(!searchIndex)
{
myaxios.get('/indexrs51gfe2o2')
.then(function (response) {
documents = JSON.parse(response.data);
searchIndex = lunr(function() {
this.ref("id");
this.field("title", { boost: 10 });
this.field("content");
for (var key in documents){
this.add({
"id": documents[key].url,
"title": documents[key].title,
"content": documents[key].content
});
}
});
})
.catch(function (error) {});
}
});
searchButton.addEventListener("click", function(event){
event.preventDefault();
var term = document.getElementById('searchField').value;
var results = searchIndex.search(term);
var resultPages = results.map(function (match) {
return documents[match.ref];
});
resultsString = "<div class='resultwrapper'><h1>Result for " + term + "</h1>";
resultsString += "<button id='closeSearchResult'>close</button>";
resultsString += "<ul class='resultlist'>";
resultPages.forEach(function (r) {
resultsString += "<li class='resultitem'>";
resultsString += "<a class='resultheader' href='" + r.url + "?q=" + term + "'><h3>" + r.title + "</h3></a>";
resultsString += "<div class='resultsnippet'>" + r.content.substring(0, 200) + " ...</div>";
resultsString += "</li>"
});
resultsString += "</ul></div>";
if(!holdcontent)
{
contentwrapper = document.getElementById("searchresult").parentNode;
holdcontent = contentwrapper.innerHTML;
}
contentwrapper.innerHTML = resultsString;
document.getElementById("closeSearchResult").addEventListener("click", function(event){
contentwrapper.innerHTML = holdcontent;
});
}, false);
}
/*
var searchIndex = lunr(function() {
this.ref("id");
this.field("title", { boost: 10 });
this.field("content");
for (var key in window.pages) {
this.add({
"id": key,
"title": pages[key].title,
"content": pages[key].content
});
}
});
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] === variable) {
return decodeURIComponent(pair[1].replace(/\+/g, "%20"));
}
}
}
var searchTerm = getQueryVariable("q");
// creation of searchIndex from earlier example
var results = searchIndex.search(searchTerm);
var resultPages = results.map(function (match) {
return pages[match.ref];
});
// resultPages from previous example
resultsString = "";
resultPages.forEach(function (r) {
resultsString += "<li>";
resultsString += "<a class='result' href='" + r.url + "?q=" + searchTerm + "'><h3>" + r.title + "</h3></a>";
resultsString += "<div class='snippet'>" + r.content.substring(0, 200) + "</div>";
resultsString += "</li>"
});
document.querySelector("#search-results").innerHTML = resultsString;
*/

128
plugins/search/search.php Normal file
View File

@@ -0,0 +1,128 @@
<?php
namespace Plugins\search;
use \Typemill\Plugin;
use \Typemill\Models\Write;
class Search extends index
{
protected $item;
public static function getSubscribedEvents()
{
return array(
'onSettingsLoaded' => 'onsettingsLoaded',
'onContentArrayLoaded' => 'onContentArrayLoaded',
'onPageReady' => 'onPageReady',
'onPagePublished' => 'onPagePublished',
'onPageUnpublished' => 'onPageUnpublished',
'onPageSorted' => 'onPageSorted',
'onPageDeleted' => 'onPageDeleted',
);
}
# get search.json with route
# update search.json on publish
public static function addNewRoutes()
{
# the route for the api calls
return array(
array(
'httpMethod' => 'get',
'route' => '/indexrs51gfe2o2',
'class' => 'Plugins\search\index:index'
),
);
}
public function onSettingsLoaded($settings)
{
$this->settings = $settings->getData();
}
# at any of theses events, delete the old search index
public function onPagePublished($item)
{
$this->deleteSearchIndex();
}
public function onPageUnpublished($item)
{
$this->deleteSearchIndex();
}
public function onPageSorted($inputParams)
{
$this->deleteSearchIndex();
}
public function onPageDeleted($item)
{
$this->deleteSearchIndex();
}
private function deleteSearchIndex()
{
$write = new Write();
# store the index file here
$write->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . 'index.json');
}
public function onContentArrayLoaded($contentArray)
{
# get content array
$content = $contentArray->getData();
$settings = $this->getPluginSettings('search');
$salt = "asPx9Derf2";
# activate axios and vue in frontend
$this->activateAxios();
$this->activateVue();
# add the css and vue application
$this->addCSS('/search/public/search.css');
$this->addJS('/search/public/lunr.min.js');
$this->addJS('/search/public/search.js');
# simple security for first request
$secret = time();
$secret = substr($secret,0,-1);
$secret = md5($secret . $salt);
# simple csrf protection with a session for long following requests
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
$length = 32;
$token = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
$_SESSION['search'] = $token;
$_SESSION['search-expire'] = time() + 1300; # 60 seconds * 30 minutes
# create div for vue app
$search = '<div data-access="' . $secret . '" data-token="' . $token . '" id="searchresult"></div>';
# create content type
$search = Array
(
'rawHtml' => $search,
'allowRawHtmlInSafeMode' => true,
'autobreak' => 1
);
$content[] = $search;
$contentArray->setData($content);
}
public function onPageReady($page)
{
$pageData = $page->getData($page);
$pageData['widgets']['search'] = '<div class="searchContainer" id="searchForm">'.
'<input id="searchField" type="text" placeholder="search ..." />'.
'<button id="searchButton" type="button">GO</button>'.
'</div>';
$page->setData($pageData);
}
}

View File

@@ -0,0 +1,6 @@
name: Search
version: 1.0.0
description: Adds a search to typemill with lunr.js.
author: Sebastian Schürmanns
homepage: https://typemill.net
licence: MIT